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 @@ -23,7 +23,7 @@
private const string GeneratedNamespace = "TUnit.Generated";

private static readonly DiagnosticDescriptor GenerationErrorDescriptor = new(
"TUNIT0999",

Check warning on line 26 in src/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs

View workflow job for this annotation

GitHub Actions / modularpipeline (fr-FR)

Activer le suivi de version d'analyseur pour le projet d'analyseur contenant la règle 'TUNIT0999' (https://github.com/dotnet/roslyn-analyzers/blob/main/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md)

Check warning on line 26 in src/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs

View workflow job for this annotation

GitHub Actions / modularpipeline (windows-latest)

Check warning on line 26 in src/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs

View workflow job for this annotation

GitHub Actions / modularpipeline (macos-latest)

Check warning on line 26 in src/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs

View workflow job for this annotation

GitHub Actions / modularpipeline (ubuntu-latest)

Check warning on line 26 in src/TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs

View workflow job for this annotation

GitHub Actions / modularpipeline (ubuntu-latest)

"Source Generation Error",
"Failed to generate test metadata for {0}.{1}: {2}",
"TUnit",
Expand Down Expand Up @@ -7195,8 +7195,10 @@

private static bool AreSameAttribute(AttributeData a1, AttributeData a2)
{
// Compare attributes by their constructor arguments and attribute class
if (a1.AttributeClass?.Name != a2.AttributeClass?.Name)
// Compare attributes by their constructor arguments and attribute class. The class is compared
// as a constructed type, not by name: ClassDataSource<A> and ClassDataSource<B> share a name and
// take no constructor arguments, yet each scopes a different instantiation (#7158).
if (!SymbolEqualityComparer.Default.Equals(a1.AttributeClass, a2.AttributeClass))
{
return false;
}
Expand Down
33 changes: 33 additions & 0 deletions tests/TUnit.Core.SourceGenerator.Tests/Bugs/7158/Tests7158.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace TUnit.Core.SourceGenerator.Tests;

/// <summary>
/// A generic method with several [GenerateGenericTest] types and one typed ClassDataSource&lt;T&gt;
/// per type pairs each instantiation only with the data source of its type (#7158).
/// </summary>
internal class Tests7158 : TestsBase
{
[Test]
public Task Test() => RunTest(Path.Combine(
Git.TestsDirectory.FullName,
"TUnit.TestProject",
"Bugs",
"_7158",
"GenerateGenericTestTypedClassDataSourceTests.cs"),
async generatedFiles =>
{
var source = generatedFiles.Single(f => f.Contains("GenerateGenericTestTypedClassDataSourceTests"));
const string dataSourceA = "new global::TUnit.Core.ClassDataSourceAttribute<global::TUnit.TestProject.Bugs._7158.Gen7158A>()";
const string dataSourceB = "new global::TUnit.Core.ClassDataSourceAttribute<global::TUnit.TestProject.Bugs._7158.Gen7158B>()";

// Each entry's test data sources hold only the data source of its own type argument.
var entryA = source.Substring(source.IndexOf("methodName: \"Run<Gen7158A>\"", StringComparison.Ordinal));
entryA = entryA.Substring(0, entryA.IndexOf("methodMetadata:", StringComparison.Ordinal));
var entryB = source.Substring(source.IndexOf("methodName: \"Run<Gen7158B>\"", StringComparison.Ordinal));
entryB = entryB.Substring(0, entryB.IndexOf("methodMetadata:", StringComparison.Ordinal));

await Assert.That(entryA).Contains(dataSourceA);
await Assert.That(entryA).DoesNotContain(dataSourceB);
await Assert.That(entryB).Contains(dataSourceB);
await Assert.That(entryB).DoesNotContain(dataSourceA);
});
}
139 changes: 139 additions & 0 deletions tests/TUnit.Core.SourceGenerator.Tests/Tests7158.Test.verified.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// <auto-generated/>
#pragma warning disable

#nullable enable
namespace TUnit.Generated;
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("TUnit", "VERSION_SCRUBBED")]
internal static class TUnit_TestProject_Bugs__7158_GenerateGenericTestTypedClassDataSourceTests_Run__T_TestSource
{
private static readonly global::TUnit.Core.ClassMetadata __classMetadata = global::TUnit.Core.ClassMetadata.GetOrAdd("TestsBase`1:global::TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests", new global::TUnit.Core.ClassMetadata
{
Type = typeof(global::TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests),
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests)),
Name = "GenerateGenericTestTypedClassDataSourceTests",
Namespace = "TUnit.TestProject.Bugs._7158",
Assembly = global::TUnit.Core.AssemblyMetadata.GetOrAdd("TestsBase`1", "TestsBase`1"),
Parameters = global::System.Array.Empty<global::TUnit.Core.ParameterMetadata>(),
Properties = global::System.Array.Empty<global::TUnit.Core.PropertyMetadata>(),
Parent = null
});
private static readonly global::System.Type __classType = typeof(global::TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests);
private static readonly global::TUnit.Core.MethodMetadata __mm_0 = global::TUnit.Core.MethodMetadataFactory.Create("Run", __classType, typeof(global::System.Threading.Tasks.Task), __classMetadata, genericTypeCount: 1, parameters: global::TUnit.Core.ParameterMetadataFactory.ForGenericMethod(typeof(global::TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests), "Run", false, 1, new global::TUnit.Core.ParameterMetadata[]
{
global::TUnit.Core.ParameterMetadataFactory.Create(typeof(object), "value", new global::TUnit.Core.GenericParameter(0, true, "T"), false)
}));
private static global::TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests __CreateInstance_0(global::System.Type[] typeArgs, object?[] args)
{
return new global::TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests();
}
private static global::System.Threading.Tasks.ValueTask __Invoke_0(global::TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests instance, int methodIndex, object?[] args, global::System.Threading.CancellationToken cancellationToken)
{
switch (methodIndex)
{
case 0:
{
try
{
return new global::System.Threading.Tasks.ValueTask(instance.Run<global::TUnit.TestProject.Bugs._7158.Gen7158A>(global::TUnit.Core.Helpers.CastHelper.Cast<global::TUnit.TestProject.Bugs._7158.Gen7158A>(args[0])));
}
catch (global::System.Exception ex)
{
return new global::System.Threading.Tasks.ValueTask(global::System.Threading.Tasks.Task.FromException(ex));
}
}
case 1:
{
try
{
return new global::System.Threading.Tasks.ValueTask(instance.Run<global::TUnit.TestProject.Bugs._7158.Gen7158B>(global::TUnit.Core.Helpers.CastHelper.Cast<global::TUnit.TestProject.Bugs._7158.Gen7158B>(args[0])));
}
catch (global::System.Exception ex)
{
return new global::System.Threading.Tasks.ValueTask(global::System.Threading.Tasks.Task.FromException(ex));
}
}
default:
throw new global::System.ArgumentOutOfRangeException(nameof(methodIndex));
}
}
private static global::System.Attribute[] __Attributes_0(int groupIndex)
{
switch (groupIndex)
{
case 0:
{
return new global::System.Attribute[]
{
new global::TUnit.Core.TestAttribute(),
new global::TUnit.Core.GenerateGenericTestAttribute(typeof(global::TUnit.TestProject.Bugs._7158.Gen7158A)),
new global::TUnit.Core.GenerateGenericTestAttribute(typeof(global::TUnit.TestProject.Bugs._7158.Gen7158B)),
new global::TUnit.Core.ClassDataSourceAttribute<global::TUnit.TestProject.Bugs._7158.Gen7158A>()
{Shared = global::TUnit.Core.SharedType.None,},
new global::TUnit.Core.ClassDataSourceAttribute<global::TUnit.TestProject.Bugs._7158.Gen7158B>()
{Shared = global::TUnit.Core.SharedType.None,},
new global::TUnit.TestProject.Attributes.EngineTest(global::TUnit.TestProject.Attributes.ExpectedResult.Pass)
};
}
case 1:
{
return new global::System.Attribute[]
{
new global::TUnit.Core.TestAttribute(),
new global::TUnit.Core.GenerateGenericTestAttribute(typeof(global::TUnit.TestProject.Bugs._7158.Gen7158A)),
new global::TUnit.Core.GenerateGenericTestAttribute(typeof(global::TUnit.TestProject.Bugs._7158.Gen7158B)),
new global::TUnit.Core.ClassDataSourceAttribute<global::TUnit.TestProject.Bugs._7158.Gen7158A>()
{Shared = global::TUnit.Core.SharedType.None,},
new global::TUnit.Core.ClassDataSourceAttribute<global::TUnit.TestProject.Bugs._7158.Gen7158B>()
{Shared = global::TUnit.Core.SharedType.None,},
new global::TUnit.TestProject.Attributes.EngineTest(global::TUnit.TestProject.Attributes.ExpectedResult.Pass)
};
}
default:
throw new global::System.ArgumentOutOfRangeException(nameof(groupIndex));
}
}
public static readonly global::TUnit.Core.TestEntry<global::TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests>[] Entries_0 = new global::TUnit.Core.TestEntry<global::TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests>[]
{
global::TUnit.Core.TestEntryFactory.Create<global::TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests>(
methodName: "Run<Gen7158A>",
fullyQualifiedName: "TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests.Run<Gen7158A>",
methodTypeArgumentsIdentity: "<global::TUnit.TestProject.Bugs._7158.Gen7158A>",
filePath: "",
lineNumber: 26,
hasDataSource: true,
testDataSources: new global::TUnit.Core.IDataSourceAttribute[]
{
new global::TUnit.Core.ClassDataSourceAttribute<global::TUnit.TestProject.Bugs._7158.Gen7158A>()
{Shared = global::TUnit.Core.SharedType.None,},
},
methodMetadata: __mm_0,
createInstance: __CreateInstance_0,
invokeBody: __Invoke_0,
methodIndex: 0,
createAttributes: __Attributes_0,
attributeGroupIndex: 0),
global::TUnit.Core.TestEntryFactory.Create<global::TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests>(
methodName: "Run<Gen7158B>",
fullyQualifiedName: "TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests.Run<Gen7158B>",
methodTypeArgumentsIdentity: "<global::TUnit.TestProject.Bugs._7158.Gen7158B>",
filePath: "",
lineNumber: 26,
hasDataSource: true,
testDataSources: new global::TUnit.Core.IDataSourceAttribute[]
{
new global::TUnit.Core.ClassDataSourceAttribute<global::TUnit.TestProject.Bugs._7158.Gen7158B>()
{Shared = global::TUnit.Core.SharedType.None,},
},
methodMetadata: __mm_0,
createInstance: __CreateInstance_0,
invokeBody: __Invoke_0,
methodIndex: 1,
createAttributes: __Attributes_0,
attributeGroupIndex: 1),
};
}
internal static partial class TUnit_TestRegistration
{
static readonly int _r_TUnit_TestProject_Bugs__7158_GenerateGenericTestTypedClassDataSourceTests_Run__T_TestSource_0 = global::TUnit.Core.SourceRegistrar.RegisterEntries<global::TUnit.TestProject.Bugs._7158.GenerateGenericTestTypedClassDataSourceTests>(static () => TUnit_TestProject_Bugs__7158_GenerateGenericTestTypedClassDataSourceTests_Run__T_TestSource.Entries_0);
}
25 changes: 25 additions & 0 deletions tests/TUnit.Engine.Tests/Issue7158Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Shouldly;
using TUnit.Engine.Tests.Enums;

namespace TUnit.Engine.Tests;

/// <summary>
/// A generic method with several [GenerateGenericTest] types and one typed ClassDataSource&lt;T&gt; per
/// type runs each instantiation only with the data source of its type, in both modes. Source-gen mode
/// used to pair every instantiation with every data source, failing the mismatched pairs with an
/// InvalidCastException (#7158).
/// </summary>
public class Issue7158Tests(TestMode testMode) : InvokableTestBase(testMode)
{
[Test]
public async Task Each_Instantiation_Runs_Only_Its_Typed_Class_Data_Source()
{
await RunTestsWithFilter(
"/*/TUnit.TestProject.Bugs._7158/GenerateGenericTestTypedClassDataSourceTests/*",
[
result => result.ResultSummary.Counters.Total.ShouldBe(2),
result => result.ResultSummary.Counters.Passed.ShouldBe(2),
result => result.ResultSummary.Counters.Failed.ShouldBe(0)
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using TUnit.TestProject.Attributes;

namespace TUnit.TestProject.Bugs._7158;

public class Gen7158A : IDisposable
{
public void Dispose()
{
}
}

public class Gen7158B : IDisposable
{
public void Dispose()
{
}
}

/// <summary>
/// Each [GenerateGenericTest] instantiation runs only the typed ClassDataSource&lt;T&gt; whose type
/// binds to its type argument, in both modes (#7158).
/// </summary>
[EngineTest(ExpectedResult.Pass)]
public class GenerateGenericTestTypedClassDataSourceTests
{
[Test]
[GenerateGenericTest(typeof(Gen7158A))]
[GenerateGenericTest(typeof(Gen7158B))]
[ClassDataSource<Gen7158A>(Shared = SharedType.None)]
[ClassDataSource<Gen7158B>(Shared = SharedType.None)]
public async Task Run<T>(T value)
{
await Assert.That(value).IsTypeOf<T>();
}
}
Loading