Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
901cc5e
perf: optimize some repeated enumerations
thomhurst Oct 28, 2025
a8754b8
perf: enhance performance by caching results and reducing reflection …
thomhurst Oct 28, 2025
6927f55
Refactor test metadata generation to use static lambdas and support V…
thomhurst Oct 29, 2025
d7b68e7
refactor: optimize argument formatting in TestContext and improve tim…
thomhurst Oct 29, 2025
d7e7d3d
refactor: streamline argument formatting in ArgumentFormatter and opt…
thomhurst Oct 29, 2025
372de24
refactor: replace Array.Empty with array literals for improved readab…
thomhurst Oct 29, 2025
e132c13
refactor: replace Enumerable.Empty with array literal for improved pe…
thomhurst Oct 29, 2025
8bbb4ea
refactor: use static lambdas in GetReferencedAssemblies and GetInterf…
thomhurst Oct 29, 2025
3783332
refactor: use static lambdas in TestMetadataGenerator, AsyncConvert, …
thomhurst Oct 29, 2025
308611e
Refactor AttributeFactory to use static lambda expressions in test so…
thomhurst Oct 29, 2025
7c0b563
refactor: simplify counter increment logic in EventReceiverOrchestrat…
thomhurst Oct 29, 2025
368eeb6
refactor: update classData handling to fetch fresh data for each test…
thomhurst Oct 29, 2025
9eee0e9
refactor: streamline class data fetching to avoid redundant calls in …
thomhurst Oct 29, 2025
b5009b6
Add exception handling to test invocations and metadata generation
thomhurst Oct 29, 2025
5fbf6a5
refactor: introduce HashSetPool for efficient HashSet management duri…
thomhurst Oct 29, 2025
f0a4e54
refactor: extract RepeatCount from attributes to improve test metadat…
thomhurst Oct 29, 2025
505cb28
refactor: remove RepeatAttribute from DynamicTest attributes for clea…
thomhurst Oct 29, 2025
bf9515b
refactor: remove --fail-fast argument to allow all tests to run even …
thomhurst Oct 29, 2025
c6c7b15
refactor: invalidate cached eligible event objects when ClassInstance…
thomhurst Oct 29, 2025
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
23 changes: 14 additions & 9 deletions TUnit.Assertions/Conditions/HasDistinctItemsAssertion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,26 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<TCollecti
return Task.FromResult(AssertionResult.Failed("collection was null"));
}

var list = value.ToList();
var distinctList = list.Distinct().ToList();
var seen = new HashSet<TItem>();
var duplicates = new List<TItem>();
var totalCount = 0;

if (list.Count == distinctList.Count)
foreach (var item in value)
{
return Task.FromResult(AssertionResult.Passed);
totalCount++;
if (!seen.Add(item) && !duplicates.Contains(item))
{
duplicates.Add(item);
}
}

var duplicates = list.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
if (duplicates.Count == 0)
{
return Task.FromResult(AssertionResult.Passed);
}

return Task.FromResult(AssertionResult.Failed(
$"found {list.Count - distinctList.Count} duplicate(s): {string.Join(", ", duplicates)}"));
$"found {totalCount - seen.Count} duplicate(s): {string.Join(", ", duplicates)}"));
}

protected override string GetExpectation() => "to have distinct items";
Expand Down
22 changes: 20 additions & 2 deletions TUnit.Assertions/Conditions/StringAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<string> m

if (_ignoringWhitespace)
{
actualValue = string.Concat(actualValue.Where(c => !char.IsWhiteSpace(c)));
expectedValue = string.Concat(expectedValue.Where(c => !char.IsWhiteSpace(c)));
actualValue = RemoveWhitespace(actualValue);
expectedValue = RemoveWhitespace(expectedValue);
}

if (actualValue.Contains(expectedValue, _comparison))
Expand All @@ -101,6 +101,24 @@ protected override Task<AssertionResult> CheckAsync(EvaluationMetadata<string> m
return Task.FromResult(AssertionResult.Failed($"found \"{value}\""));
}

private static string RemoveWhitespace(string input)
{
if (string.IsNullOrEmpty(input))
{
return input;
}

var sb = new StringBuilder(input.Length);
foreach (var c in input)
{
if (!char.IsWhiteSpace(c))
{
sb.Append(c);
}
}
return sb.ToString();
}

protected override string GetExpectation() => $"to contain \"{_expected}\"";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal sealed class TUnit_TestProject_AbstractTests_ConcreteClass1_AssertClass
TestClassType = typeof(global::TUnit.TestProject.AbstractTests.ConcreteClass1),
TestMethodName = "AssertClassName",
Dependencies = global::System.Array.Empty<global::TUnit.Core.TestDependency>(),
AttributeFactory = () =>
AttributeFactory = static () =>
[
new global::TUnit.Core.TestAttribute(),
new global::TUnit.Core.InheritsTestsAttribute()
Expand All @@ -34,15 +34,15 @@ internal sealed class TUnit_TestProject_AbstractTests_ConcreteClass1_AssertClass
ReturnType = typeof(global::System.Threading.Tasks.Task),
ReturnTypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::System.Threading.Tasks.Task)),
Parameters = global::System.Array.Empty<global::TUnit.Core.ParameterMetadata>(),
Class = global::TUnit.Core.ClassMetadata.GetOrAdd("TestsBase`1:global::TUnit.TestProject.AbstractTests.ConcreteClass1", () =>
Class = global::TUnit.Core.ClassMetadata.GetOrAdd("TestsBase`1:global::TUnit.TestProject.AbstractTests.ConcreteClass1", static () =>
{
var classMetadata = new global::TUnit.Core.ClassMetadata
{
Type = typeof(global::TUnit.TestProject.AbstractTests.ConcreteClass1),
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TUnit.TestProject.AbstractTests.ConcreteClass1)),
Name = "ConcreteClass1",
Namespace = "TUnit.TestProject.AbstractTests",
Assembly = global::TUnit.Core.AssemblyMetadata.GetOrAdd("TestsBase`1", () => new global::TUnit.Core.AssemblyMetadata { Name = "TestsBase`1" }),
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
Expand All @@ -56,9 +56,16 @@ internal sealed class TUnit_TestProject_AbstractTests_ConcreteClass1_AssertClass
})
},
InstanceFactory = (typeArgs, args) => new global::TUnit.TestProject.AbstractTests.ConcreteClass1(),
InvokeTypedTest = async (instance, args, cancellationToken) =>
InvokeTypedTest = static (instance, args, cancellationToken) =>
{
await instance.AssertClassName();
try
{
return new global::System.Threading.Tasks.ValueTask(instance.AssertClassName());
}
catch (global::System.Exception ex)
{
return new global::System.Threading.Tasks.ValueTask(global::System.Threading.Tasks.Task.FromException(ex));
}
},
};
metadata.UseRuntimeDataGeneration(testSessionId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ internal sealed class TUnit_TestProject_AbstractTests_ConcreteClass2_SecondTest_
TestClassType = typeof(global::TUnit.TestProject.AbstractTests.ConcreteClass2),
TestMethodName = "SecondTest",
Dependencies = global::System.Array.Empty<global::TUnit.Core.TestDependency>(),
AttributeFactory = () =>
AttributeFactory = static () =>
[
new global::TUnit.Core.TestAttribute(),
new global::TUnit.Core.InheritsTestsAttribute(),
Expand All @@ -35,15 +35,15 @@ internal sealed class TUnit_TestProject_AbstractTests_ConcreteClass2_SecondTest_
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.AbstractTests.ConcreteClass2", () =>
Class = global::TUnit.Core.ClassMetadata.GetOrAdd("TestsBase`1:global::TUnit.TestProject.AbstractTests.ConcreteClass2", static () =>
{
var classMetadata = new global::TUnit.Core.ClassMetadata
{
Type = typeof(global::TUnit.TestProject.AbstractTests.ConcreteClass2),
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TUnit.TestProject.AbstractTests.ConcreteClass2)),
Name = "ConcreteClass2",
Namespace = "TUnit.TestProject.AbstractTests",
Assembly = global::TUnit.Core.AssemblyMetadata.GetOrAdd("TestsBase`1", () => new global::TUnit.Core.AssemblyMetadata { Name = "TestsBase`1" }),
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
Expand All @@ -57,10 +57,17 @@ internal sealed class TUnit_TestProject_AbstractTests_ConcreteClass2_SecondTest_
})
},
InstanceFactory = (typeArgs, args) => new global::TUnit.TestProject.AbstractTests.ConcreteClass2(),
InvokeTypedTest = async (instance, args, cancellationToken) =>
InvokeTypedTest = static (instance, args, cancellationToken) =>
{
instance.SecondTest();
await global::System.Threading.Tasks.Task.CompletedTask;
try
{
instance.SecondTest();
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);
Expand Down Expand Up @@ -95,7 +102,7 @@ internal sealed class TUnit_TestProject_AbstractTests_ConcreteClass2_AssertClass
TestClassType = typeof(global::TUnit.TestProject.AbstractTests.ConcreteClass2),
TestMethodName = "AssertClassName",
Dependencies = global::System.Array.Empty<global::TUnit.Core.TestDependency>(),
AttributeFactory = () =>
AttributeFactory = static () =>
[
new global::TUnit.Core.TestAttribute(),
new global::TUnit.Core.InheritsTestsAttribute(),
Expand All @@ -117,15 +124,15 @@ internal sealed class TUnit_TestProject_AbstractTests_ConcreteClass2_AssertClass
ReturnType = typeof(global::System.Threading.Tasks.Task),
ReturnTypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::System.Threading.Tasks.Task)),
Parameters = global::System.Array.Empty<global::TUnit.Core.ParameterMetadata>(),
Class = global::TUnit.Core.ClassMetadata.GetOrAdd("TestsBase`1:global::TUnit.TestProject.AbstractTests.ConcreteClass2", () =>
Class = global::TUnit.Core.ClassMetadata.GetOrAdd("TestsBase`1:global::TUnit.TestProject.AbstractTests.ConcreteClass2", static () =>
{
var classMetadata = new global::TUnit.Core.ClassMetadata
{
Type = typeof(global::TUnit.TestProject.AbstractTests.ConcreteClass2),
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TUnit.TestProject.AbstractTests.ConcreteClass2)),
Name = "ConcreteClass2",
Namespace = "TUnit.TestProject.AbstractTests",
Assembly = global::TUnit.Core.AssemblyMetadata.GetOrAdd("TestsBase`1", () => new global::TUnit.Core.AssemblyMetadata { Name = "TestsBase`1" }),
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
Expand All @@ -139,9 +146,16 @@ internal sealed class TUnit_TestProject_AbstractTests_ConcreteClass2_AssertClass
})
},
InstanceFactory = (typeArgs, args) => new global::TUnit.TestProject.AbstractTests.ConcreteClass2(),
InvokeTypedTest = async (instance, args, cancellationToken) =>
InvokeTypedTest = static (instance, args, cancellationToken) =>
{
await instance.AssertClassName();
try
{
return new global::System.Threading.Tasks.ValueTask(instance.AssertClassName());
}
catch (global::System.Exception ex)
{
return new global::System.Threading.Tasks.ValueTask(global::System.Threading.Tasks.Task.FromException(ex));
}
},
};
metadata.UseRuntimeDataGeneration(testSessionId);
Expand Down Expand Up @@ -176,7 +190,7 @@ internal sealed class TUnit_TestProject_AbstractTests_ConcreteClass1_AssertClass
TestClassType = typeof(global::TUnit.TestProject.AbstractTests.ConcreteClass1),
TestMethodName = "AssertClassName",
Dependencies = global::System.Array.Empty<global::TUnit.Core.TestDependency>(),
AttributeFactory = () =>
AttributeFactory = static () =>
[
new global::TUnit.Core.TestAttribute(),
new global::TUnit.Core.InheritsTestsAttribute()
Expand All @@ -197,15 +211,15 @@ internal sealed class TUnit_TestProject_AbstractTests_ConcreteClass1_AssertClass
ReturnType = typeof(global::System.Threading.Tasks.Task),
ReturnTypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::System.Threading.Tasks.Task)),
Parameters = global::System.Array.Empty<global::TUnit.Core.ParameterMetadata>(),
Class = global::TUnit.Core.ClassMetadata.GetOrAdd("TestsBase`1:global::TUnit.TestProject.AbstractTests.ConcreteClass1", () =>
Class = global::TUnit.Core.ClassMetadata.GetOrAdd("TestsBase`1:global::TUnit.TestProject.AbstractTests.ConcreteClass1", static () =>
{
var classMetadata = new global::TUnit.Core.ClassMetadata
{
Type = typeof(global::TUnit.TestProject.AbstractTests.ConcreteClass1),
TypeInfo = new global::TUnit.Core.ConcreteType(typeof(global::TUnit.TestProject.AbstractTests.ConcreteClass1)),
Name = "ConcreteClass1",
Namespace = "TUnit.TestProject.AbstractTests",
Assembly = global::TUnit.Core.AssemblyMetadata.GetOrAdd("TestsBase`1", () => new global::TUnit.Core.AssemblyMetadata { Name = "TestsBase`1" }),
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
Expand All @@ -219,9 +233,16 @@ internal sealed class TUnit_TestProject_AbstractTests_ConcreteClass1_AssertClass
})
},
InstanceFactory = (typeArgs, args) => new global::TUnit.TestProject.AbstractTests.ConcreteClass1(),
InvokeTypedTest = async (instance, args, cancellationToken) =>
InvokeTypedTest = static (instance, args, cancellationToken) =>
{
await instance.AssertClassName();
try
{
return new global::System.Threading.Tasks.ValueTask(instance.AssertClassName());
}
catch (global::System.Exception ex)
{
return new global::System.Threading.Tasks.ValueTask(global::System.Threading.Tasks.Task.FromException(ex));
}
},
};
metadata.UseRuntimeDataGeneration(testSessionId);
Expand Down
Loading
Loading