Skip to content

Commit

Permalink
## 8.1.3 - 2024-10-14
Browse files Browse the repository at this point in the history
### Changed

- Fixed regression for default constructors - the build method should appear again
- Added generation of methods for properties from private constructors, keeping the Build method removed.
  • Loading branch information
pmrogala committed Oct 14, 2024
1 parent 43d7c31 commit 5e43c6c
Show file tree
Hide file tree
Showing 9 changed files with 68 additions and 40 deletions.
2 changes: 1 addition & 1 deletion Buildenator/Configuration/Contract/IEntityToBuild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ internal interface IEntityToBuild : IAdditionalNamespacesProvider
string Name { get; }
IReadOnlyList<TypedSymbol> SettableProperties { get; }
IReadOnlyList<TypedSymbol> ReadOnlyProperties { get; }
EntityToBuild.Constructor? ConstructorToBuild { get; }
EntityToBuild.Constructor ConstructorToBuild { get; }

IReadOnlyList<ITypedSymbol> GetAllUniqueReadOnlyPropertiesWithoutConstructorsParametersMatch();
IReadOnlyList<ITypedSymbol> GetAllUniqueSettablePropertiesAndParameters();
Expand Down
46 changes: 24 additions & 22 deletions Buildenator/Configuration/EntityToBuild.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using System.Collections.Generic;
using System.Linq;
using Buildenator.Abstraction;
using System.Diagnostics;
using System.Reflection.Metadata;

namespace Buildenator.Configuration;

Expand All @@ -13,7 +15,7 @@ internal sealed class EntityToBuild : IEntityToBuild
public string Name { get; }
public string FullName { get; }
public string FullNameWithConstraints { get; }
public Constructor? ConstructorToBuild { get; }
public Constructor ConstructorToBuild { get; }
public IReadOnlyList<TypedSymbol> SettableProperties { get; }
public IReadOnlyList<TypedSymbol> ReadOnlyProperties { get; }
public string[] AdditionalNamespaces { get; }
Expand Down Expand Up @@ -50,23 +52,13 @@ public EntityToBuild(

public IReadOnlyList<ITypedSymbol> GetAllUniqueSettablePropertiesAndParameters()
{
if (ConstructorToBuild is null)
{
return _uniqueTypedSymbols ??= SettableProperties;
}

return _uniqueTypedSymbols ??= SettableProperties
.Where(x => !ConstructorToBuild.ContainsParameter(x.SymbolName))
.Concat(ConstructorToBuild.Parameters).ToList();
}

public IReadOnlyList<ITypedSymbol> GetAllUniqueReadOnlyPropertiesWithoutConstructorsParametersMatch()
{
if (ConstructorToBuild is null)
{
return _uniqueReadOnlyTypedSymbols ??= ReadOnlyProperties;
}

return _uniqueReadOnlyTypedSymbols ??= ReadOnlyProperties
.Where(x => !ConstructorToBuild.ContainsParameter(x.SymbolName)).ToList();
}
Expand All @@ -86,28 +78,38 @@ private static (TypedSymbol[] Settable, TypedSymbol[] ReadOnly) DivideProperties

internal sealed class Constructor
{
public static Constructor? CreateConstructorOrDefault(
public bool IsPrivate { get; }

public static Constructor CreateConstructorOrDefault(
INamedTypeSymbol entityToBuildSymbol,
IMockingProperties? mockingConfiguration,
IFixtureProperties? fixtureConfiguration,
NullableStrategy nullableStrategy)
{
var onlyPublicOrInternalConstructors = entityToBuildSymbol.Constructors
.Where(m =>
!m.IsStatic
&& !m.IsImplicitlyDeclared
&& (m.DeclaredAccessibility == Accessibility.Public || m.DeclaredAccessibility == Accessibility.Internal))
var constructors = entityToBuildSymbol.Constructors.Select(a => a).ToArray();
var onlyPublicConstructors = constructors
.Where(m => m.DeclaredAccessibility == Accessibility.Public || m.DeclaredAccessibility == Accessibility.Internal)
.ToList();

return onlyPublicOrInternalConstructors.Count > 0
? new Constructor(onlyPublicOrInternalConstructors.OrderByDescending(x => x.Parameters.Length).First().Parameters
.ToDictionary(x => x.PascalCaseName(), s => new TypedSymbol(s, mockingConfiguration, fixtureConfiguration, nullableStrategy)))
: default;
var isPrivate = onlyPublicConstructors.Count == 0;

Dictionary<string, TypedSymbol> parameters = [];
if (!isPrivate)
{
parameters = onlyPublicConstructors
.OrderByDescending(x => x.Parameters.Length)
.First()
.Parameters
.ToDictionary(x => x.PascalCaseName(), s => new TypedSymbol(s, mockingConfiguration, fixtureConfiguration, nullableStrategy));
}

return new Constructor(parameters, isPrivate);
}

private Constructor(IReadOnlyDictionary<string, TypedSymbol> constructorParameters)
private Constructor(IReadOnlyDictionary<string, TypedSymbol> constructorParameters, bool isPrivate)
{
ConstructorParameters = constructorParameters;
IsPrivate = isPrivate;
}

public IReadOnlyDictionary<string, TypedSymbol> ConstructorParameters { get; }
Expand Down
20 changes: 8 additions & 12 deletions Buildenator/Generators/BuilderSourceStringGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,10 @@ private string GenerateBuilderDefinition()

private string GenerateBuildsCode()
{
if (_entity.ConstructorToBuild is null)
if (_entity.ConstructorToBuild.IsPrivate)
return "";

var (parameters, properties) = GetParametersAndProperties(_entity.ConstructorToBuild);
var (parameters, properties) = GetParametersAndProperties();

var disableWarning = _builder.NullableStrategy == NullableStrategy.Enabled
? "#pragma warning disable CS8604\n"
Expand Down Expand Up @@ -106,10 +106,10 @@ private string GenerateBuildManyCode()

private string GenerateStaticBuildsCode()
{
if (_entity.ConstructorToBuild is null)
if (_entity.ConstructorToBuild.IsPrivate)
return "";

var (parameters, properties) = GetParametersAndProperties(_entity.ConstructorToBuild);
var (parameters, properties) = GetParametersAndProperties();
var moqInit = parameters
.Concat(properties)
.Where(symbol => symbol.IsMockable())
Expand Down Expand Up @@ -145,14 +145,10 @@ private string GenerateImplicitCastCode()
return $@" public static implicit operator {_entity.FullName}({_builder.FullName} builder) => builder.Build();";
}

private (IReadOnlyList<ITypedSymbol> Parameters, IReadOnlyList<ITypedSymbol> Properties) GetParametersAndProperties(EntityToBuild.Constructor constructorToBuild)
private (IReadOnlyList<ITypedSymbol> Parameters, IReadOnlyList<ITypedSymbol> Properties) GetParametersAndProperties()
{
var parameters = constructorToBuild.Parameters;
var properties = _entity.SettableProperties.AsEnumerable();
if (_entity.ConstructorToBuild is not null)
{
properties = properties.Where(x => !constructorToBuild.ContainsParameter(x.SymbolName));
}
var parameters = _entity.ConstructorToBuild.Parameters;
var properties = _entity.SettableProperties.Where(x => !_entity.ConstructorToBuild.ContainsParameter(x.SymbolName));
return (parameters.ToList(), properties.ToList());
}

Expand Down Expand Up @@ -194,7 +190,7 @@ private string GenerateBuildEntityString(IEnumerable<ITypedSymbol> parameters, I
// ------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a source generator named Buildenator (https://github.com/pmrogala/Buildenator)
// Version 8.1.2
// Version 8.1.3
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
Expand Down
2 changes: 1 addition & 1 deletion Buildenator/Generators/PropertiesStringGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public string GeneratePropertiesCode()
{
var properties = _entity.GetAllUniqueSettablePropertiesAndParameters();

if (_builder.ShouldGenerateMethodsForUnreachableProperties)
if (_builder.ShouldGenerateMethodsForUnreachableProperties || _entity.ConstructorToBuild.IsPrivate)
{
properties = properties.Concat(_entity.GetAllUniqueReadOnlyPropertiesWithoutConstructorsParametersMatch()).ToList();
}
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
### Removed

## 6.1.3 & 8.1.3 - 2024-10-11

### Changed

- Fixed regression for default constructors - the build method should appear again
- Added generation of methods for properties from private constructors, keeping the Build method removed.

## 6.1.2 & 8.1.2 - 2024-10-11

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ public class EntityWithImplicitConstructor
{

public int PropertyIntGetter { get; } = 1;
public string PropertyGetter { get; } = "1";
public string PropertyGetter { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Buildenator.Abstraction;
using Buildenator.IntegrationTests.SharedEntities;
using System;

namespace Buildenator.IntegrationTests.Source.Builders;

Expand All @@ -8,6 +9,6 @@ public partial class EntityWithPrivateConstructorBuilder
{
public EntityWithPrivateConstructor Build()
{
return default!;
throw new InvalidOperationException("It is a test!");
}
}
25 changes: 23 additions & 2 deletions Tests/Buildenator.IntegrationTests/BuildersGeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
using Buildenator.IntegrationTests.Source.Builders;
using Buildenator.IntegrationTests.SharedEntities.DifferentNamespace;
using FluentAssertions;
using System.Collections.Generic;
using System.Linq;
using Xunit;
using PostBuildEntityBuilder = Buildenator.IntegrationTests.Source.Builders.PostBuildEntityBuilder;

Expand Down Expand Up @@ -274,4 +272,27 @@ public void BuildersGenerator_ReadOnlyProperty_ShouldCreateMethodForSettingItsVa
_ = builder.WithPrivateField(privateField);
_ = builder.Build().PrivateField.Should().BeEquivalentTo(privateField);
}

[Fact]
public void BuildersGenerator_DefaultPublicConstructor_ShouldCreateBuildMethod()
{
_ = new EntityWithDefaultConstructorBuilder().Build().Should().NotBeNull();
}


[Fact]
public void BuildersGenerator_PrivateConstructor_ShouldNotGenerateBuildMethod()
{
var lamda = () => new EntityWithPrivateConstructorBuilder().Build();
lamda.Should().ThrowExactly<InvalidOperationException>().Which.Message.Should().Be("It is a test!");
}

[Fact]
public void BuildersGenerator_PrivateConstructor_ShouldCreateWithProperties()
{
var builder = new EntityWithPrivateConstructorBuilder();
_ = builder.WithPropertyIntGetter(1);
_ = builder.WithPropertyGetter("1");
_ = builder.WithEntityInDifferentNamespace(null!);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using AutoFixture;
using AutoFixture.AutoMoq;
using AutoFixture.Xunit2;
using Buildenator.CodeAnalysis;
using Buildenator.Configuration;
using Buildenator.Configuration.Contract;
Expand Down Expand Up @@ -99,6 +100,7 @@ public void GeneratePropertiesCode_ShouldNotGenerateCodeForAlreadyDeclaredFields
_ = result.Should().NotContain(existingMethod);
}


[Fact]
public void GeneratePropertiesCode_ShouldGenerateMethodDefinitionForMockableTypedSymbols()
{
Expand Down

0 comments on commit 5e43c6c

Please sign in to comment.