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
@@ -0,0 +1,205 @@
// <auto-generated/>
#pragma warning disable

#nullable enable
namespace TUnit.Generated;
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("TUnit", "1.0.0.0")]
internal sealed class TUnit_TestProject_AssemblyRepeatTests_TestWithAssemblyRepeat_TestSource : global::TUnit.Core.Interfaces.SourceGenerator.ITestSource, global::TUnit.Core.Interfaces.SourceGenerator.ITestDescriptorSource
{
public async global::System.Collections.Generic.IAsyncEnumerable<global::TUnit.Core.TestMetadata> GetTestsAsync(string testSessionId, [global::System.Runtime.CompilerServices.EnumeratorCancellation] global::System.Threading.CancellationToken cancellationToken = default)
{
var metadata = new global::TUnit.Core.TestMetadata<global::TUnit.TestProject.AssemblyRepeatTests>
{
TestName = "TestWithAssemblyRepeat",
TestClassType = typeof(global::TUnit.TestProject.AssemblyRepeatTests),
TestMethodName = "TestWithAssemblyRepeat",
Dependencies = global::System.Array.Empty<global::TUnit.Core.TestDependency>(),
AttributeFactory = static () =>
[
new global::TUnit.Core.TestAttribute(),
new global::TUnit.Core.RepeatAttribute(3)
],
RepeatCount = 3,
DataSources = global::System.Array.Empty<global::TUnit.Core.IDataSourceAttribute>(),
ClassDataSources = global::System.Array.Empty<global::TUnit.Core.IDataSourceAttribute>(),
PropertyDataSources = global::System.Array.Empty<global::TUnit.Core.PropertyDataSource>(),
PropertyInjections = global::System.Array.Empty<global::TUnit.Core.PropertyInjectionData>(),
InheritanceDepth = 0,
FilePath = @"",
LineNumber = 9,
MethodMetadata = new global::TUnit.Core.MethodMetadata
{
Type = typeof(global::TUnit.TestProject.AssemblyRepeatTests),
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TUnit.TestProject.AssemblyRepeatTests)),
Name = "TestWithAssemblyRepeat",
GenericTypeCount = 0,
ReturnType = typeof(void),
ReturnTypeInfo = new global::TUnit.Core.ConcreteType(typeof(void)),
Parameters = global::System.Array.Empty<global::TUnit.Core.ParameterMetadata>(),
Class = global::TUnit.Core.ClassMetadata.GetOrAdd("TestsBase`1:global::TUnit.TestProject.AssemblyRepeatTests", static () =>
{
var classMetadata = new global::TUnit.Core.ClassMetadata
{
Type = typeof(global::TUnit.TestProject.AssemblyRepeatTests),
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TUnit.TestProject.AssemblyRepeatTests)),
Name = "AssemblyRepeatTests",
Namespace = "TUnit.TestProject",
Assembly = global::TUnit.Core.AssemblyMetadata.GetOrAdd("TestsBase`1", static () => new global::TUnit.Core.AssemblyMetadata { Name = "TestsBase`1" }),
Parameters = global::System.Array.Empty<global::TUnit.Core.ParameterMetadata>(),
Properties = global::System.Array.Empty<global::TUnit.Core.PropertyMetadata>(),
Parent = null
};
return classMetadata;
})
},
InstanceFactory = (typeArgs, args) => new global::TUnit.TestProject.AssemblyRepeatTests(),
InvokeTypedTest = static (instance, args, cancellationToken) =>
{
try
{
instance.TestWithAssemblyRepeat();
return default(global::System.Threading.Tasks.ValueTask);
}
catch (global::System.Exception ex)
{
return new global::System.Threading.Tasks.ValueTask(global::System.Threading.Tasks.Task.FromException(ex));
}
},
};
metadata.UseRuntimeDataGeneration(testSessionId);
yield return metadata;
yield break;
}
public global::System.Collections.Generic.IEnumerable<global::TUnit.Core.TestDescriptor> EnumerateTestDescriptors()
{
yield return new global::TUnit.Core.TestDescriptor
{
TestId = "TUnit.TestProject.AssemblyRepeatTests.TestWithAssemblyRepeat",
ClassName = "AssemblyRepeatTests",
MethodName = "TestWithAssemblyRepeat",
FullyQualifiedName = "TUnit.TestProject.AssemblyRepeatTests.TestWithAssemblyRepeat",
FilePath = @"",
LineNumber = 9,
Categories = global::System.Array.Empty<string>(),
Properties = global::System.Array.Empty<string>(),
HasDataSource = false,
RepeatCount = 3,
DependsOn = global::System.Array.Empty<string>(),
Materializer = GetTestsAsync
};
}
}
internal static class TUnit_TestProject_AssemblyRepeatTests_TestWithAssemblyRepeat_ModuleInitializer
{
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
global::TUnit.Core.SourceRegistrar.Register(typeof(global::TUnit.TestProject.AssemblyRepeatTests), new TUnit_TestProject_AssemblyRepeatTests_TestWithAssemblyRepeat_TestSource());
}
}


// ===== FILE SEPARATOR =====

// <auto-generated/>
#pragma warning disable

#nullable enable
namespace TUnit.Generated;
[global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("TUnit", "1.0.0.0")]
internal sealed class TUnit_TestProject_AssemblyRepeatTests_TestWithMethodRepeatOverride_TestSource : global::TUnit.Core.Interfaces.SourceGenerator.ITestSource, global::TUnit.Core.Interfaces.SourceGenerator.ITestDescriptorSource
{
public async global::System.Collections.Generic.IAsyncEnumerable<global::TUnit.Core.TestMetadata> GetTestsAsync(string testSessionId, [global::System.Runtime.CompilerServices.EnumeratorCancellation] global::System.Threading.CancellationToken cancellationToken = default)
{
var metadata = new global::TUnit.Core.TestMetadata<global::TUnit.TestProject.AssemblyRepeatTests>
{
TestName = "TestWithMethodRepeatOverride",
TestClassType = typeof(global::TUnit.TestProject.AssemblyRepeatTests),
TestMethodName = "TestWithMethodRepeatOverride",
Dependencies = global::System.Array.Empty<global::TUnit.Core.TestDependency>(),
AttributeFactory = static () =>
[
new global::TUnit.Core.TestAttribute(),
new global::TUnit.Core.RepeatAttribute(1),
new global::TUnit.Core.RepeatAttribute(3)
],
RepeatCount = 1,
DataSources = global::System.Array.Empty<global::TUnit.Core.IDataSourceAttribute>(),
ClassDataSources = global::System.Array.Empty<global::TUnit.Core.IDataSourceAttribute>(),
PropertyDataSources = global::System.Array.Empty<global::TUnit.Core.PropertyDataSource>(),
PropertyInjections = global::System.Array.Empty<global::TUnit.Core.PropertyInjectionData>(),
InheritanceDepth = 0,
FilePath = @"",
LineNumber = 14,
MethodMetadata = new global::TUnit.Core.MethodMetadata
{
Type = typeof(global::TUnit.TestProject.AssemblyRepeatTests),
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TUnit.TestProject.AssemblyRepeatTests)),
Name = "TestWithMethodRepeatOverride",
GenericTypeCount = 0,
ReturnType = typeof(void),
ReturnTypeInfo = new global::TUnit.Core.ConcreteType(typeof(void)),
Parameters = global::System.Array.Empty<global::TUnit.Core.ParameterMetadata>(),
Class = global::TUnit.Core.ClassMetadata.GetOrAdd("TestsBase`1:global::TUnit.TestProject.AssemblyRepeatTests", static () =>
{
var classMetadata = new global::TUnit.Core.ClassMetadata
{
Type = typeof(global::TUnit.TestProject.AssemblyRepeatTests),
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TUnit.TestProject.AssemblyRepeatTests)),
Name = "AssemblyRepeatTests",
Namespace = "TUnit.TestProject",
Assembly = global::TUnit.Core.AssemblyMetadata.GetOrAdd("TestsBase`1", static () => new global::TUnit.Core.AssemblyMetadata { Name = "TestsBase`1" }),
Parameters = global::System.Array.Empty<global::TUnit.Core.ParameterMetadata>(),
Properties = global::System.Array.Empty<global::TUnit.Core.PropertyMetadata>(),
Parent = null
};
return classMetadata;
})
},
InstanceFactory = (typeArgs, args) => new global::TUnit.TestProject.AssemblyRepeatTests(),
InvokeTypedTest = static (instance, args, cancellationToken) =>
{
try
{
instance.TestWithMethodRepeatOverride();
return default(global::System.Threading.Tasks.ValueTask);
}
catch (global::System.Exception ex)
{
return new global::System.Threading.Tasks.ValueTask(global::System.Threading.Tasks.Task.FromException(ex));
}
},
};
metadata.UseRuntimeDataGeneration(testSessionId);
yield return metadata;
yield break;
}
public global::System.Collections.Generic.IEnumerable<global::TUnit.Core.TestDescriptor> EnumerateTestDescriptors()
{
yield return new global::TUnit.Core.TestDescriptor
{
TestId = "TUnit.TestProject.AssemblyRepeatTests.TestWithMethodRepeatOverride",
ClassName = "AssemblyRepeatTests",
MethodName = "TestWithMethodRepeatOverride",
FullyQualifiedName = "TUnit.TestProject.AssemblyRepeatTests.TestWithMethodRepeatOverride",
FilePath = @"",
LineNumber = 14,
Categories = global::System.Array.Empty<string>(),
Properties = global::System.Array.Empty<string>(),
HasDataSource = false,
RepeatCount = 1,
DependsOn = global::System.Array.Empty<string>(),
Materializer = GetTestsAsync
};
}
}
internal static class TUnit_TestProject_AssemblyRepeatTests_TestWithMethodRepeatOverride_ModuleInitializer
{
[global::System.Runtime.CompilerServices.ModuleInitializer]
public static void Initialize()
{
global::TUnit.Core.SourceRegistrar.Register(typeof(global::TUnit.TestProject.AssemblyRepeatTests), new TUnit_TestProject_AssemblyRepeatTests_TestWithMethodRepeatOverride_TestSource());
}
}
43 changes: 43 additions & 0 deletions TUnit.Core.SourceGenerator.Tests/RepeatTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,47 @@ public Task Test() => RunTest(Path.Combine(Git.RootDirectory.FullName,
async generatedFiles =>
{
});

[Test]
public async Task Assembly_Level_Repeat()
{
var source = """
using TUnit.Core;

[assembly: Repeat(3)]

namespace TUnit.TestProject;

public class AssemblyRepeatTests
{
[Test]
public void TestWithAssemblyRepeat()
{
}

[Test]
[Repeat(1)]
public void TestWithMethodRepeatOverride()
{
}
}
""";

var tempFile = Path.GetTempFileName() + ".cs";
await File.WriteAllTextAsync(tempFile, source);

try
{
await TestMetadataGenerator.RunTest(tempFile, async generatedFiles =>
{
});
}
finally
{
if (File.Exists(tempFile))
{
File.Delete(tempFile);
}
}
}
}
3 changes: 2 additions & 1 deletion TUnit.Core.SourceGenerator/Analyzers/TestMethodAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ private static (bool isSkipped, string? skipReason) ExtractSkipInfo(IMethodSymbo
private static int ExtractRepeatCount(IMethodSymbol methodSymbol)
{
var repeatAttribute = methodSymbol.GetAttributes()
.FirstOrDefault(a => a.AttributeClass?.Name == "RepeatAttribute");
.FirstOrDefault(a => a.AttributeClass?.Name == "RepeatAttribute" &&
a.AttributeClass.ContainingNamespace?.ToDisplayString() == "TUnit.Core");

if (repeatAttribute is { ConstructorArguments.Length: > 0 })
{
Expand Down
31 changes: 29 additions & 2 deletions TUnit.Core.SourceGenerator/Generators/TestMetadataGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2102,6 +2102,7 @@ private static int ExtractRepeatCount(TestMethodMetadata testMethod)
foreach (var attr in testMethod.MethodAttributes)
{
if (attr.AttributeClass?.Name == "RepeatAttribute" &&
attr.AttributeClass.ContainingNamespace?.ToDisplayString() == "TUnit.Core" &&
attr.ConstructorArguments.Length > 0 &&
attr.ConstructorArguments[0].Value is int count)
{
Expand All @@ -2113,6 +2114,19 @@ private static int ExtractRepeatCount(TestMethodMetadata testMethod)
foreach (var attr in testMethod.TypeSymbol.GetAttributes())
{
if (attr.AttributeClass?.Name == "RepeatAttribute" &&
attr.AttributeClass.ContainingNamespace?.ToDisplayString() == "TUnit.Core" &&
attr.ConstructorArguments.Length > 0 &&
attr.ConstructorArguments[0].Value is int count)
{
return count;
}
}

// Check assembly attributes
foreach (var attr in testMethod.TypeSymbol.ContainingAssembly.GetAttributes())
{
if (attr.AttributeClass?.Name == "RepeatAttribute" &&
attr.AttributeClass.ContainingNamespace?.ToDisplayString() == "TUnit.Core" &&
attr.ConstructorArguments.Length > 0 &&
attr.ConstructorArguments[0].Value is int count)
{
Expand Down Expand Up @@ -5138,7 +5152,8 @@ private static void GenerateConcreteTestMetadataForNonGeneric(
{
// Check method-level RepeatAttribute first
var repeatAttribute = methodSymbol.GetAttributes()
.FirstOrDefault(a => a.AttributeClass?.Name == "RepeatAttribute");
.FirstOrDefault(a => a.AttributeClass?.Name == "RepeatAttribute" &&
a.AttributeClass.ContainingNamespace?.ToDisplayString() == "TUnit.Core");

if (repeatAttribute?.ConstructorArguments.Length > 0
&& repeatAttribute.ConstructorArguments[0].Value is int methodCount)
Expand All @@ -5148,14 +5163,26 @@ private static void GenerateConcreteTestMetadataForNonGeneric(

// Check class-level RepeatAttribute (can be inherited)
var classRepeatAttr = typeSymbol.GetAttributesIncludingBaseTypes()
.FirstOrDefault(a => a.AttributeClass?.Name == "RepeatAttribute");
.FirstOrDefault(a => a.AttributeClass?.Name == "RepeatAttribute" &&
a.AttributeClass.ContainingNamespace?.ToDisplayString() == "TUnit.Core");

if (classRepeatAttr?.ConstructorArguments.Length > 0
&& classRepeatAttr.ConstructorArguments[0].Value is int classCount)
{
return classCount;
}

// Check assembly-level RepeatAttribute
var assemblyRepeatAttr = typeSymbol.ContainingAssembly.GetAttributes()
.FirstOrDefault(a => a.AttributeClass?.Name == "RepeatAttribute" &&
a.AttributeClass.ContainingNamespace?.ToDisplayString() == "TUnit.Core");

if (assemblyRepeatAttr?.ConstructorArguments.Length > 0
&& assemblyRepeatAttr.ConstructorArguments[0].Value is int assemblyCount)
{
return assemblyCount;
}

// No repeat attribute found
return null;
}
Expand Down
3 changes: 2 additions & 1 deletion TUnit.Engine/Discovery/ReflectionTestDataCollector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1062,7 +1062,8 @@ private static Task<TestMetadata> BuildTestMetadata(
GenericMethodTypeArguments = testMethod.IsGenericMethodDefinition ? null : testMethod.GetGenericArguments(),
AttributeFactory = () => ReflectionAttributeExtractor.GetAllAttributes(testClass, testMethod),
RepeatCount = testMethod.GetCustomAttribute<RepeatAttribute>()?.Times
?? testClass.GetCustomAttribute<RepeatAttribute>()?.Times,
?? testClass.GetCustomAttribute<RepeatAttribute>()?.Times
?? testClass.Assembly.GetCustomAttribute<RepeatAttribute>()?.Times,
PropertyInjections = PropertySourceRegistry.DiscoverInjectableProperties(testClass),
InheritanceDepth = inheritanceDepth
});
Expand Down
Loading