diff --git a/TUnit.Core.SourceGenerator.Tests/DataSourceGeneratorTests.cs b/TUnit.Core.SourceGenerator.Tests/DataSourceGeneratorTests.cs index b9d4f77534..2a5f6b63b8 100644 --- a/TUnit.Core.SourceGenerator.Tests/DataSourceGeneratorTests.cs +++ b/TUnit.Core.SourceGenerator.Tests/DataSourceGeneratorTests.cs @@ -1,15 +1,34 @@ using TUnit.Core.SourceGenerator.CodeGenerators; +using TUnit.Core.SourceGenerator.Tests.Options; namespace TUnit.Core.SourceGenerator.Tests; internal class DataSourceGeneratorTests : TestsBase { [Test] - public Task Test() => RunTest(Path.Combine(Git.RootDirectory.FullName, + public Task Typed() => RunTest(Path.Combine(Git.RootDirectory.FullName, "TUnit.TestProject", "DataSourceGeneratorTests.cs"), async generatedFiles => { await Assert.That(generatedFiles.Length).IsEqualTo(3); }); + + [Test] + public Task NonTyped() => RunTest(Path.Combine(Git.RootDirectory.FullName, + "TUnit.TestProject", + "AutoDataTests.cs"), + new RunTestOptions() + { + AdditionalFiles = [ + Path.Combine(Git.RootDirectory.FullName, + "TUnit.TestProject", + "Attributes", + "AutoDataAttribute.cs") + ] + }, + async generatedFiles => + { + await Assert.That(generatedFiles.Length).IsEqualTo(1); + }); } \ No newline at end of file diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/ArgumentsRetriever.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/ArgumentsRetriever.cs index 13adca5bf4..30efffc35a 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/ArgumentsRetriever.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/ArgumentsRetriever.cs @@ -63,7 +63,14 @@ public static IEnumerable GetArguments(GeneratorAttributeSyntaxCo if (dataAttribute.AttributeClass?.IsOrInherits(WellKnownFullyQualifiedClassNames .DataSourceGeneratorAttribute.WithGlobalPrefix) == true) { - yield return DataSourceGeneratorRetriever.Parse(context, namedTypeSymbol, dataAttribute, argumentsType, + yield return DataSourceGeneratorRetriever.Parse(context, namedTypeSymbol, parameterOrPropertyTypes, dataAttribute, argumentsType, + index, propertyName); + } + + if (dataAttribute.AttributeClass?.IsOrInherits(WellKnownFullyQualifiedClassNames + .NonTypedDataSourceGeneratorAttribute.WithGlobalPrefix) == true) + { + yield return DataSourceGeneratorRetriever.Parse(context, namedTypeSymbol, parameterOrPropertyTypes, dataAttribute, argumentsType, index, propertyName); } } diff --git a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/DataSourceGeneratorRetriever.cs b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/DataSourceGeneratorRetriever.cs index 1f5b9a69d6..4441d6e9c4 100644 --- a/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/DataSourceGeneratorRetriever.cs +++ b/TUnit.Core.SourceGenerator/CodeGenerators/Helpers/DataSourceGeneratorRetriever.cs @@ -1,4 +1,5 @@ -using Microsoft.CodeAnalysis; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; using TUnit.Core.SourceGenerator.Enums; using TUnit.Core.SourceGenerator.Extensions; using TUnit.Core.SourceGenerator.Models.Arguments; @@ -7,7 +8,9 @@ namespace TUnit.Core.SourceGenerator.CodeGenerators.Helpers; public static class DataSourceGeneratorRetriever { - public static ArgumentsContainer Parse(GeneratorAttributeSyntaxContext context, INamedTypeSymbol namedTypeSymbol, + public static ArgumentsContainer Parse(GeneratorAttributeSyntaxContext context, + INamedTypeSymbol namedTypeSymbol, + ImmutableArray parameterOrPropertyTypes, AttributeData attributeData, ArgumentsType argumentsType, int index, @@ -18,11 +21,12 @@ public static ArgumentsContainer Parse(GeneratorAttributeSyntaxContext context, context, attributeData: attributeData, ArgumentsType: argumentsType, + parameterOrPropertyTypes: parameterOrPropertyTypes, TestClassTypeName: namedTypeSymbol.ToDisplayString(DisplayFormats.FullyQualifiedGenericWithGlobalPrefix), AttributeDataGeneratorType: attributeData.AttributeClass!.ToDisplayString(DisplayFormats .FullyQualifiedGenericWithGlobalPrefix), - GenericArguments: GetDataGeneratorAttributeBaseClass(attributeData.AttributeClass).TypeArguments - .Select(x => x.ToDisplayString(DisplayFormats.FullyQualifiedGenericWithGlobalPrefix)).ToArray(), + GenericArguments: GetDataGeneratorAttributeBaseClass(attributeData.AttributeClass)?.TypeArguments + .Select(x => x.ToDisplayString(DisplayFormats.FullyQualifiedGenericWithGlobalPrefix)).ToArray() ?? [], AttributeIndex: index ) { @@ -36,11 +40,16 @@ .Value.Value as bool? ?? }; } - private static INamedTypeSymbol GetDataGeneratorAttributeBaseClass(ITypeSymbol attributeClass) + private static INamedTypeSymbol? GetDataGeneratorAttributeBaseClass(ITypeSymbol attributeClass) { var selfAndBaseTypes = attributeClass.GetSelfAndBaseTypes(); - return (INamedTypeSymbol) selfAndBaseTypes.First(HasGeneratorInterface); + if (selfAndBaseTypes.FirstOrDefault(HasGeneratorInterface) is INamedTypeSymbol generatorInterface) + { + return generatorInterface; + } + + return null; } private static bool HasGeneratorInterface(ITypeSymbol t) diff --git a/TUnit.Core.SourceGenerator/Models/Arguments/GeneratedArgumentsContainer.cs b/TUnit.Core.SourceGenerator/Models/Arguments/GeneratedArgumentsContainer.cs index 4ced356cb1..c9dce30578 100644 --- a/TUnit.Core.SourceGenerator/Models/Arguments/GeneratedArgumentsContainer.cs +++ b/TUnit.Core.SourceGenerator/Models/Arguments/GeneratedArgumentsContainer.cs @@ -1,18 +1,22 @@ +using System.Collections.Immutable; using Microsoft.CodeAnalysis; using TUnit.Core.SourceGenerator.CodeGenerators.Writers; using TUnit.Core.SourceGenerator.Enums; +using TUnit.Core.SourceGenerator.Extensions; namespace TUnit.Core.SourceGenerator.Models.Arguments; public record GeneratedArgumentsContainer : ArgumentsContainer { public GeneratedArgumentsContainer(GeneratorAttributeSyntaxContext context, AttributeData attributeData, - ArgumentsType ArgumentsType, int AttributeIndex, string TestClassTypeName, string[] GenericArguments, + ArgumentsType ArgumentsType, ImmutableArray parameterOrPropertyTypes, int AttributeIndex, + string TestClassTypeName, string[] GenericArguments, string AttributeDataGeneratorType) : base(ArgumentsType) { this.AttributeIndex = AttributeIndex; Context = context; AttributeData = attributeData; + ParameterOrPropertyTypes = parameterOrPropertyTypes; this.TestClassTypeName = TestClassTypeName; this.GenericArguments = GenericArguments; this.AttributeDataGeneratorType = AttributeDataGeneratorType; @@ -137,8 +141,17 @@ public override void WriteVariableAssignments(SourceCodeWriter sourceCodeWriter, var generatedDataVariableName = $"{VariableNamePrefix}GeneratedData"; sourceCodeWriter.WriteLine($"var {generatedDataVariableName} = {generatedDataVariableName}Accessor();"); - - if (GenericArguments.Length > 1) + + if (GenericArguments.Length == 0) + { + for (var i = 0; i < ParameterOrPropertyTypes.Length; i++) + { + var refIndex = i; + + sourceCodeWriter.WriteLine(GenerateVariable(ParameterOrPropertyTypes[i].GloballyQualified(), $"({ParameterOrPropertyTypes[i].GloballyQualified()}){generatedDataVariableName}[{i}]", ref refIndex).ToString()); + } + } + else if (GenericArguments.Length > 1) { for (var i = 0; i < GenericArguments.Length; i++) { @@ -176,6 +189,7 @@ public override string[] GetArgumentTypes() public GeneratorAttributeSyntaxContext Context { get; } public AttributeData AttributeData { get; } + public ImmutableArray ParameterOrPropertyTypes { get; } public string TestClassTypeName { get; } public string[] GenericArguments { get; } diff --git a/TUnit.Core.SourceGenerator/WellKnownFullyQualifiedClassNames.cs b/TUnit.Core.SourceGenerator/WellKnownFullyQualifiedClassNames.cs index 0e0891111b..b7679d51bd 100644 --- a/TUnit.Core.SourceGenerator/WellKnownFullyQualifiedClassNames.cs +++ b/TUnit.Core.SourceGenerator/WellKnownFullyQualifiedClassNames.cs @@ -16,6 +16,7 @@ public static class WellKnownFullyQualifiedClassNames public static readonly FullyQualifiedTypeName MatrixAttribute = "TUnit.Core.MatrixAttribute"; public static readonly FullyQualifiedTypeName ClassConstructorAttribute = "TUnit.Core.ClassConstructorAttribute"; public static readonly FullyQualifiedTypeName DataSourceGeneratorAttribute = "TUnit.Core.DataSourceGeneratorAttribute"; + public static readonly FullyQualifiedTypeName NonTypedDataSourceGeneratorAttribute = "TUnit.Core.NonTypedDataSourceGeneratorAttribute"; // Metadata diff --git a/TUnit.Core/Attributes/TestData/INonTypedDataSourceGeneratorAttribute.cs b/TUnit.Core/Attributes/TestData/INonTypedDataSourceGeneratorAttribute.cs new file mode 100644 index 0000000000..af3fc08670 --- /dev/null +++ b/TUnit.Core/Attributes/TestData/INonTypedDataSourceGeneratorAttribute.cs @@ -0,0 +1,3 @@ +namespace TUnit.Core; + +internal interface INonTypedDataSource : IDataAttribute; \ No newline at end of file diff --git a/TUnit.Core/Attributes/TestData/NonTypedDataSourceGeneratorAttribute.cs b/TUnit.Core/Attributes/TestData/NonTypedDataSourceGeneratorAttribute.cs new file mode 100644 index 0000000000..954bb86f0a --- /dev/null +++ b/TUnit.Core/Attributes/TestData/NonTypedDataSourceGeneratorAttribute.cs @@ -0,0 +1,7 @@ +namespace TUnit.Core; + +[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Property, AllowMultiple = true)] +public abstract class NonTypedDataSourceGeneratorAttribute : TestDataAttribute, INonTypedDataSource +{ + public abstract IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata); +} \ No newline at end of file diff --git a/TUnit.TestProject/Attributes/AutoDataAttribute.cs b/TUnit.TestProject/Attributes/AutoDataAttribute.cs new file mode 100644 index 0000000000..5d672c45ba --- /dev/null +++ b/TUnit.TestProject/Attributes/AutoDataAttribute.cs @@ -0,0 +1,26 @@ +using AutoFixture.Kernel; + +namespace TUnit.TestProject.Attributes; + +public class AutoDataAttribute : NonTypedDataSourceGeneratorAttribute +{ + private static AutoFixture.Fixture _fixture = new(); + + public override IEnumerable> GenerateDataSources(DataGeneratorMetadata dataGeneratorMetadata) + { + yield return () => GenerateRow(dataGeneratorMetadata); + } + + private object?[] GenerateRow(DataGeneratorMetadata dataGeneratorMetadata) + { + return GenerateRowEnumerable(dataGeneratorMetadata).ToArray(); + } + + private static IEnumerable GenerateRowEnumerable(DataGeneratorMetadata dataGeneratorMetadata) + { + foreach (var parameterInfo in dataGeneratorMetadata.ParameterInfos ?? []) + { + yield return _fixture.Create(parameterInfo.ParameterType, new SpecimenContext(_fixture)); + } + } +} \ No newline at end of file diff --git a/TUnit.TestProject/AutoDataTests.cs b/TUnit.TestProject/AutoDataTests.cs new file mode 100644 index 0000000000..193657c554 --- /dev/null +++ b/TUnit.TestProject/AutoDataTests.cs @@ -0,0 +1,13 @@ +using TUnit.TestProject.Attributes; + +namespace TUnit.TestProject; + +public class AutoDataTests +{ + [AutoData] + [Test] + public Task Test1(string value1, int value2, double value3, bool value4) + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/TUnit.TestProject/TUnit.TestProject.csproj b/TUnit.TestProject/TUnit.TestProject.csproj index 7370bf5049..e265f03300 100644 --- a/TUnit.TestProject/TUnit.TestProject.csproj +++ b/TUnit.TestProject/TUnit.TestProject.csproj @@ -33,6 +33,7 @@ +