Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,37 @@ namespace TUnit.Core.SourceGenerator.CodeGenerators.Helpers;

public static class InstanceFactoryGenerator
{
/// <summary>
/// Generates code to create an instance of a type with proper required property handling.
/// This handles required properties that don't have data sources by initializing them with defaults.
/// </summary>
public static void GenerateInstanceCreation(CodeWriter writer, ITypeSymbol typeSymbol, string variableName)
{
var className = typeSymbol.GloballyQualified();
// Use GetAllRequiredProperties because even properties with data sources need to be
// initialized in the object initializer to satisfy C#'s required modifier constraint.
// The actual values will be populated by the data sources at runtime.
var requiredProperties = RequiredPropertyHelper.GetAllRequiredProperties(typeSymbol).ToArray();

if (requiredProperties.Length == 0)
{
writer.AppendLine($"{variableName} = new {className}();");
}
else
{
writer.AppendLine($"{variableName} = new {className}()");
writer.AppendLine("{");
writer.Indent();
foreach (var property in requiredProperties)
{
var defaultValue = RequiredPropertyHelper.GetDefaultValueForType(property.Type);
writer.AppendLine($"{property.Name} = {defaultValue},");
}
writer.Unindent();
writer.AppendLine("};");
}
}

public static void GenerateInstanceFactory(CodeWriter writer, ITypeSymbol typeSymbol)
{
GenerateInstanceFactory(writer, typeSymbol, null);
Expand Down
16 changes: 8 additions & 8 deletions TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1048,7 +1048,7 @@ private static void GenerateMethodDataSourceFactory(CodeWriter writer, IMethodSy
writer.AppendLine("else");
writer.AppendLine("{");
writer.Indent();
writer.AppendLine($"instance = new {fullyQualifiedType}();");
InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance");
writer.Unindent();
writer.AppendLine("}");
writer.AppendLine($"var result = (({fullyQualifiedType})instance).{methodCall};");
Expand Down Expand Up @@ -1080,7 +1080,7 @@ private static void GenerateMethodDataSourceFactory(CodeWriter writer, IMethodSy
writer.AppendLine("else");
writer.AppendLine("{");
writer.Indent();
writer.AppendLine($"instance = new {fullyQualifiedType}();");
InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance");
writer.Unindent();
writer.AppendLine("}");
writer.AppendLine($"var result = (({fullyQualifiedType})instance).{methodCall};");
Expand Down Expand Up @@ -1124,7 +1124,7 @@ private static void GenerateMethodDataSourceFactory(CodeWriter writer, IMethodSy
writer.AppendLine("else");
writer.AppendLine("{");
writer.Indent();
writer.AppendLine($"instance = new {fullyQualifiedType}();");
InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance");
writer.Unindent();
writer.AppendLine("}");
writer.AppendLine($"var result = (({fullyQualifiedType})instance).{methodCall};");
Expand Down Expand Up @@ -1167,7 +1167,7 @@ private static void GenerateMethodDataSourceFactory(CodeWriter writer, IMethodSy
writer.AppendLine("else");
writer.AppendLine("{");
writer.Indent();
writer.AppendLine($"instance = new {fullyQualifiedType}();");
InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance");
writer.Unindent();
writer.AppendLine("}");
writer.AppendLine($"var result = (({fullyQualifiedType})instance).{methodCall};");
Expand Down Expand Up @@ -1217,7 +1217,7 @@ private static void GeneratePropertyDataSourceFactory(CodeWriter writer, IProper
writer.AppendLine("else");
writer.AppendLine("{");
writer.Indent();
writer.AppendLine($"instance = new {fullyQualifiedType}();");
InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance");
writer.Unindent();
writer.AppendLine("}");
writer.AppendLine($"var result = (({fullyQualifiedType})instance).{propertyAccess};");
Expand Down Expand Up @@ -1249,7 +1249,7 @@ private static void GeneratePropertyDataSourceFactory(CodeWriter writer, IProper
writer.AppendLine("else");
writer.AppendLine("{");
writer.Indent();
writer.AppendLine($"instance = new {fullyQualifiedType}();");
InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance");
writer.Unindent();
writer.AppendLine("}");
writer.AppendLine($"var result = (({fullyQualifiedType})instance).{propertyAccess};");
Expand Down Expand Up @@ -1293,7 +1293,7 @@ private static void GeneratePropertyDataSourceFactory(CodeWriter writer, IProper
writer.AppendLine("else");
writer.AppendLine("{");
writer.Indent();
writer.AppendLine($"instance = new {fullyQualifiedType}();");
InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance");
writer.Unindent();
writer.AppendLine("}");
writer.AppendLine($"var result = (({fullyQualifiedType})instance).{propertyAccess};");
Expand Down Expand Up @@ -1336,7 +1336,7 @@ private static void GeneratePropertyDataSourceFactory(CodeWriter writer, IProper
writer.AppendLine("else");
writer.AppendLine("{");
writer.Indent();
writer.AppendLine($"instance = new {fullyQualifiedType}();");
InstanceFactoryGenerator.GenerateInstanceCreation(writer, targetType, "instance");
writer.Unindent();
writer.AppendLine("}");
writer.AppendLine($"var result = (({fullyQualifiedType})instance).{propertyAccess};");
Expand Down
48 changes: 48 additions & 0 deletions TUnit.TestProject/Bugs/3951/Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using TUnit.Core.Interfaces;
using TUnit.TestProject.Attributes;

namespace TUnit.TestProject.Bugs._3951;


public sealed class MyTestGeneratorAttribute<T> : DataSourceGeneratorAttribute<T> where T : MyType, new()
{
protected override IEnumerable<Func<T>> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata) => [() => new T()];
}

public class MyType;

public class ErrContext: IAsyncInitializer, IAsyncDisposable
{
public ValueTask DisposeAsync() => default;
public Task InitializeAsync() => Task.CompletedTask;
}

public class ErrFixture<T> : IAsyncDisposable, IAsyncInitializer
{
public required ErrContext Fixture { get; set; }
public ValueTask DisposeAsync() => default;
public Task InitializeAsync() => Task.CompletedTask;
}

public class ErrTest
{
[ClassDataSource<ErrFixture<MyType>>(Shared = SharedType.PerClass)]
public required ErrFixture<MyType> Fixture { get; init; }

public IEnumerable<Func<ErrContext>> TestExecutions => [() => Fixture.Fixture];

[MethodDataSource("TestExecutions")]

[Test]
public async Task MyTest(ErrContext context)
{
await Assert.That(context.GetType()).IsNotAssignableTo<MyType>();
}

[MyTestGeneratorAttribute<MyType>]
[Test]
public async Task MyTest2(MyType t)
{
await Assert.That(t.GetType()).IsAssignableTo<MyType>();
}
}
Loading