Repository navigation
Description
What happens
In reflection mode, a class-level [MethodDataSource] on a generic test class aborts discovery of the whole assembly. Every test in the assembly is replaced by a single failing [ASSEMBLY SCAN FAILED] <assembly> entry. A run with a --treenode-filter that does not match that entry reports "Zero tests ran".
[MethodDataSource(nameof(Strings))]
public class OnlyMds<T>(T value)
{
public static IEnumerable<string> Strings() => ["s"];
[Test]
[Arguments("a")]
public void Test(T arg) { }
}Reproduced on main (c4b9977) by adding this class to tests/TUnit.TestProject. With --reflection --list-tests the project lists 49 tests instead of thousands. Running --treenode-filter "/*/*/*/AssemblyScanFailed" --reflection shows:
InvalidOperationException: Failed to get data from source: Method, property, or field 'Strings' not found in class 'Object' with the specified arguments.
at TUnit.Core.MethodDataSourceAttribute.GetDataRowsAsync(...) MethodDataSourceAttribute.cs:261
at ReflectionTestDataCollector.GetDataFromSourceAsync(...) ReflectionTestDataCollector.cs:1419
at ReflectionTestDataCollector.GroupClassRowsByTypeArgumentsAsync(...) :1077
at ReflectionTestDataCollector.DiscoverGenericTests(Type) :753
at ReflectionTestDataCollector.DiscoverTestsInAssembly(Assembly) :456
GroupClassRowsByTypeArgumentsAsync evaluates the class data sources with metadata whose class is object, so the member lookup fails. The exception is not caught per class, so it escapes to ProcessAssemblyAsync.
Source-gen mode runs the test, but it builds the instance from the method row's value (ctor=a) and ignores the class [MethodDataSource] row ("s").
Expected
- Reflection resolves the class-level
[MethodDataSource]member on the test class, and groups its rows by type as it does for other class sources. - A failure while discovering one class is reported as a failed test for that class only, not for the whole assembly.
- Both modes build the instance from the class source's row.
Related difference (class-row instantiations)
Moved to #7335: source-gen runs only one of two class-level sources that infer the same instantiation. Source-gen also does not infer class type arguments from a class-level [MethodDataSource] at all (#7333), and tuple rows from a class-level [MethodDataSource] do not instantiate a class with several type parameters (#7334).
Found while handling a review thread on #7149.
Activity
fix(engine): read a generic class's own class-level [MethodDataSource…
fix(engine): infer own-class [MethodDataSource] types from the member…
fix(engine): infer own-member row types from the constructor the inst…
fix(sourcegen): read own-class [MethodDataSource] on a class-row [Gen…
fix(sourcegen): read the own-class member on class-row [GenerateGener…
fix(sourcegen): read own-class [MethodDataSource] members with runtim…
fix(engine): unwrap TestDataRow when inferring a generic class's type…
fix(engine): infer a generic class's types from unwrapped TestDataRow…
fix(engine): give own-class member rows their declared types; merge c…
fix(sourcegen): read own-class tuple sources whose constructor column…
fix(sourcegen): read a matching own-class [MethodDataSource] on class…
fix(sourcegen): infer class types from a partially generic own-class …
fix(engine): pass every tuple column of an own-class class row to the…
fix(engine): infer own-class row types through [TestConstructor] and …
fix(engine): read class parameter metadata from the [TestConstructor]…
fix(engine): spread System.Tuple own-class rows over the constructor;…
fix(engine): read a generic class's own class-level [MethodDataSource…