From 525b9ef93699624163a6ac158de5349c3acaa1b5 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 14:43:12 +0000
Subject: [PATCH 001/124] First code commit
---
.idea/.idea.TUnit/.idea/.gitignore | 13 +
.idea/.idea.TUnit/.idea/encodings.xml | 4 +
.idea/.idea.TUnit/.idea/indexLayout.xml | 8 +
TUnit.Assertions/Assert.cs | 22 ++
TUnit.Assertions/AssertCondition.cs | 22 ++
.../AssertConditions/FailureLocation.cs | 8 +
.../Generic/EqualsAssertCondition.cs | 19 ++
.../Generic/SameReferenceAssertCondition.cs | 18 ++
.../String/StringCompareAssertionSettings.cs | 7 +
.../String/StringEqualsAssertCondition.cs | 19 ++
.../Exceptions/AssertionException.cs | 8 +
TUnit.Assertions/Exceptions/TUnitException.cs | 22 ++
TUnit.Assertions/IAssertCondition.cs | 10 +
TUnit.Assertions/Is.cs | 16 +
TUnit.Assertions/Is_Strings.cs | 11 +
TUnit.Assertions/TUnit.Assertions.csproj | 9 +
.../Attributes/OneTimeSetUpAttribute.cs | 4 +
.../Attributes/OneTimeTearDownAttribute.cs | 4 +
TUnit.Core/Attributes/SetUpAttribute.cs | 4 +
TUnit.Core/Attributes/SkipAttribute.cs | 4 +
TUnit.Core/Attributes/TUnitAttribute.cs | 3 +
TUnit.Core/Attributes/TearDownAttribute.cs | 4 +
TUnit.Core/Attributes/TestAttribute.cs | 4 +
.../Attributes/TestWithDataAttribute.cs | 12 +
TUnit.Core/SourceLocation.cs | 3 +
TUnit.Core/TUnit.Core.csproj | 10 +
TUnit.Core/Test.cs | 81 +++++
TUnit.Core/TestCollection.cs | 35 +++
TUnit.Core/TypeInformation.cs | 8 +
.../Modules/AddLocalNuGetDirectoryModule.cs | 20 ++
.../Modules/AddReferencesToTestProject.cs | 36 +++
.../CreateLocalNuGetDirectoryModule.cs | 17 +
.../Modules/GetPackageProjectsModule.cs | 15 +
.../MoveNuGetPackagesToLocalSourceModule.cs | 24 ++
.../Modules/PackTUnitFilesModule.cs | 27 ++
TUnit.Pipeline/PackedProject.cs | 3 +
TUnit.Pipeline/Program.cs | 9 +
TUnit.Pipeline/TUnit.Pipeline.csproj | 16 +
TUnit.TestAdapter/AssemblyLoader.cs | 35 +++
TUnit.TestAdapter/AsyncTestExecutor.cs | 294 ++++++++++++++++++
.../Constants/TestAdapterConstants.cs | 7 +
.../Extensions/TestExtensions.cs | 19 ++
TUnit.TestAdapter/ProcessingTest.cs | 5 +
TUnit.TestAdapter/Properties/AssemblyInfo.cs | 2 +
TUnit.TestAdapter/SourceLocationHelper.cs | 40 +++
TUnit.TestAdapter/TUnit.TestAdapter.csproj | 21 ++
TUnit.TestAdapter/TestAndClass.cs | 5 +
TUnit.TestAdapter/TestCollector.cs | 36 +++
TUnit.TestAdapter/TestDiscoverer.cs | 28 ++
TUnit.TestAdapter/TestExecutor.cs | 52 ++++
TUnit.TestAdapter/TestsLoader.cs | 61 ++++
TUnit.TestProject/GlobalUsings.cs | 0
TUnit.TestProject/TUnit.TestProject.csproj | 21 ++
TUnit.TestProject/Tests.cs | 17 +
TUnit.sln | 46 +++
TUnit/TUnit.csproj | 14 +
56 files changed, 1262 insertions(+)
create mode 100644 .idea/.idea.TUnit/.idea/.gitignore
create mode 100644 .idea/.idea.TUnit/.idea/encodings.xml
create mode 100644 .idea/.idea.TUnit/.idea/indexLayout.xml
create mode 100644 TUnit.Assertions/Assert.cs
create mode 100644 TUnit.Assertions/AssertCondition.cs
create mode 100644 TUnit.Assertions/AssertConditions/FailureLocation.cs
create mode 100644 TUnit.Assertions/AssertConditions/Generic/EqualsAssertCondition.cs
create mode 100644 TUnit.Assertions/AssertConditions/Generic/SameReferenceAssertCondition.cs
create mode 100644 TUnit.Assertions/AssertConditions/String/StringCompareAssertionSettings.cs
create mode 100644 TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs
create mode 100644 TUnit.Assertions/Exceptions/AssertionException.cs
create mode 100644 TUnit.Assertions/Exceptions/TUnitException.cs
create mode 100644 TUnit.Assertions/IAssertCondition.cs
create mode 100644 TUnit.Assertions/Is.cs
create mode 100644 TUnit.Assertions/Is_Strings.cs
create mode 100644 TUnit.Assertions/TUnit.Assertions.csproj
create mode 100644 TUnit.Core/Attributes/OneTimeSetUpAttribute.cs
create mode 100644 TUnit.Core/Attributes/OneTimeTearDownAttribute.cs
create mode 100644 TUnit.Core/Attributes/SetUpAttribute.cs
create mode 100644 TUnit.Core/Attributes/SkipAttribute.cs
create mode 100644 TUnit.Core/Attributes/TUnitAttribute.cs
create mode 100644 TUnit.Core/Attributes/TearDownAttribute.cs
create mode 100644 TUnit.Core/Attributes/TestAttribute.cs
create mode 100644 TUnit.Core/Attributes/TestWithDataAttribute.cs
create mode 100644 TUnit.Core/SourceLocation.cs
create mode 100644 TUnit.Core/TUnit.Core.csproj
create mode 100644 TUnit.Core/Test.cs
create mode 100644 TUnit.Core/TestCollection.cs
create mode 100644 TUnit.Core/TypeInformation.cs
create mode 100644 TUnit.Pipeline/Modules/AddLocalNuGetDirectoryModule.cs
create mode 100644 TUnit.Pipeline/Modules/AddReferencesToTestProject.cs
create mode 100644 TUnit.Pipeline/Modules/CreateLocalNuGetDirectoryModule.cs
create mode 100644 TUnit.Pipeline/Modules/GetPackageProjectsModule.cs
create mode 100644 TUnit.Pipeline/Modules/MoveNuGetPackagesToLocalSourceModule.cs
create mode 100644 TUnit.Pipeline/Modules/PackTUnitFilesModule.cs
create mode 100644 TUnit.Pipeline/PackedProject.cs
create mode 100644 TUnit.Pipeline/Program.cs
create mode 100644 TUnit.Pipeline/TUnit.Pipeline.csproj
create mode 100644 TUnit.TestAdapter/AssemblyLoader.cs
create mode 100644 TUnit.TestAdapter/AsyncTestExecutor.cs
create mode 100644 TUnit.TestAdapter/Constants/TestAdapterConstants.cs
create mode 100644 TUnit.TestAdapter/Extensions/TestExtensions.cs
create mode 100644 TUnit.TestAdapter/ProcessingTest.cs
create mode 100644 TUnit.TestAdapter/Properties/AssemblyInfo.cs
create mode 100644 TUnit.TestAdapter/SourceLocationHelper.cs
create mode 100644 TUnit.TestAdapter/TUnit.TestAdapter.csproj
create mode 100644 TUnit.TestAdapter/TestAndClass.cs
create mode 100644 TUnit.TestAdapter/TestCollector.cs
create mode 100644 TUnit.TestAdapter/TestDiscoverer.cs
create mode 100644 TUnit.TestAdapter/TestExecutor.cs
create mode 100644 TUnit.TestAdapter/TestsLoader.cs
create mode 100644 TUnit.TestProject/GlobalUsings.cs
create mode 100644 TUnit.TestProject/TUnit.TestProject.csproj
create mode 100644 TUnit.TestProject/Tests.cs
create mode 100644 TUnit.sln
create mode 100644 TUnit/TUnit.csproj
diff --git a/.idea/.idea.TUnit/.idea/.gitignore b/.idea/.idea.TUnit/.idea/.gitignore
new file mode 100644
index 0000000000..0be7a56a1e
--- /dev/null
+++ b/.idea/.idea.TUnit/.idea/.gitignore
@@ -0,0 +1,13 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/projectSettingsUpdater.xml
+/modules.xml
+/contentModel.xml
+/.idea.TUnit.iml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.idea.TUnit/.idea/encodings.xml b/.idea/.idea.TUnit/.idea/encodings.xml
new file mode 100644
index 0000000000..df87cf951f
--- /dev/null
+++ b/.idea/.idea.TUnit/.idea/encodings.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.TUnit/.idea/indexLayout.xml b/.idea/.idea.TUnit/.idea/indexLayout.xml
new file mode 100644
index 0000000000..7b08163ceb
--- /dev/null
+++ b/.idea/.idea.TUnit/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TUnit.Assertions/Assert.cs b/TUnit.Assertions/Assert.cs
new file mode 100644
index 0000000000..609b4a54bc
--- /dev/null
+++ b/TUnit.Assertions/Assert.cs
@@ -0,0 +1,22 @@
+using TUnit.Assertions.Exceptions;
+
+namespace TUnit.Assertions;
+
+public static class Assert
+{
+ public static void That(T value, AssertCondition assertCondition)
+ {
+ if (!assertCondition.Matches(value))
+ {
+ var message = GetMessage(assertCondition, value);
+ throw new AssertionException(message);
+ }
+ }
+
+ private static string GetMessage(AssertCondition assertCondition, T actualValue)
+ {
+ return assertCondition.MessageFactory != null
+ ? assertCondition.MessageFactory((assertCondition.ExpectedValue, actualValue))
+ : assertCondition.Message;
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertCondition.cs b/TUnit.Assertions/AssertCondition.cs
new file mode 100644
index 0000000000..b56e622ce3
--- /dev/null
+++ b/TUnit.Assertions/AssertCondition.cs
@@ -0,0 +1,22 @@
+namespace TUnit.Assertions;
+
+public abstract class AssertCondition : IAssertCondition
+{
+ internal AssertCondition(T expected)
+ {
+ ExpectedValue = expected;
+ }
+
+ internal Func<(T expectedValue, T actualValue), string>? MessageFactory { get; private set; }
+
+ public T ExpectedValue { get; }
+ public abstract bool Matches(T actualValue);
+
+ public abstract string Message { get; protected set; }
+
+ public IAssertCondition WithMessage(Func<(T expectedValue, T actualValue), string> messageFactory)
+ {
+ MessageFactory = messageFactory;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/FailureLocation.cs b/TUnit.Assertions/AssertConditions/FailureLocation.cs
new file mode 100644
index 0000000000..b9bd39ac5b
--- /dev/null
+++ b/TUnit.Assertions/AssertConditions/FailureLocation.cs
@@ -0,0 +1,8 @@
+namespace TUnit.Assertions.AssertConditions;
+
+public record FailureLocation
+{
+ public long Position { get; }
+ public object? ExpectedValue { get; }
+ public object? ActualValue { get; }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/Generic/EqualsAssertCondition.cs b/TUnit.Assertions/AssertConditions/Generic/EqualsAssertCondition.cs
new file mode 100644
index 0000000000..5ea7757dc3
--- /dev/null
+++ b/TUnit.Assertions/AssertConditions/Generic/EqualsAssertCondition.cs
@@ -0,0 +1,19 @@
+namespace TUnit.Assertions.AssertConditions.Generic;
+
+public class EqualsAssertCondition : AssertCondition
+{
+ private readonly T _expected;
+
+ public EqualsAssertCondition(T expected) : base(expected)
+ {
+ _expected = expected;
+ }
+
+ public override bool Matches(T actualValue)
+ {
+ Message = $"Expected {_expected} but received {actualValue}";
+ return Equals(actualValue, _expected);
+ }
+
+ public override string Message { get; protected set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/Generic/SameReferenceAssertCondition.cs b/TUnit.Assertions/AssertConditions/Generic/SameReferenceAssertCondition.cs
new file mode 100644
index 0000000000..f31a6b8ea0
--- /dev/null
+++ b/TUnit.Assertions/AssertConditions/Generic/SameReferenceAssertCondition.cs
@@ -0,0 +1,18 @@
+namespace TUnit.Assertions.AssertConditions.Generic;
+
+public class SameReferenceAssertCondition : AssertCondition
+{
+ private readonly T _expected;
+
+ public SameReferenceAssertCondition(T expected) : base(expected)
+ {
+ _expected = expected;
+ }
+
+ public override bool Matches(T actualValue)
+ {
+ return ReferenceEquals(actualValue, _expected);
+ }
+
+ public override string Message { get; protected set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/String/StringCompareAssertionSettings.cs b/TUnit.Assertions/AssertConditions/String/StringCompareAssertionSettings.cs
new file mode 100644
index 0000000000..398c05b6f1
--- /dev/null
+++ b/TUnit.Assertions/AssertConditions/String/StringCompareAssertionSettings.cs
@@ -0,0 +1,7 @@
+namespace TUnit.Assertions.AssertConditions.String;
+
+public record StringCompareAssertionSettings
+{
+ public StringComparison StringComparison { get; init; } = StringComparison.Ordinal;
+ public bool Trim { get; init; } = false;
+};
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs b/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs
new file mode 100644
index 0000000000..783edef8ab
--- /dev/null
+++ b/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs
@@ -0,0 +1,19 @@
+namespace TUnit.Assertions.AssertConditions.String;
+
+public class StringEqualsAssertCondition : AssertCondition
+{
+ private readonly StringComparison _stringComparison;
+
+ public StringEqualsAssertCondition(string expected, StringComparison stringComparison) : base(expected)
+ {
+ _stringComparison = stringComparison;
+ }
+
+ public override bool Matches(string actualValue)
+ {
+ Message = $"Expected {ExpectedValue} but received {actualValue}";
+ return string.Equals(actualValue, ExpectedValue, _stringComparison);
+ }
+
+ public override string Message { get; protected set; } = string.Empty;
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/Exceptions/AssertionException.cs b/TUnit.Assertions/Exceptions/AssertionException.cs
new file mode 100644
index 0000000000..d5bfe8360a
--- /dev/null
+++ b/TUnit.Assertions/Exceptions/AssertionException.cs
@@ -0,0 +1,8 @@
+namespace TUnit.Assertions.Exceptions;
+
+public class AssertionException : TUnitException
+{
+ public AssertionException(string? message) : base(message)
+ {
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/Exceptions/TUnitException.cs b/TUnit.Assertions/Exceptions/TUnitException.cs
new file mode 100644
index 0000000000..e448e3472a
--- /dev/null
+++ b/TUnit.Assertions/Exceptions/TUnitException.cs
@@ -0,0 +1,22 @@
+using System.Runtime.Serialization;
+
+namespace TUnit.Assertions.Exceptions;
+
+public class TUnitException : Exception
+{
+ public TUnitException()
+ {
+ }
+
+ protected TUnitException(SerializationInfo info, StreamingContext context) : base(info, context)
+ {
+ }
+
+ public TUnitException(string? message) : base(message)
+ {
+ }
+
+ public TUnitException(string? message, Exception? innerException) : base(message, innerException)
+ {
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/IAssertCondition.cs b/TUnit.Assertions/IAssertCondition.cs
new file mode 100644
index 0000000000..f4d510cb4e
--- /dev/null
+++ b/TUnit.Assertions/IAssertCondition.cs
@@ -0,0 +1,10 @@
+namespace TUnit.Assertions;
+
+public interface IAssertCondition
+{
+ internal T ExpectedValue { get; }
+
+ public bool Matches(T actualValue);
+
+ internal string Message { get; }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/Is.cs b/TUnit.Assertions/Is.cs
new file mode 100644
index 0000000000..c3d437ed41
--- /dev/null
+++ b/TUnit.Assertions/Is.cs
@@ -0,0 +1,16 @@
+using TUnit.Assertions.AssertConditions.Generic;
+
+namespace TUnit.Assertions;
+
+public static partial class Is
+{
+ public static IAssertCondition EqualTo(T expected)
+ {
+ return new EqualsAssertCondition(expected);
+ }
+
+ public static IAssertCondition SameReference(T expected)
+ {
+ return new SameReferenceAssertCondition(expected);
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/Is_Strings.cs b/TUnit.Assertions/Is_Strings.cs
new file mode 100644
index 0000000000..c553cc9b70
--- /dev/null
+++ b/TUnit.Assertions/Is_Strings.cs
@@ -0,0 +1,11 @@
+using TUnit.Assertions.AssertConditions.String;
+
+namespace TUnit.Assertions;
+
+public static partial class Is
+{
+ public static IAssertCondition EqualTo(string expected, StringComparison stringComparison = StringComparison.Ordinal)
+ {
+ return new StringEqualsAssertCondition(expected, stringComparison);
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/TUnit.Assertions.csproj b/TUnit.Assertions/TUnit.Assertions.csproj
new file mode 100644
index 0000000000..ca62071d4f
--- /dev/null
+++ b/TUnit.Assertions/TUnit.Assertions.csproj
@@ -0,0 +1,9 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
diff --git a/TUnit.Core/Attributes/OneTimeSetUpAttribute.cs b/TUnit.Core/Attributes/OneTimeSetUpAttribute.cs
new file mode 100644
index 0000000000..40c13ba781
--- /dev/null
+++ b/TUnit.Core/Attributes/OneTimeSetUpAttribute.cs
@@ -0,0 +1,4 @@
+namespace TUnit.Core.Attributes;
+
+[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
+public class OneTimeSetUpAttribute : TUnitAttribute;
\ No newline at end of file
diff --git a/TUnit.Core/Attributes/OneTimeTearDownAttribute.cs b/TUnit.Core/Attributes/OneTimeTearDownAttribute.cs
new file mode 100644
index 0000000000..9a828fc5ef
--- /dev/null
+++ b/TUnit.Core/Attributes/OneTimeTearDownAttribute.cs
@@ -0,0 +1,4 @@
+namespace TUnit.Core.Attributes;
+
+[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
+public class OneTimeTearDownAttribute : TUnitAttribute;
\ No newline at end of file
diff --git a/TUnit.Core/Attributes/SetUpAttribute.cs b/TUnit.Core/Attributes/SetUpAttribute.cs
new file mode 100644
index 0000000000..96c1c4b5c9
--- /dev/null
+++ b/TUnit.Core/Attributes/SetUpAttribute.cs
@@ -0,0 +1,4 @@
+namespace TUnit.Core.Attributes;
+
+[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
+public class SetUpAttribute : TUnitAttribute;
\ No newline at end of file
diff --git a/TUnit.Core/Attributes/SkipAttribute.cs b/TUnit.Core/Attributes/SkipAttribute.cs
new file mode 100644
index 0000000000..22bb734105
--- /dev/null
+++ b/TUnit.Core/Attributes/SkipAttribute.cs
@@ -0,0 +1,4 @@
+namespace TUnit.Core.Attributes;
+
+[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
+public class SkipAttribute : TUnitAttribute;
\ No newline at end of file
diff --git a/TUnit.Core/Attributes/TUnitAttribute.cs b/TUnit.Core/Attributes/TUnitAttribute.cs
new file mode 100644
index 0000000000..d8eb96163a
--- /dev/null
+++ b/TUnit.Core/Attributes/TUnitAttribute.cs
@@ -0,0 +1,3 @@
+namespace TUnit.Core.Attributes;
+
+public class TUnitAttribute : Attribute;
\ No newline at end of file
diff --git a/TUnit.Core/Attributes/TearDownAttribute.cs b/TUnit.Core/Attributes/TearDownAttribute.cs
new file mode 100644
index 0000000000..7032514a65
--- /dev/null
+++ b/TUnit.Core/Attributes/TearDownAttribute.cs
@@ -0,0 +1,4 @@
+namespace TUnit.Core.Attributes;
+
+[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
+public class TearDownAttribute : TUnitAttribute;
\ No newline at end of file
diff --git a/TUnit.Core/Attributes/TestAttribute.cs b/TUnit.Core/Attributes/TestAttribute.cs
new file mode 100644
index 0000000000..48c90ae33d
--- /dev/null
+++ b/TUnit.Core/Attributes/TestAttribute.cs
@@ -0,0 +1,4 @@
+namespace TUnit.Core.Attributes;
+
+[AttributeUsage(AttributeTargets.Method)]
+public class TestAttribute : TUnitAttribute;
\ No newline at end of file
diff --git a/TUnit.Core/Attributes/TestWithDataAttribute.cs b/TUnit.Core/Attributes/TestWithDataAttribute.cs
new file mode 100644
index 0000000000..64f8a17825
--- /dev/null
+++ b/TUnit.Core/Attributes/TestWithDataAttribute.cs
@@ -0,0 +1,12 @@
+namespace TUnit.Core.Attributes;
+
+[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
+public class TestWithDataAttribute : TUnitAttribute
+{
+ public object[] Values { get; }
+
+ public TestWithDataAttribute(params object[] values)
+ {
+ Values = values;
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Core/SourceLocation.cs b/TUnit.Core/SourceLocation.cs
new file mode 100644
index 0000000000..c98e2e3bc9
--- /dev/null
+++ b/TUnit.Core/SourceLocation.cs
@@ -0,0 +1,3 @@
+namespace TUnit.Core;
+
+public record SourceLocation(string? FileName, int MinLineNumber, int MaxLineNumber);
\ No newline at end of file
diff --git a/TUnit.Core/TUnit.Core.csproj b/TUnit.Core/TUnit.Core.csproj
new file mode 100644
index 0000000000..c81818ddbf
--- /dev/null
+++ b/TUnit.Core/TUnit.Core.csproj
@@ -0,0 +1,10 @@
+
+
+
+ net6.0
+ enable
+ enable
+ default
+
+
+
diff --git a/TUnit.Core/Test.cs b/TUnit.Core/Test.cs
new file mode 100644
index 0000000000..acf720e459
--- /dev/null
+++ b/TUnit.Core/Test.cs
@@ -0,0 +1,81 @@
+using System.Reflection;
+using TUnit.Core.Attributes;
+
+namespace TUnit.Core;
+
+public record Test
+{
+ public Test(MethodInfo MethodInfo,
+ SourceLocation SourceLocation,
+ object?[]? Arguments)
+ {
+ var classType = MethodInfo.DeclaringType!;
+
+ this.MethodInfo = MethodInfo;
+ this.Arguments = Arguments;
+ this.SourceLocation = SourceLocation;
+
+ TestName = MethodInfo.Name;
+ ClassName = classType.Name;
+ FullyQualifiedClassName = classType.FullName!;
+ Assembly = classType.Assembly;
+ Source = classType.Assembly.Location;
+ FullName = $"{classType.FullName}.{MethodInfo.Name}{GetArguments(Arguments)}";
+ IsSkipped = MethodInfo.CustomAttributes
+ .Concat(classType.CustomAttributes)
+ .Any(x => x.AttributeType == typeof(SkipAttribute));
+
+ FileName = SourceLocation.FileName;
+ MinLineNumber = SourceLocation.MinLineNumber;
+ MaxLineNumber = SourceLocation.MaxLineNumber;
+ }
+
+ public Guid Id { get; } = Guid.NewGuid();
+
+ public string TestName { get; }
+
+ public string ClassName { get; }
+
+ public string FullyQualifiedClassName { get; set; }
+
+ public Assembly Assembly { get; }
+
+ public string Source { get; }
+ public string FullName { get; }
+ public MethodInfo MethodInfo { get; init; }
+ public string? FileName { get; set; }
+ public int MinLineNumber { get; set; }
+ public int MaxLineNumber { get; set; }
+ public object?[]? Arguments { get; init; }
+ public SourceLocation SourceLocation { get; }
+
+ public bool IsSkipped { get; }
+
+ public void Deconstruct(out MethodInfo methodInfo, out object?[]? arguments)
+ {
+ methodInfo = MethodInfo;
+ arguments = Arguments;
+ }
+
+ private static string GetArguments(object?[]? arguments)
+ {
+ if (arguments is null)
+ {
+ return string.Empty;
+ }
+
+ var argsAsString = arguments.Select(StringifyArgument);
+
+ return $"({string.Join(", ", argsAsString)})";
+ }
+
+ private static string StringifyArgument(object? obj)
+ {
+ return obj switch
+ {
+ null => "null",
+ string stringValue => $"\"{stringValue}\"",
+ _ => obj.ToString() ?? string.Empty
+ };
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Core/TestCollection.cs b/TUnit.Core/TestCollection.cs
new file mode 100644
index 0000000000..06f2ccf2cc
--- /dev/null
+++ b/TUnit.Core/TestCollection.cs
@@ -0,0 +1,35 @@
+using System.Collections.Immutable;
+
+namespace TUnit.Core;
+
+///
+/// Contains a collection of tests that can be run. Should be disposed to ensure that the
+/// temporary containing the test assemblies is unloaded.
+///
+public sealed class TestCollection
+{
+ ///
+ /// The test sources (assembly file names).
+ ///
+ public IReadOnlyList Sources { get; }
+
+ ///
+ /// The tests that were discovered.
+ ///
+ public IReadOnlyList Tests { get; private set; }
+
+ public TestCollection(IEnumerable sources, IEnumerable tests)
+ {
+ Sources = ImmutableArray.CreateRange(sources);
+ Tests = ImmutableArray.CreateRange(tests);
+ }
+
+ ///
+ /// Filters the tests in the test collection. This is used for partial test runs.
+ ///
+ /// The filter to apply.
+ public void Filter(Func filter)
+ {
+ Tests = ImmutableArray.CreateRange(Tests.Where(filter));
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Core/TypeInformation.cs b/TUnit.Core/TypeInformation.cs
new file mode 100644
index 0000000000..f8244c6e59
--- /dev/null
+++ b/TUnit.Core/TypeInformation.cs
@@ -0,0 +1,8 @@
+using System.Reflection;
+
+namespace TUnit.Core;
+
+public record TypeInformation(Assembly Assembly)
+{
+ public Type[] Types { get; } = Assembly.GetTypes();
+}
\ No newline at end of file
diff --git a/TUnit.Pipeline/Modules/AddLocalNuGetDirectoryModule.cs b/TUnit.Pipeline/Modules/AddLocalNuGetDirectoryModule.cs
new file mode 100644
index 0000000000..fbd3b7a9da
--- /dev/null
+++ b/TUnit.Pipeline/Modules/AddLocalNuGetDirectoryModule.cs
@@ -0,0 +1,20 @@
+using ModularPipelines.Attributes;
+using ModularPipelines.Context;
+using ModularPipelines.DotNet.Extensions;
+using ModularPipelines.DotNet.Options;
+using ModularPipelines.Models;
+using ModularPipelines.Modules;
+
+namespace TUnit.Pipeline.Modules;
+
+[DependsOn]
+public class AddLocalNuGetDirectoryModule : Module
+{
+ protected override async Task ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
+ {
+ var directoryResult = await GetModule();
+
+ return await context.DotNet().Nuget.Add
+ .Source(new DotNetNugetAddSourceOptions(directoryResult.Value!), cancellationToken);
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Pipeline/Modules/AddReferencesToTestProject.cs b/TUnit.Pipeline/Modules/AddReferencesToTestProject.cs
new file mode 100644
index 0000000000..19f7effa90
--- /dev/null
+++ b/TUnit.Pipeline/Modules/AddReferencesToTestProject.cs
@@ -0,0 +1,36 @@
+using EnumerableAsyncProcessor.Extensions;
+using ModularPipelines.Attributes;
+using ModularPipelines.Context;
+using ModularPipelines.DotNet.Extensions;
+using ModularPipelines.DotNet.Options;
+using ModularPipelines.Extensions;
+using ModularPipelines.Git.Extensions;
+using ModularPipelines.Models;
+using ModularPipelines.Modules;
+
+namespace TUnit.Pipeline.Modules;
+[DependsOn]
+[DependsOn]
+[DependsOn]
+public class AddReferencesToTestProject : Module
+{
+ protected override async Task ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
+ {
+ var testProject = context.Git().RootDirectory.FindFile(x => x.Name == "TUnit.TestProject.csproj").AssertExists();
+ var projects = await GetModule();
+ var localNugetDirectory = await GetModule();
+
+ await projects.Value!
+ .ForEachAsync(
+ x => context.DotNet().Remove.Package(new DotNetRemovePackageOptions(x.Name), cancellationToken),
+ cancellationToken: cancellationToken)
+ .ProcessOneAtATime();
+
+ return await projects.Value!
+ .SelectAsync(async x => await context.DotNet().Add.Package(new DotNetAddPackageOptions(testProject, x.Name)
+ {
+ Version = x.Version,
+ Source = localNugetDirectory.Value!
+ }, cancellationToken), cancellationToken: cancellationToken).ProcessOneAtATime();
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Pipeline/Modules/CreateLocalNuGetDirectoryModule.cs b/TUnit.Pipeline/Modules/CreateLocalNuGetDirectoryModule.cs
new file mode 100644
index 0000000000..2f2ae5d93c
--- /dev/null
+++ b/TUnit.Pipeline/Modules/CreateLocalNuGetDirectoryModule.cs
@@ -0,0 +1,17 @@
+using ModularPipelines.Context;
+using ModularPipelines.Extensions;
+using ModularPipelines.FileSystem;
+using ModularPipelines.Modules;
+
+namespace TUnit.Pipeline.Modules;
+
+public class CreateLocalNuGetDirectoryModule : Module
+{
+ protected override Task ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
+ {
+ return context.FileSystem.GetFolder(Environment.SpecialFolder.UserProfile)
+ .GetFolder("LocalNuGet")
+ .Create()
+ .AsTask();
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Pipeline/Modules/GetPackageProjectsModule.cs b/TUnit.Pipeline/Modules/GetPackageProjectsModule.cs
new file mode 100644
index 0000000000..7a78526f8f
--- /dev/null
+++ b/TUnit.Pipeline/Modules/GetPackageProjectsModule.cs
@@ -0,0 +1,15 @@
+using ModularPipelines.Context;
+using ModularPipelines.Extensions;
+using ModularPipelines.Git.Extensions;
+using ModularPipelines.Modules;
+using File = ModularPipelines.FileSystem.File;
+
+namespace TUnit.Pipeline.Modules;
+
+public class GetPackageProjectsModule : Module>
+{
+ protected override Task?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
+ {
+ return context.Git().RootDirectory.GetFiles(x => x.Extension == ".csproj").Where(x => !x.Name.Contains("Pipeline")).Where(x => !x.Name.Contains("TestProject")).ToList().AsTask?>();
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Pipeline/Modules/MoveNuGetPackagesToLocalSourceModule.cs b/TUnit.Pipeline/Modules/MoveNuGetPackagesToLocalSourceModule.cs
new file mode 100644
index 0000000000..bbffe9f141
--- /dev/null
+++ b/TUnit.Pipeline/Modules/MoveNuGetPackagesToLocalSourceModule.cs
@@ -0,0 +1,24 @@
+using ModularPipelines.Attributes;
+using ModularPipelines.Context;
+using ModularPipelines.Extensions;
+using ModularPipelines.Git.Extensions;
+using ModularPipelines.Modules;
+using File = ModularPipelines.FileSystem.File;
+
+namespace TUnit.Pipeline.Modules;
+
+[DependsOn]
+public class MoveNuGetPackagesToLocalSourceModule : Module>
+{
+ protected override async Task?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
+ {
+ var localNugetDirectory = await GetModule();
+
+ var nugetPackages = context.Git().RootDirectory
+ .GetFiles(x => x.Extension is ".nupkg" or ".snupkg");
+
+ return nugetPackages
+ .Select(x => x.MoveTo(localNugetDirectory.Value.AssertExists()))
+ .ToList();
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Pipeline/Modules/PackTUnitFilesModule.cs b/TUnit.Pipeline/Modules/PackTUnitFilesModule.cs
new file mode 100644
index 0000000000..26395b55f3
--- /dev/null
+++ b/TUnit.Pipeline/Modules/PackTUnitFilesModule.cs
@@ -0,0 +1,27 @@
+using EnumerableAsyncProcessor.Extensions;
+using ModularPipelines.Context;
+using ModularPipelines.DotNet.Extensions;
+using ModularPipelines.DotNet.Options;
+using ModularPipelines.Models;
+using ModularPipelines.Modules;
+using ModularPipelines.Attributes;
+
+namespace TUnit.Pipeline.Modules;
+[DependsOn]
+public class PackTUnitFilesModule : Module>
+{
+ protected override async Task?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
+ {
+ var projects = await GetModule();
+
+ var guid = Guid.NewGuid();
+ var version = $"0.0.1-alpha{guid}";
+
+ var packedProjects = await projects.Value!.SelectAsync(async project =>
+ {
+ return await context.DotNet().Pack(new DotNetPackOptions(project) { Properties = new[] { new KeyValue("Version", version), new KeyValue("PackageVersion", version) } }, cancellationToken);
+ }, cancellationToken: cancellationToken).ProcessOneAtATime();
+
+ return projects.Value!.Select(x => new PackedProject(x.Name, version)).ToList();
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Pipeline/PackedProject.cs b/TUnit.Pipeline/PackedProject.cs
new file mode 100644
index 0000000000..34cb1938b7
--- /dev/null
+++ b/TUnit.Pipeline/PackedProject.cs
@@ -0,0 +1,3 @@
+namespace TUnit.Pipeline.Modules;
+
+public record PackedProject(string Name, string Version);
\ No newline at end of file
diff --git a/TUnit.Pipeline/Program.cs b/TUnit.Pipeline/Program.cs
new file mode 100644
index 0000000000..982b27198f
--- /dev/null
+++ b/TUnit.Pipeline/Program.cs
@@ -0,0 +1,9 @@
+using ModularPipelines.Extensions;
+using ModularPipelines.Host;
+
+await PipelineHostBuilder.Create()
+ .ConfigureServices((context, collection) =>
+ {
+ collection.AddModulesFromAssembly(typeof(Program).Assembly);
+ })
+ .ExecutePipelineAsync();
\ No newline at end of file
diff --git a/TUnit.Pipeline/TUnit.Pipeline.csproj b/TUnit.Pipeline/TUnit.Pipeline.csproj
new file mode 100644
index 0000000000..3590d330fc
--- /dev/null
+++ b/TUnit.Pipeline/TUnit.Pipeline.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ net6.0
+ enable
+ enable
+ default
+
+
+
+
+
+
+
+
diff --git a/TUnit.TestAdapter/AssemblyLoader.cs b/TUnit.TestAdapter/AssemblyLoader.cs
new file mode 100644
index 0000000000..b4bdeaf457
--- /dev/null
+++ b/TUnit.TestAdapter/AssemblyLoader.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+
+namespace TUnit.TestAdapter;
+
+internal class AssemblyLoader
+{
+ internal Assembly? LoadByPath(string assemblyPath)
+ {
+ if (!File.Exists(assemblyPath))
+ {
+ return null;
+ }
+
+ try
+ {
+ return Assembly.LoadFrom(assemblyPath);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ internal Assembly? LoadByName(AssemblyName assemblyName)
+ {
+ try
+ {
+ return Assembly.Load(assemblyName);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TUnit.TestAdapter/AsyncTestExecutor.cs b/TUnit.TestAdapter/AsyncTestExecutor.cs
new file mode 100644
index 0000000000..12455e55c4
--- /dev/null
+++ b/TUnit.TestAdapter/AsyncTestExecutor.cs
@@ -0,0 +1,294 @@
+using System.Collections.Concurrent;
+using System.Diagnostics;
+using System.Reflection;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
+using TUnit.Core;
+using TUnit.Core.Attributes;
+using TUnit.TestAdapter.Extensions;
+
+namespace TUnit.TestAdapter;
+
+public class AsyncTestExecutor(CancellationTokenSource cancellationTokenSource)
+{
+ private bool _canRunAnotherTest = true;
+
+ private readonly ConcurrentDictionary _oneTimeSetUpRegistry = new();
+ private readonly ConcurrentDictionary _oneTimeTearDownRegistry = new();
+
+ public async Task RunInAsyncContext(IEnumerable tests, IRunContext? runContext, IFrameworkHandle? frameworkHandle)
+ {
+ var allTestsOrderedByClass = tests
+ .GroupBy(x => x.FullyQualifiedClassName)
+ .SelectMany(x => x.ToList())
+ .ToList();
+
+ var queue = new Queue(allTestsOrderedByClass);
+
+ if (queue.Count is 0)
+ {
+ return;
+ }
+
+ MonitorSystemResources();
+
+ var executingTests = new List();
+
+ await foreach (var testCase in ProcessQueue(queue, frameworkHandle))
+ {
+ executingTests.Add(testCase);
+
+ SetupRunOneTimeTearDownForClass(testCase, allTestsOrderedByClass, executingTests);
+
+ executingTests.RemoveAll(x => x.Task.IsCompletedSuccessfully);
+ }
+
+ executingTests.RemoveAll(x => x.Task.IsCompletedSuccessfully);
+
+ await Task.WhenAll(executingTests.Select(x => x.Task));
+ await Task.WhenAll(_oneTimeTearDownRegistry.Values);
+ }
+
+ private void SetupRunOneTimeTearDownForClass(ProcessingTest processingTest,
+ IEnumerable allTestsOrderedByClass,
+ IEnumerable executingTests)
+ {
+ var lastTestForClass = allTestsOrderedByClass.Last(x =>
+ x.FullyQualifiedClassName == processingTest.Test.FullyQualifiedClassName);
+
+ if (processingTest.Test.FullName != lastTestForClass.FullName)
+ {
+ return;
+ }
+
+ var executingTestsForThisClass = executingTests
+ .Where(x => x.Test.FullyQualifiedClassName == processingTest.Test.FullyQualifiedClassName)
+ .Select(x => x.Task)
+ .ToArray();
+
+ Task.WhenAll(executingTestsForThisClass).ContinueWith(x =>
+ {
+ _ = _oneTimeTearDownRegistry.GetOrAdd(processingTest.Test.FullyQualifiedClassName,
+ ExecuteOneTimeTearDowns(processingTest.Class));
+
+ return Task.CompletedTask;
+ });
+ }
+
+ private async IAsyncEnumerable ProcessQueue(Queue queue, ITestExecutionRecorder? frameworkHandle)
+ {
+ while (queue.Count > 0)
+ {
+ if (_canRunAnotherTest)
+ {
+ var test = queue.Dequeue();
+
+ var @class = CreateTestClass(test);
+
+ yield return new ProcessingTest(test, @class, ProcessTest(test, @class, frameworkHandle));
+ }
+ else
+ {
+ await Task.Delay(100);
+ }
+ }
+ }
+
+ private async Task ProcessTest(Test test, object @class, ITestExecutionRecorder? frameworkHandle)
+ {
+ await ExecuteTestMethod(test, @class, frameworkHandle);
+ }
+
+ private static object CreateTestClass(Test test)
+ {
+ return Activator.CreateInstance(test.MethodInfo.DeclaringType!)!;
+ }
+
+ private async ValueTask ExecuteTestMethod(Test test, object @class, ITestExecutionRecorder? frameworkHandle)
+ {
+ var testCase = test.ToTestCase();
+
+ if (test.IsSkipped)
+ {
+ frameworkHandle?.RecordEnd(testCase, TestOutcome.Skipped);
+ return;
+ }
+
+ frameworkHandle?.RecordStart(testCase);
+
+ var start = DateTimeOffset.Now;
+ var stopwatch = Stopwatch.StartNew();
+
+ try
+ {
+ await ExecuteTest(test, @class);
+
+ frameworkHandle?.RecordEnd(testCase, TestOutcome.Passed);
+
+ frameworkHandle?.RecordResult(new TestResult(testCase)
+ {
+ Outcome = TestOutcome.Passed,
+
+ DisplayName = test.TestName,
+ StartTime = start,
+ EndTime = DateTimeOffset.Now,
+ Duration = stopwatch.Elapsed,
+ ComputerName = Environment.MachineName,
+ });
+ }
+ catch (Exception e)
+ {
+ frameworkHandle?.RecordEnd(testCase, TestOutcome.Failed);
+
+ frameworkHandle?.RecordResult(new TestResult(testCase)
+ {
+ Outcome = TestOutcome.Failed,
+
+ DisplayName = test.TestName,
+ ErrorMessage = e.Message,
+ ErrorStackTrace = e.StackTrace,
+ StartTime = start,
+ EndTime = DateTimeOffset.Now,
+ Duration = stopwatch.Elapsed,
+ ComputerName = Environment.MachineName,
+ });
+ }
+ }
+
+ private async Task ExecuteTest(Test test, object @class)
+ {
+ await ExecuteSetUps(@class);
+
+ var methodExecutionObject = test.MethodInfo.Invoke(@class, test.Arguments);
+
+ if (methodExecutionObject is Task task)
+ {
+ await task;
+ }
+
+ await ExecuteTearDowns(@class);
+ }
+
+ private async Task ExecuteSetUps(object @class)
+ {
+ await _oneTimeSetUpRegistry.GetOrAdd(@class.GetType().FullName!, _ => ExecuteOneTimeSetUps(@class));
+
+ var setUpMethods = @class.GetType()
+ .GetMethods()
+ .Where(x => !x.IsStatic)
+ .Where(x => x.CustomAttributes.Any(attributeData => attributeData.AttributeType == typeof(SetUpAttribute)));
+
+ foreach (var setUpMethod in setUpMethods)
+ {
+ var result = setUpMethod.Invoke(@class, null);
+
+ if (result is Task task)
+ {
+ await task;
+ }
+ }
+ }
+
+ private async Task ExecuteTearDowns(object @class)
+ {
+ var tearDownMethods = @class.GetType()
+ .GetMethods()
+ .Where(x => !x.IsStatic)
+ .Where(x => x.CustomAttributes.Any(attributeData => attributeData.AttributeType == typeof(TearDownAttribute)));
+
+ var exceptions = new List();
+
+ foreach (var tearDownMethod in tearDownMethods)
+ {
+ try
+ {
+ var result = tearDownMethod.Invoke(@class, null);
+
+ if (result is Task task)
+ {
+ await task;
+ }
+ }
+ catch (Exception e)
+ {
+ exceptions.Add(e);
+ }
+ }
+
+ if (exceptions.Any())
+ {
+ throw new AggregateException(exceptions);
+ }
+ }
+
+ private async Task ExecuteOneTimeSetUps(object @class)
+ {
+ var oneTimeSetUpMethods = @class.GetType()
+ .GetMethods()
+ .Where(x => x.IsStatic)
+ .Where(x => x.CustomAttributes.Any(attributeData => attributeData.AttributeType == typeof(OneTimeSetUpAttribute)));
+
+ foreach (var oneTimeSetUpMethod in oneTimeSetUpMethods)
+ {
+ var result = oneTimeSetUpMethod.Invoke(null, BindingFlags.Static | BindingFlags.Public, null, null, null);
+
+ if (result is Task task)
+ {
+ await task;
+ }
+ }
+ }
+
+ private async Task ExecuteOneTimeTearDowns(object @class)
+ {
+ var oneTimeTearDownMethods = @class.GetType()
+ .GetMethods()
+ .Where(x => x.IsStatic)
+ .Where(x => x.CustomAttributes.Any(attributeData => attributeData.AttributeType == typeof(OneTimeTearDownAttribute)));
+
+ foreach (var oneTimeTearDownMethod in oneTimeTearDownMethods)
+ {
+ var result = oneTimeTearDownMethod.Invoke(null, BindingFlags.Static | BindingFlags.Public, null, null, null);
+
+ if (result is Task task)
+ {
+ await task;
+ }
+ }
+ }
+
+ private void MonitorSystemResources()
+ {
+ Task.Factory.StartNew(async _ =>
+ {
+ while (!cancellationTokenSource.IsCancellationRequested)
+ {
+ await Task.Delay(500);
+
+ var cpuUsage = await GetCpuUsageForProcess();
+
+ _canRunAnotherTest = cpuUsage < 80;
+ }
+ }, null, TaskCreationOptions.LongRunning);
+ }
+
+ private async Task GetCpuUsageForProcess()
+ {
+ var startTime = DateTime.UtcNow;
+
+ var startCpuUsage = Process.GetCurrentProcess().TotalProcessorTime;
+ await Task.Delay(500);
+
+ var endTime = DateTime.UtcNow;
+
+ var endCpuUsage = Process.GetCurrentProcess().TotalProcessorTime;
+
+ var cpuUsedMs = (endCpuUsage - startCpuUsage).TotalMilliseconds;
+
+ var totalMsPassed = (endTime - startTime).TotalMilliseconds;
+
+ var cpuUsageTotal = cpuUsedMs / (Environment.ProcessorCount * totalMsPassed);
+
+ return cpuUsageTotal * 100;
+ }
+}
\ No newline at end of file
diff --git a/TUnit.TestAdapter/Constants/TestAdapterConstants.cs b/TUnit.TestAdapter/Constants/TestAdapterConstants.cs
new file mode 100644
index 0000000000..5c7d052d29
--- /dev/null
+++ b/TUnit.TestAdapter/Constants/TestAdapterConstants.cs
@@ -0,0 +1,7 @@
+namespace TUnit.TestAdapter.Constants;
+
+internal static class TestAdapterConstants
+{
+ internal const string ExecutorUriString = "executor://tunit/TestRunner/net";
+ internal static readonly Uri ExecutorUri = new(ExecutorUriString);
+}
\ No newline at end of file
diff --git a/TUnit.TestAdapter/Extensions/TestExtensions.cs b/TUnit.TestAdapter/Extensions/TestExtensions.cs
new file mode 100644
index 0000000000..6a2da9ac39
--- /dev/null
+++ b/TUnit.TestAdapter/Extensions/TestExtensions.cs
@@ -0,0 +1,19 @@
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using TUnit.Core;
+using TUnit.TestAdapter.Constants;
+
+namespace TUnit.TestAdapter.Extensions;
+
+public static class TestExtensions
+{
+ public static TestCase ToTestCase(this Test test)
+ {
+ return new TestCase(test.FullName, TestAdapterConstants.ExecutorUri, test.Source)
+ {
+ DisplayName = test.TestName,
+ Id = test.Id,
+ CodeFilePath = test.FileName,
+ LineNumber = test.MinLineNumber
+ };
+ }
+}
\ No newline at end of file
diff --git a/TUnit.TestAdapter/ProcessingTest.cs b/TUnit.TestAdapter/ProcessingTest.cs
new file mode 100644
index 0000000000..1ea3b1f5af
--- /dev/null
+++ b/TUnit.TestAdapter/ProcessingTest.cs
@@ -0,0 +1,5 @@
+using TUnit.Core;
+
+namespace TUnit.TestAdapter;
+
+public record ProcessingTest(Test Test, object Class, Task Task);
\ No newline at end of file
diff --git a/TUnit.TestAdapter/Properties/AssemblyInfo.cs b/TUnit.TestAdapter/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..c65238fb38
--- /dev/null
+++ b/TUnit.TestAdapter/Properties/AssemblyInfo.cs
@@ -0,0 +1,2 @@
+
+
diff --git a/TUnit.TestAdapter/SourceLocationHelper.cs b/TUnit.TestAdapter/SourceLocationHelper.cs
new file mode 100644
index 0000000000..46e8bb4a01
--- /dev/null
+++ b/TUnit.TestAdapter/SourceLocationHelper.cs
@@ -0,0 +1,40 @@
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using TUnit.Core;
+
+namespace TUnit.TestAdapter;
+
+public class SourceLocationHelper
+{
+ private readonly IMessageLogger? _logger;
+ private static readonly SourceLocation EmptySourceLocation = new(null, 0, 0);
+
+ public SourceLocationHelper(IMessageLogger? logger)
+ {
+ _logger = logger;
+ }
+
+ public SourceLocation GetSourceLocation(DiaSession diaSession, string className, string methodName)
+ {
+ try
+ {
+ var navigationData = diaSession.GetNavigationDataForMethod(className, methodName);
+
+ if (navigationData is null)
+ {
+ _logger?.SendMessage(TestMessageLevel.Error, $"No navigation data found for {className}.{methodName}");
+
+ return EmptySourceLocation;
+ }
+
+ return new SourceLocation(navigationData.FileName, navigationData.MinLineNumber, navigationData.MaxLineNumber);
+ }
+ catch (Exception e)
+ {
+ _logger?.SendMessage(TestMessageLevel.Error, $"Error retrieving source location for {className}.{methodName}");
+ _logger?.SendMessage(TestMessageLevel.Error, e.ToString());
+
+ return EmptySourceLocation;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TUnit.TestAdapter/TUnit.TestAdapter.csproj b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
new file mode 100644
index 0000000000..e01dcca5d4
--- /dev/null
+++ b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net6.0
+ enable
+ enable
+ default
+ Library
+ true
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TUnit.TestAdapter/TestAndClass.cs b/TUnit.TestAdapter/TestAndClass.cs
new file mode 100644
index 0000000000..fa1d944872
--- /dev/null
+++ b/TUnit.TestAdapter/TestAndClass.cs
@@ -0,0 +1,5 @@
+using TUnit.Core;
+
+namespace TUnit.TestAdapter;
+
+public record TestAndClass(Test Test, object Class);
\ No newline at end of file
diff --git a/TUnit.TestAdapter/TestCollector.cs b/TUnit.TestAdapter/TestCollector.cs
new file mode 100644
index 0000000000..dcadf7c83c
--- /dev/null
+++ b/TUnit.TestAdapter/TestCollector.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using TUnit.Core;
+
+namespace TUnit.TestAdapter;
+
+public class TestCollector
+{
+ private readonly IMessageLogger? _messageLogger;
+
+ public TestCollector(IMessageLogger? messageLogger)
+ {
+ _messageLogger = messageLogger;
+ }
+
+ public TestCollection CollectionFromSources(IEnumerable sources)
+ {
+ var sourcesAsList = sources.ToList();
+
+ return new TestCollection(sourcesAsList, TestsFromSources(sourcesAsList));
+ }
+
+ public IEnumerable TestsFromSources(IEnumerable sources)
+ {
+ var assemblyLoader = new AssemblyLoader();
+ var testsLoader = new TestsLoader(_messageLogger);
+
+ var tests = sources
+ .Select(assemblyLoader.LoadByPath)
+ .OfType()
+ .Select(x => new TypeInformation(x))
+ .SelectMany(x => testsLoader.GetTests(x));
+
+ return tests;
+ }
+}
\ No newline at end of file
diff --git a/TUnit.TestAdapter/TestDiscoverer.cs b/TUnit.TestAdapter/TestDiscoverer.cs
new file mode 100644
index 0000000000..cebbe8170c
--- /dev/null
+++ b/TUnit.TestAdapter/TestDiscoverer.cs
@@ -0,0 +1,28 @@
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using TUnit.TestAdapter.Constants;
+using TUnit.TestAdapter.Extensions;
+
+namespace TUnit.TestAdapter;
+
+[FileExtension(".dll")]
+[FileExtension(".exe")]
+[DefaultExecutorUri(TestAdapterConstants.ExecutorUriString)]
+[ExtensionUri(TestAdapterConstants.ExecutorUriString)]
+public class TestDiscoverer : ITestDiscoverer
+{
+ public void DiscoverTests(IEnumerable sources,
+ IDiscoveryContext discoveryContext,
+ IMessageLogger logger,
+ ITestCaseDiscoverySink discoverySink)
+ {
+ var testCollector = new TestCollector(logger);
+
+ foreach (var test in testCollector.TestsFromSources(sources))
+ {
+ logger.SendMessage(TestMessageLevel.Informational, "Test found: " + test.FullName);
+ discoverySink.SendTestCase(test.ToTestCase());
+ }
+ }
+}
\ No newline at end of file
diff --git a/TUnit.TestAdapter/TestExecutor.cs b/TUnit.TestAdapter/TestExecutor.cs
new file mode 100644
index 0000000000..9bb266b7c8
--- /dev/null
+++ b/TUnit.TestAdapter/TestExecutor.cs
@@ -0,0 +1,52 @@
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
+using TUnit.TestAdapter.Constants;
+
+namespace TUnit.TestAdapter;
+
+[FileExtension(".dll")]
+[FileExtension(".exe")]
+[DefaultExecutorUri(TestAdapterConstants.ExecutorUriString)]
+[ExtensionUri(TestAdapterConstants.ExecutorUriString)]
+public class TestExecutor : ITestExecutor2
+{
+ private readonly CancellationTokenSource _cancellationTokenSource = new();
+ private readonly AsyncTestExecutor _asyncTestExecutor;
+
+ public TestExecutor()
+ {
+ _asyncTestExecutor = new AsyncTestExecutor(_cancellationTokenSource);
+ }
+
+ public void RunTests(IEnumerable? tests, IRunContext? runContext, IFrameworkHandle? frameworkHandle)
+ {
+ RunTests(tests?.Select(x => x.Source), runContext, frameworkHandle);
+ }
+
+ public void RunTests(IEnumerable? sources, IRunContext? runContext, IFrameworkHandle? frameworkHandle)
+ {
+ if (sources is null)
+ {
+ return;
+ }
+
+ var tests = new TestCollector(frameworkHandle).TestsFromSources(sources);
+
+ _asyncTestExecutor.RunInAsyncContext(tests, runContext, frameworkHandle).GetAwaiter().GetResult();
+ }
+
+ public void Cancel()
+ {
+ _cancellationTokenSource.Cancel();
+ }
+
+ public bool ShouldAttachToTestHost(IEnumerable? tests, IRunContext runContext)
+ {
+ return ShouldAttachToTestHost(tests?.Select(x => x.Source), runContext);
+ }
+
+ public bool ShouldAttachToTestHost(IEnumerable? sources, IRunContext runContext)
+ {
+ return runContext.IsBeingDebugged;
+ }
+}
\ No newline at end of file
diff --git a/TUnit.TestAdapter/TestsLoader.cs b/TUnit.TestAdapter/TestsLoader.cs
new file mode 100644
index 0000000000..0a3af6f7f6
--- /dev/null
+++ b/TUnit.TestAdapter/TestsLoader.cs
@@ -0,0 +1,61 @@
+using System.Reflection;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
+using TUnit.Core;
+using TUnit.Core.Attributes;
+
+namespace TUnit.TestAdapter;
+
+public class TestsLoader
+{
+ private static readonly Type[] TestAttributes = [typeof(TestAttribute), typeof(TestWithDataAttribute)];
+ private readonly SourceLocationHelper _sourceLocationHelper;
+
+ public TestsLoader(IMessageLogger? messageLogger)
+ {
+ _sourceLocationHelper = new SourceLocationHelper(messageLogger);
+ }
+
+ public IEnumerable GetTests(TypeInformation typeInformation)
+ {
+ using var diaSession = new DiaSession(typeInformation.Assembly.Location);
+
+ var methods = typeInformation.Types.SelectMany(x => x.GetMethods());
+
+ foreach (var methodInfo in methods)
+ {
+ if (!HasTestAttributes(methodInfo))
+ {
+ continue;
+ }
+
+ var sourceLocation = _sourceLocationHelper
+ .GetSourceLocation(diaSession, methodInfo.DeclaringType!.Name, methodInfo.Name);
+
+ foreach (var testWithDataAttribute in methodInfo.CustomAttributes.Where(x => x.AttributeType == typeof(TestWithDataAttribute)))
+ {
+ var arguments = testWithDataAttribute.ConstructorArguments.Select(x => x.Value);
+
+ yield return new Test(
+ MethodInfo: methodInfo,
+ SourceLocation: sourceLocation,
+ Arguments: arguments.ToArray()
+ );
+ }
+
+ if(methodInfo.CustomAttributes.Any(x => x.AttributeType == typeof(TestAttribute)))
+ {
+ yield return new Test(
+ MethodInfo: methodInfo,
+ SourceLocation: sourceLocation,
+ Arguments: null
+ );
+ }
+ }
+ }
+
+ private static bool HasTestAttributes(MethodInfo methodInfo)
+ {
+ return methodInfo.CustomAttributes.Select(x => x.AttributeType).Intersect(TestAttributes).Any();
+ }
+}
\ No newline at end of file
diff --git a/TUnit.TestProject/GlobalUsings.cs b/TUnit.TestProject/GlobalUsings.cs
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/TUnit.TestProject/TUnit.TestProject.csproj b/TUnit.TestProject/TUnit.TestProject.csproj
new file mode 100644
index 0000000000..d89af0898a
--- /dev/null
+++ b/TUnit.TestProject/TUnit.TestProject.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TUnit.TestProject/Tests.cs b/TUnit.TestProject/Tests.cs
new file mode 100644
index 0000000000..02465296fa
--- /dev/null
+++ b/TUnit.TestProject/Tests.cs
@@ -0,0 +1,17 @@
+using TUnit.Core.Attributes;
+
+namespace TUnit.TestProject;
+
+public class Tests
+{
+ // [SetUp]
+ // public void Setup()
+ // {
+ // }
+
+ [Test]
+ public void Test1()
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/TUnit.sln b/TUnit.sln
new file mode 100644
index 0000000000..8e93b5d74c
--- /dev/null
+++ b/TUnit.sln
@@ -0,0 +1,46 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUnit", "TUnit\TUnit.csproj", "{45A310AD-151B-4E0F-8A2C-FC55D31B16BD}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUnit.TestAdapter", "TUnit.TestAdapter\TUnit.TestAdapter.csproj", "{37BF09F2-8CDA-4388-A13A-AB24572AACD5}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUnit.Core", "TUnit.Core\TUnit.Core.csproj", "{252CD110-7923-403F-9CCA-827E7352BF54}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUnit.TestProject", "TUnit.TestProject\TUnit.TestProject.csproj", "{2F9038D3-96AD-4D86-B06B-9E59C2B941A8}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUnit.Assertions", "TUnit.Assertions\TUnit.Assertions.csproj", "{1F1276E0-0DB5-4CD4-BF4B-74760E50B923}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TUnit.Pipeline", "TUnit.Pipeline\TUnit.Pipeline.csproj", "{527337F5-0F78-4141-901C-72EA81222AB1}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {45A310AD-151B-4E0F-8A2C-FC55D31B16BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {45A310AD-151B-4E0F-8A2C-FC55D31B16BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {45A310AD-151B-4E0F-8A2C-FC55D31B16BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {45A310AD-151B-4E0F-8A2C-FC55D31B16BD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {37BF09F2-8CDA-4388-A13A-AB24572AACD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {37BF09F2-8CDA-4388-A13A-AB24572AACD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {37BF09F2-8CDA-4388-A13A-AB24572AACD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {37BF09F2-8CDA-4388-A13A-AB24572AACD5}.Release|Any CPU.Build.0 = Release|Any CPU
+ {252CD110-7923-403F-9CCA-827E7352BF54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {252CD110-7923-403F-9CCA-827E7352BF54}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {252CD110-7923-403F-9CCA-827E7352BF54}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {252CD110-7923-403F-9CCA-827E7352BF54}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2F9038D3-96AD-4D86-B06B-9E59C2B941A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2F9038D3-96AD-4D86-B06B-9E59C2B941A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2F9038D3-96AD-4D86-B06B-9E59C2B941A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2F9038D3-96AD-4D86-B06B-9E59C2B941A8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1F1276E0-0DB5-4CD4-BF4B-74760E50B923}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1F1276E0-0DB5-4CD4-BF4B-74760E50B923}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1F1276E0-0DB5-4CD4-BF4B-74760E50B923}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1F1276E0-0DB5-4CD4-BF4B-74760E50B923}.Release|Any CPU.Build.0 = Release|Any CPU
+ {527337F5-0F78-4141-901C-72EA81222AB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {527337F5-0F78-4141-901C-72EA81222AB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {527337F5-0F78-4141-901C-72EA81222AB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {527337F5-0F78-4141-901C-72EA81222AB1}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/TUnit/TUnit.csproj b/TUnit/TUnit.csproj
new file mode 100644
index 0000000000..d75a0cf9c9
--- /dev/null
+++ b/TUnit/TUnit.csproj
@@ -0,0 +1,14 @@
+
+
+
+ net6.0
+ enable
+ enable
+
+
+
+
+
+
+
+
From ecdda9d76c3882689316ba5f8ffc7bdae997d578 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 16:31:12 +0000
Subject: [PATCH 002/124] Pipeline fix
---
TUnit.Assertions/Is.cs | 4 +--
TUnit.Assertions/Is_Strings.cs | 2 +-
.../Modules/AddLocalNuGetDirectoryModule.cs | 10 +++++++
.../Modules/AddReferencesToTestProject.cs | 16 +++++++++-
TUnit.Pipeline/Modules/CleanProjectsModule.cs | 29 +++++++++++++++++++
.../MoveNuGetPackagesToLocalSourceModule.cs | 6 ++++
.../Modules/PackTUnitFilesModule.cs | 16 +++++++---
TUnit.TestAdapter/TUnit.TestAdapter.csproj | 13 ++++++---
TUnit.TestProject/TUnit.TestProject.csproj | 6 +++-
TUnit.TestProject/Tests.cs | 4 ++-
TUnit/TUnit.csproj | 1 +
11 files changed, 93 insertions(+), 14 deletions(-)
create mode 100644 TUnit.Pipeline/Modules/CleanProjectsModule.cs
diff --git a/TUnit.Assertions/Is.cs b/TUnit.Assertions/Is.cs
index c3d437ed41..004b56d802 100644
--- a/TUnit.Assertions/Is.cs
+++ b/TUnit.Assertions/Is.cs
@@ -4,12 +4,12 @@ namespace TUnit.Assertions;
public static partial class Is
{
- public static IAssertCondition EqualTo(T expected)
+ public static AssertCondition EqualTo(T expected)
{
return new EqualsAssertCondition(expected);
}
- public static IAssertCondition SameReference(T expected)
+ public static AssertCondition SameReference(T expected)
{
return new SameReferenceAssertCondition(expected);
}
diff --git a/TUnit.Assertions/Is_Strings.cs b/TUnit.Assertions/Is_Strings.cs
index c553cc9b70..5ecf030d19 100644
--- a/TUnit.Assertions/Is_Strings.cs
+++ b/TUnit.Assertions/Is_Strings.cs
@@ -4,7 +4,7 @@ namespace TUnit.Assertions;
public static partial class Is
{
- public static IAssertCondition EqualTo(string expected, StringComparison stringComparison = StringComparison.Ordinal)
+ public static AssertCondition EqualTo(string expected, StringComparison stringComparison = StringComparison.Ordinal)
{
return new StringEqualsAssertCondition(expected, stringComparison);
}
diff --git a/TUnit.Pipeline/Modules/AddLocalNuGetDirectoryModule.cs b/TUnit.Pipeline/Modules/AddLocalNuGetDirectoryModule.cs
index fbd3b7a9da..1ca62e5dbf 100644
--- a/TUnit.Pipeline/Modules/AddLocalNuGetDirectoryModule.cs
+++ b/TUnit.Pipeline/Modules/AddLocalNuGetDirectoryModule.cs
@@ -14,6 +14,16 @@ public class AddLocalNuGetDirectoryModule : Module
{
var directoryResult = await GetModule();
+ var currentNuGetSources = await context.DotNet()
+ .Nuget
+ .List
+ .Source(token: cancellationToken);
+
+ if (currentNuGetSources.StandardOutput.Contains(directoryResult.Value!))
+ {
+ return currentNuGetSources;
+ }
+
return await context.DotNet().Nuget.Add
.Source(new DotNetNugetAddSourceOptions(directoryResult.Value!), cancellationToken);
}
diff --git a/TUnit.Pipeline/Modules/AddReferencesToTestProject.cs b/TUnit.Pipeline/Modules/AddReferencesToTestProject.cs
index 19f7effa90..303a46f414 100644
--- a/TUnit.Pipeline/Modules/AddReferencesToTestProject.cs
+++ b/TUnit.Pipeline/Modules/AddReferencesToTestProject.cs
@@ -7,6 +7,7 @@
using ModularPipelines.Git.Extensions;
using ModularPipelines.Models;
using ModularPipelines.Modules;
+using File = ModularPipelines.FileSystem.File;
namespace TUnit.Pipeline.Modules;
[DependsOn]
@@ -22,15 +23,28 @@ public class AddReferencesToTestProject : Module
await projects.Value!
.ForEachAsync(
- x => context.DotNet().Remove.Package(new DotNetRemovePackageOptions(x.Name), cancellationToken),
+ x => RemovePackage(context, cancellationToken, testProject, x),
cancellationToken: cancellationToken)
.ProcessOneAtATime();
return await projects.Value!
+ .Where(x => x.Name == "TUnit.TestAdapter")
.SelectAsync(async x => await context.DotNet().Add.Package(new DotNetAddPackageOptions(testProject, x.Name)
{
Version = x.Version,
Source = localNugetDirectory.Value!
}, cancellationToken), cancellationToken: cancellationToken).ProcessOneAtATime();
}
+
+ private static async Task RemovePackage(IPipelineContext context, CancellationToken cancellationToken, File testProject, PackedProject x)
+ {
+ var existingPackages = await context.DotNet().List.Package(new DotNetListPackageOptions(testProject), cancellationToken);
+
+ if (!existingPackages.StandardOutput.Contains($" > {x.Name} "))
+ {
+ return;
+ }
+
+ await context.DotNet().Remove.Package(new DotNetRemovePackageOptions(testProject, x.Name), cancellationToken);
+ }
}
\ No newline at end of file
diff --git a/TUnit.Pipeline/Modules/CleanProjectsModule.cs b/TUnit.Pipeline/Modules/CleanProjectsModule.cs
new file mode 100644
index 0000000000..370a5fdd9d
--- /dev/null
+++ b/TUnit.Pipeline/Modules/CleanProjectsModule.cs
@@ -0,0 +1,29 @@
+using EnumerableAsyncProcessor.Extensions;
+using ModularPipelines.Context;
+using ModularPipelines.DotNet.Extensions;
+using ModularPipelines.DotNet.Options;
+using ModularPipelines.Models;
+using ModularPipelines.Modules;
+using ModularPipelines.Attributes;
+
+namespace TUnit.Pipeline.Modules;
+[DependsOn]
+public class CleanProjectsModule : Module
+{
+ protected override async Task ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
+ {
+ var projects = await GetModule();
+ return await projects.Value!.SelectAsync(x => context.DotNet().Clean(new DotNetCleanOptions(x), cancellationToken), cancellationToken: cancellationToken).ProcessOneAtATime();
+ }
+}
+
+[DependsOn]
+[DependsOn]
+public class BuildProjectsModule : Module
+{
+ protected override async Task ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
+ {
+ var projects = await GetModule();
+ return await projects.Value!.SelectAsync(x => context.DotNet().Build(new DotNetBuildOptions(x), cancellationToken), cancellationToken: cancellationToken).ProcessOneAtATime();
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Pipeline/Modules/MoveNuGetPackagesToLocalSourceModule.cs b/TUnit.Pipeline/Modules/MoveNuGetPackagesToLocalSourceModule.cs
index bbffe9f141..abfc7cb521 100644
--- a/TUnit.Pipeline/Modules/MoveNuGetPackagesToLocalSourceModule.cs
+++ b/TUnit.Pipeline/Modules/MoveNuGetPackagesToLocalSourceModule.cs
@@ -8,12 +8,18 @@
namespace TUnit.Pipeline.Modules;
[DependsOn]
+[DependsOn]
public class MoveNuGetPackagesToLocalSourceModule : Module>
{
protected override async Task?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
{
var localNugetDirectory = await GetModule();
+ foreach (var file in localNugetDirectory.Value!.ListFiles().Where(x => x.Name.Contains("TUnit")))
+ {
+ file.Delete();
+ }
+
var nugetPackages = context.Git().RootDirectory
.GetFiles(x => x.Extension is ".nupkg" or ".snupkg");
diff --git a/TUnit.Pipeline/Modules/PackTUnitFilesModule.cs b/TUnit.Pipeline/Modules/PackTUnitFilesModule.cs
index 26395b55f3..d7ecb1d172 100644
--- a/TUnit.Pipeline/Modules/PackTUnitFilesModule.cs
+++ b/TUnit.Pipeline/Modules/PackTUnitFilesModule.cs
@@ -8,6 +8,7 @@
namespace TUnit.Pipeline.Modules;
[DependsOn]
+[DependsOn]
public class PackTUnitFilesModule : Module>
{
protected override async Task?> ExecuteAsync(IPipelineContext context, CancellationToken cancellationToken)
@@ -18,10 +19,17 @@ public class PackTUnitFilesModule : Module>
var version = $"0.0.1-alpha{guid}";
var packedProjects = await projects.Value!.SelectAsync(async project =>
- {
- return await context.DotNet().Pack(new DotNetPackOptions(project) { Properties = new[] { new KeyValue("Version", version), new KeyValue("PackageVersion", version) } }, cancellationToken);
- }, cancellationToken: cancellationToken).ProcessOneAtATime();
+ {
+ return await context.DotNet()
+ .Pack(
+ new DotNetPackOptions(project)
+ {
+ Properties = new[]
+ { new KeyValue("Version", version), new KeyValue("PackageVersion", version) }
+ }, cancellationToken);
+ }, cancellationToken: cancellationToken)
+ .ProcessOneAtATime();
- return projects.Value!.Select(x => new PackedProject(x.Name, version)).ToList();
+ return projects.Value!.Select(x => new PackedProject(x.NameWithoutExtension, version)).ToList();
}
}
\ No newline at end of file
diff --git a/TUnit.TestAdapter/TUnit.TestAdapter.csproj b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
index e01dcca5d4..fa55d26432 100644
--- a/TUnit.TestAdapter/TUnit.TestAdapter.csproj
+++ b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
@@ -5,17 +5,22 @@
enable
enable
default
+ true
Library
- true
-
-
+
-
+
+
+
+
+
+
+
diff --git a/TUnit.TestProject/TUnit.TestProject.csproj b/TUnit.TestProject/TUnit.TestProject.csproj
index d89af0898a..1c10300534 100644
--- a/TUnit.TestProject/TUnit.TestProject.csproj
+++ b/TUnit.TestProject/TUnit.TestProject.csproj
@@ -11,7 +11,11 @@
-
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
diff --git a/TUnit.TestProject/Tests.cs b/TUnit.TestProject/Tests.cs
index 02465296fa..fcb3f98e50 100644
--- a/TUnit.TestProject/Tests.cs
+++ b/TUnit.TestProject/Tests.cs
@@ -1,3 +1,4 @@
+using TUnit.Assertions;
using TUnit.Core.Attributes;
namespace TUnit.TestProject;
@@ -12,6 +13,7 @@ public class Tests
[Test]
public void Test1()
{
-
+ var one = "1";
+ Assert.That(one, Is.EqualTo("1"));
}
}
\ No newline at end of file
diff --git a/TUnit/TUnit.csproj b/TUnit/TUnit.csproj
index d75a0cf9c9..73ce60e529 100644
--- a/TUnit/TUnit.csproj
+++ b/TUnit/TUnit.csproj
@@ -7,6 +7,7 @@
+
From 1222d7c4db372ada89d4cf791e30cdaccfd91e69 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 18:31:59 +0000
Subject: [PATCH 003/124] Fixes
---
.../Modules/AddReferencesToTestProject.cs | 2 +-
TUnit.Pipeline/TUnit.Pipeline.csproj | 4 +-
TUnit.TestAdapter/AssemblyLoader.cs | 3 +-
TUnit.TestAdapter/AsyncTestExecutor.cs | 54 +++++++----
.../ReflectionMetadataProvider.cs | 88 +++++++++++++++++
TUnit.TestAdapter/SourceLocationHelper.cs | 95 +++++++++++++++++--
TUnit.TestAdapter/TUnit.TestAdapter.csproj | 18 +++-
TUnit.TestAdapter/TUnit.TestAdapter.props | 10 ++
TUnit.TestAdapter/TestCollector.cs | 1 +
TUnit.TestAdapter/TestDiscoverer.cs | 8 +-
TUnit.TestAdapter/TestExecutor.cs | 1 +
TUnit.TestAdapter/TestsLoader.cs | 5 +-
TUnit.TestProject/TUnit.TestProject.csproj | 10 +-
TUnit.TestProject/Tests.cs | 23 +++++
14 files changed, 281 insertions(+), 41 deletions(-)
create mode 100644 TUnit.TestAdapter/ReflectionMetadataProvider.cs
create mode 100644 TUnit.TestAdapter/TUnit.TestAdapter.props
diff --git a/TUnit.Pipeline/Modules/AddReferencesToTestProject.cs b/TUnit.Pipeline/Modules/AddReferencesToTestProject.cs
index 303a46f414..da7f0da703 100644
--- a/TUnit.Pipeline/Modules/AddReferencesToTestProject.cs
+++ b/TUnit.Pipeline/Modules/AddReferencesToTestProject.cs
@@ -32,7 +32,7 @@ await projects.Value!
.SelectAsync(async x => await context.DotNet().Add.Package(new DotNetAddPackageOptions(testProject, x.Name)
{
Version = x.Version,
- Source = localNugetDirectory.Value!
+ Source = localNugetDirectory.Value!,
}, cancellationToken), cancellationToken: cancellationToken).ProcessOneAtATime();
}
diff --git a/TUnit.Pipeline/TUnit.Pipeline.csproj b/TUnit.Pipeline/TUnit.Pipeline.csproj
index 3590d330fc..7579ccbe99 100644
--- a/TUnit.Pipeline/TUnit.Pipeline.csproj
+++ b/TUnit.Pipeline/TUnit.Pipeline.csproj
@@ -9,8 +9,8 @@
-
-
+
+
diff --git a/TUnit.TestAdapter/AssemblyLoader.cs b/TUnit.TestAdapter/AssemblyLoader.cs
index b4bdeaf457..529d0d08af 100644
--- a/TUnit.TestAdapter/AssemblyLoader.cs
+++ b/TUnit.TestAdapter/AssemblyLoader.cs
@@ -1,4 +1,5 @@
using System.Reflection;
+using System.Runtime.Loader;
namespace TUnit.TestAdapter;
@@ -13,7 +14,7 @@ internal class AssemblyLoader
try
{
- return Assembly.LoadFrom(assemblyPath);
+ return AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
}
catch
{
diff --git a/TUnit.TestAdapter/AsyncTestExecutor.cs b/TUnit.TestAdapter/AsyncTestExecutor.cs
index 12455e55c4..cc8bbd3e7e 100644
--- a/TUnit.TestAdapter/AsyncTestExecutor.cs
+++ b/TUnit.TestAdapter/AsyncTestExecutor.cs
@@ -36,6 +36,11 @@ public async Task RunInAsyncContext(IEnumerable tests, IRunContext? runCon
await foreach (var testCase in ProcessQueue(queue, frameworkHandle))
{
+ if (cancellationTokenSource.IsCancellationRequested)
+ {
+ break;
+ }
+
executingTests.Add(testCase);
SetupRunOneTimeTearDownForClass(testCase, allTestsOrderedByClass, executingTests);
@@ -79,7 +84,7 @@ private async IAsyncEnumerable ProcessQueue(Queue queue, I
{
while (queue.Count > 0)
{
- if (_canRunAnotherTest)
+ if (_canRunAnotherTest && !cancellationTokenSource.IsCancellationRequested)
{
var test = queue.Dequeue();
@@ -87,6 +92,10 @@ private async IAsyncEnumerable ProcessQueue(Queue queue, I
yield return new ProcessingTest(test, @class, ProcessTest(test, @class, frameworkHandle));
}
+ else if (cancellationTokenSource.IsCancellationRequested)
+ {
+ break;
+ }
else
{
await Task.Delay(100);
@@ -159,12 +168,7 @@ private async Task ExecuteTest(Test test, object @class)
{
await ExecuteSetUps(@class);
- var methodExecutionObject = test.MethodInfo.Invoke(@class, test.Arguments);
-
- if (methodExecutionObject is Task task)
- {
- await task;
- }
+ await InvokeMethod(@class, test.MethodInfo, test.Arguments);
await ExecuteTearDowns(@class);
}
@@ -180,12 +184,7 @@ private async Task ExecuteSetUps(object @class)
foreach (var setUpMethod in setUpMethods)
{
- var result = setUpMethod.Invoke(@class, null);
-
- if (result is Task task)
- {
- await task;
- }
+ await InvokeMethod(@class, setUpMethod, null);
}
}
@@ -202,12 +201,7 @@ private async Task ExecuteTearDowns(object @class)
{
try
{
- var result = tearDownMethod.Invoke(@class, null);
-
- if (result is Task task)
- {
- await task;
- }
+ await InvokeMethod(@class, tearDownMethod, null);
}
catch (Exception e)
{
@@ -291,4 +285,26 @@ private async Task GetCpuUsageForProcess()
return cpuUsageTotal * 100;
}
+
+ public async Task InvokeMethod(object @class, MethodInfo methodInfo, object?[]? arguments)
+ {
+ try
+ {
+ var result = methodInfo.Invoke(@class, arguments);
+
+ if (result is Task task)
+ {
+ await task;
+ }
+ }
+ catch (TargetInvocationException e)
+ {
+ if (e.InnerException is null)
+ {
+ throw;
+ }
+
+ throw e.InnerException;
+ }
+ }
}
\ No newline at end of file
diff --git a/TUnit.TestAdapter/ReflectionMetadataProvider.cs b/TUnit.TestAdapter/ReflectionMetadataProvider.cs
new file mode 100644
index 0000000000..24fa4b2aa2
--- /dev/null
+++ b/TUnit.TestAdapter/ReflectionMetadataProvider.cs
@@ -0,0 +1,88 @@
+using System.Reflection;
+using System.Runtime.Loader;
+
+namespace TUnit.TestAdapter
+{
+ internal sealed class ReflectionMetadataProvider
+ {
+ public Type? GetDeclaringType(string assemblyPath, string reflectedTypeName, string methodName)
+ {
+ var type = TryGetSingleMethod(assemblyPath, reflectedTypeName, methodName)?.DeclaringType;
+ if (type == null)
+ {
+ return null;
+ }
+
+ if (type.IsConstructedGenericType)
+ {
+ type = type.GetGenericTypeDefinition();
+ }
+
+ return type;
+ }
+
+ public Type? GetStateMachineType(string assemblyPath, string reflectedTypeName, string methodName)
+ {
+ var method = TryGetSingleMethod(assemblyPath, reflectedTypeName, methodName);
+ if (method == null)
+ {
+ return null;
+ }
+
+ var candidate = null as Type;
+
+ foreach (var attributeData in CustomAttributeData.GetCustomAttributes(method))
+ {
+ for (var current = attributeData.Constructor.DeclaringType; current != null; current = current.GetTypeInfo().BaseType)
+ {
+ if (current.FullName != "System.Runtime.CompilerServices.StateMachineAttribute")
+ {
+ continue;
+ }
+
+ var parameters = attributeData.Constructor.GetParameters();
+ for (var i = 0; i < parameters.Length; i++)
+ {
+ if (parameters[i].Name != "stateMachineType")
+ {
+ continue;
+ }
+
+ if (attributeData.ConstructorArguments[i].Value is Type argument)
+ {
+ if (candidate != null)
+ {
+ return null;
+ }
+
+ candidate = argument;
+ }
+ }
+ }
+ }
+
+ return candidate;
+ }
+
+ private static MethodInfo? TryGetSingleMethod(string assemblyPath, string reflectedTypeName, string methodName)
+ {
+ try
+ {
+ var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
+
+ var type = assembly.GetType(reflectedTypeName, throwOnError: false);
+
+ var methods = type?.GetMethods().Where(m => m.Name == methodName).Take(2).ToList();
+ return methods?.Count == 1 ? methods[0] : null;
+ }
+ catch (FileNotFoundException)
+ {
+ return null;
+ }
+ }
+
+ public void Dispose()
+ {
+ }
+ }
+}
diff --git a/TUnit.TestAdapter/SourceLocationHelper.cs b/TUnit.TestAdapter/SourceLocationHelper.cs
index 46e8bb4a01..1bdb79429b 100644
--- a/TUnit.TestAdapter/SourceLocationHelper.cs
+++ b/TUnit.TestAdapter/SourceLocationHelper.cs
@@ -1,32 +1,40 @@
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using System.Reflection;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using TUnit.Core;
namespace TUnit.TestAdapter;
-public class SourceLocationHelper
+public class SourceLocationHelper : IDisposable
{
private readonly IMessageLogger? _logger;
private static readonly SourceLocation EmptySourceLocation = new(null, 0, 0);
+ private readonly ReflectionMetadataProvider _metadataProvider;
+
+ private readonly Dictionary _sessionsByAssemblyPath = new (StringComparer.OrdinalIgnoreCase);
public SourceLocationHelper(IMessageLogger? logger)
{
_logger = logger;
+ _metadataProvider = new ReflectionMetadataProvider();
}
- public SourceLocation GetSourceLocation(DiaSession diaSession, string className, string methodName)
+ public SourceLocation GetSourceLocation(string assemblyLocation, string className, string methodName)
{
try
{
- var navigationData = diaSession.GetNavigationDataForMethod(className, methodName);
+ var navigationData = TryGetNavigationData(assemblyLocation, className, methodName);
if (navigationData is null)
{
_logger?.SendMessage(TestMessageLevel.Error, $"No navigation data found for {className}.{methodName}");
-
+ _logger?.SendMessage(TestMessageLevel.Error, $"Assembly: {assemblyLocation}");
+
return EmptySourceLocation;
}
-
+
+ _logger?.SendMessage(TestMessageLevel.Informational, $"Navigation data found for {className}.{methodName}");
+
return new SourceLocation(navigationData.FileName, navigationData.MinLineNumber, navigationData.MaxLineNumber);
}
catch (Exception e)
@@ -37,4 +45,79 @@ public SourceLocation GetSourceLocation(DiaSession diaSession, string className,
return EmptySourceLocation;
}
}
+
+ private SourceLocation? TryGetNavigationData(string assemblyLocation, string className, string methodName)
+ {
+ var sessionData = TryGetSessionData(assemblyLocation, className, methodName);
+
+ if (sessionData != null)
+ {
+ return sessionData;
+ }
+
+ var stateMachine =
+ _metadataProvider.GetStateMachineType(assemblyLocation, className, methodName);
+
+ if (stateMachine != null)
+ {
+ sessionData = TryGetSessionData(stateMachine.Assembly.Location, stateMachine.FullName!, "MoveNext");
+
+ if (sessionData != null)
+ {
+ return sessionData;
+ }
+ }
+
+ var declaringType2 =
+ _metadataProvider.GetStateMachineType(assemblyLocation, className, methodName);
+
+ if (declaringType2 != null)
+ {
+ sessionData = TryGetSessionData(declaringType2.Assembly.Location, declaringType2.FullName!, methodName);
+
+ if (sessionData != null)
+ {
+ return sessionData;
+ }
+ }
+
+ return null;
+ }
+
+ private Type? DoWithBreaker(string assemblyLocation, Func method, string declaringTypeName, string methodName)
+ {
+ try
+ {
+ return method.Invoke(assemblyLocation, declaringTypeName, methodName);
+ }
+ catch
+ {
+ // Ignored
+ }
+
+ return null;
+ }
+
+ private SourceLocation? TryGetSessionData(string assemblyPath, string declaringTypeFullName, string methodName)
+ {
+ if (!_sessionsByAssemblyPath.TryGetValue(assemblyPath, out var session))
+ {
+ session = new DiaSession(assemblyPath);
+ _sessionsByAssemblyPath.Add(assemblyPath, session);
+ }
+
+ var data = session.GetNavigationData(declaringTypeFullName, methodName);
+
+ return string.IsNullOrEmpty(data?.FileName)
+ ? null
+ : new SourceLocation(data.FileName, data.MinLineNumber, data.MaxLineNumber);
+ }
+
+ public void Dispose()
+ {
+ foreach (var diaSession in _sessionsByAssemblyPath.Values)
+ {
+ diaSession.Dispose();
+ }
+ }
}
\ No newline at end of file
diff --git a/TUnit.TestAdapter/TUnit.TestAdapter.csproj b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
index fa55d26432..de159ad477 100644
--- a/TUnit.TestAdapter/TUnit.TestAdapter.csproj
+++ b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
@@ -17,10 +17,24 @@
-
+
+ $(TargetsForTfmSpecificContentInPackage);_AddToOutput
+
+
+
-
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
diff --git a/TUnit.TestAdapter/TUnit.TestAdapter.props b/TUnit.TestAdapter/TUnit.TestAdapter.props
new file mode 100644
index 0000000000..815f953031
--- /dev/null
+++ b/TUnit.TestAdapter/TUnit.TestAdapter.props
@@ -0,0 +1,10 @@
+
+
+
+
+ TUnit.TestAdapter.dll
+ PreserveNewest
+ true
+
+
+
\ No newline at end of file
diff --git a/TUnit.TestAdapter/TestCollector.cs b/TUnit.TestAdapter/TestCollector.cs
index dcadf7c83c..ba26cb590c 100644
--- a/TUnit.TestAdapter/TestCollector.cs
+++ b/TUnit.TestAdapter/TestCollector.cs
@@ -26,6 +26,7 @@ public IEnumerable TestsFromSources(IEnumerable sources)
var testsLoader = new TestsLoader(_messageLogger);
var tests = sources
+ .Select(source => Path.IsPathRooted(source) ? source : Path.Combine(Directory.GetCurrentDirectory(), source))
.Select(assemblyLoader.LoadByPath)
.OfType()
.Select(x => new TypeInformation(x))
diff --git a/TUnit.TestAdapter/TestDiscoverer.cs b/TUnit.TestAdapter/TestDiscoverer.cs
index cebbe8170c..e68c77a891 100644
--- a/TUnit.TestAdapter/TestDiscoverer.cs
+++ b/TUnit.TestAdapter/TestDiscoverer.cs
@@ -1,4 +1,5 @@
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using System.Diagnostics;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using TUnit.TestAdapter.Constants;
@@ -17,6 +18,11 @@ public void DiscoverTests(IEnumerable sources,
IMessageLogger logger,
ITestCaseDiscoverySink discoverySink)
{
+ if (!Debugger.IsAttached)
+ {
+ Debugger.Launch();
+ }
+
var testCollector = new TestCollector(logger);
foreach (var test in testCollector.TestsFromSources(sources))
diff --git a/TUnit.TestAdapter/TestExecutor.cs b/TUnit.TestAdapter/TestExecutor.cs
index 9bb266b7c8..6c4870509b 100644
--- a/TUnit.TestAdapter/TestExecutor.cs
+++ b/TUnit.TestAdapter/TestExecutor.cs
@@ -15,6 +15,7 @@ public class TestExecutor : ITestExecutor2
public TestExecutor()
{
+ Console.CancelKeyPress += (_, _) => _cancellationTokenSource.Cancel();
_asyncTestExecutor = new AsyncTestExecutor(_cancellationTokenSource);
}
diff --git a/TUnit.TestAdapter/TestsLoader.cs b/TUnit.TestAdapter/TestsLoader.cs
index 0a3af6f7f6..56df6f5b98 100644
--- a/TUnit.TestAdapter/TestsLoader.cs
+++ b/TUnit.TestAdapter/TestsLoader.cs
@@ -1,5 +1,4 @@
using System.Reflection;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using TUnit.Core;
using TUnit.Core.Attributes;
@@ -18,8 +17,6 @@ public TestsLoader(IMessageLogger? messageLogger)
public IEnumerable GetTests(TypeInformation typeInformation)
{
- using var diaSession = new DiaSession(typeInformation.Assembly.Location);
-
var methods = typeInformation.Types.SelectMany(x => x.GetMethods());
foreach (var methodInfo in methods)
@@ -30,7 +27,7 @@ public IEnumerable GetTests(TypeInformation typeInformation)
}
var sourceLocation = _sourceLocationHelper
- .GetSourceLocation(diaSession, methodInfo.DeclaringType!.Name, methodInfo.Name);
+ .GetSourceLocation(typeInformation.Assembly.Location, methodInfo.DeclaringType!.FullName!, methodInfo.Name);
foreach (var testWithDataAttribute in methodInfo.CustomAttributes.Where(x => x.AttributeType == typeof(TestWithDataAttribute)))
{
diff --git a/TUnit.TestProject/TUnit.TestProject.csproj b/TUnit.TestProject/TUnit.TestProject.csproj
index 1c10300534..82ce5f0830 100644
--- a/TUnit.TestProject/TUnit.TestProject.csproj
+++ b/TUnit.TestProject/TUnit.TestProject.csproj
@@ -4,7 +4,6 @@
net6.0
enable
enable
-
false
true
@@ -12,13 +11,14 @@
-
- all
- runtime; build; native; contentfiles; analyzers
-
+
+
+
+
+
diff --git a/TUnit.TestProject/Tests.cs b/TUnit.TestProject/Tests.cs
index fcb3f98e50..51d7657552 100644
--- a/TUnit.TestProject/Tests.cs
+++ b/TUnit.TestProject/Tests.cs
@@ -16,4 +16,27 @@ public void Test1()
var one = "1";
Assert.That(one, Is.EqualTo("1"));
}
+
+ [Test]
+ public void Test2()
+ {
+ var one = "2";
+ Assert.That(one, Is.EqualTo("1"));
+ }
+
+ [Test]
+ public async Task Test3()
+ {
+ await Task.Yield();
+ var one = "1";
+ Assert.That(one, Is.EqualTo("1"));
+ }
+
+ [Test]
+ public async Task Test4()
+ {
+ await Task.Yield();
+ var one = "2";
+ Assert.That(one, Is.EqualTo("1"));
+ }
}
\ No newline at end of file
From ff1adb3b712d537bfcde5cab5b3c542f81dc6a71 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 18:41:18 +0000
Subject: [PATCH 004/124] Fix
---
TUnit.TestAdapter/AsyncTestExecutor.cs | 21 +++++++++------------
TUnit.TestAdapter/SourceLocationHelper.cs | 4 +---
2 files changed, 10 insertions(+), 15 deletions(-)
diff --git a/TUnit.TestAdapter/AsyncTestExecutor.cs b/TUnit.TestAdapter/AsyncTestExecutor.cs
index cc8bbd3e7e..fad46e1213 100644
--- a/TUnit.TestAdapter/AsyncTestExecutor.cs
+++ b/TUnit.TestAdapter/AsyncTestExecutor.cs
@@ -1,6 +1,8 @@
using System.Collections.Concurrent;
using System.Diagnostics;
+using System.Globalization;
using System.Reflection;
+using System.Runtime.ExceptionServices;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using TUnit.Core;
@@ -168,7 +170,7 @@ private async Task ExecuteTest(Test test, object @class)
{
await ExecuteSetUps(@class);
- await InvokeMethod(@class, test.MethodInfo, test.Arguments);
+ await InvokeMethod(@class, test.MethodInfo, BindingFlags.Default, test.Arguments);
await ExecuteTearDowns(@class);
}
@@ -184,7 +186,7 @@ private async Task ExecuteSetUps(object @class)
foreach (var setUpMethod in setUpMethods)
{
- await InvokeMethod(@class, setUpMethod, null);
+ await InvokeMethod(@class, setUpMethod, BindingFlags.Default, null);
}
}
@@ -201,7 +203,7 @@ private async Task ExecuteTearDowns(object @class)
{
try
{
- await InvokeMethod(@class, tearDownMethod, null);
+ await InvokeMethod(@class, tearDownMethod, BindingFlags.Default, null);
}
catch (Exception e)
{
@@ -224,12 +226,7 @@ private async Task ExecuteOneTimeSetUps(object @class)
foreach (var oneTimeSetUpMethod in oneTimeSetUpMethods)
{
- var result = oneTimeSetUpMethod.Invoke(null, BindingFlags.Static | BindingFlags.Public, null, null, null);
-
- if (result is Task task)
- {
- await task;
- }
+ await InvokeMethod(@class, oneTimeSetUpMethod, BindingFlags.Static | BindingFlags.Public, null);
}
}
@@ -286,11 +283,11 @@ private async Task GetCpuUsageForProcess()
return cpuUsageTotal * 100;
}
- public async Task InvokeMethod(object @class, MethodInfo methodInfo, object?[]? arguments)
+ public async Task InvokeMethod(object @class, MethodInfo methodInfo, BindingFlags bindingFlags, object?[]? arguments)
{
try
{
- var result = methodInfo.Invoke(@class, arguments);
+ var result = methodInfo.Invoke(@class, bindingFlags, null, arguments, CultureInfo.InvariantCulture);
if (result is Task task)
{
@@ -304,7 +301,7 @@ public async Task InvokeMethod(object @class, MethodInfo methodInfo, object?[]?
throw;
}
- throw e.InnerException;
+ ExceptionDispatchInfo.Capture(e.InnerException).Throw();
}
}
}
\ No newline at end of file
diff --git a/TUnit.TestAdapter/SourceLocationHelper.cs b/TUnit.TestAdapter/SourceLocationHelper.cs
index 1bdb79429b..31c07c271a 100644
--- a/TUnit.TestAdapter/SourceLocationHelper.cs
+++ b/TUnit.TestAdapter/SourceLocationHelper.cs
@@ -32,9 +32,7 @@ public SourceLocation GetSourceLocation(string assemblyLocation, string classNam
return EmptySourceLocation;
}
-
- _logger?.SendMessage(TestMessageLevel.Informational, $"Navigation data found for {className}.{methodName}");
-
+
return new SourceLocation(navigationData.FileName, navigationData.MinLineNumber, navigationData.MaxLineNumber);
}
catch (Exception e)
From e0256ed7061cc280de51696efa547bc7c0b9049a Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 19:23:42 +0000
Subject: [PATCH 005/124] Fix
---
TUnit.TestAdapter/TUnit.TestAdapter.csproj | 4 ++--
TUnit.TestAdapter/TUnit.TestAdapter.props | 10 ----------
TUnit.TestAdapter/TUnit.TestAdapter.targets | 9 +++++++++
TUnit.TestProject/TUnit.TestProject.csproj | 9 ++++-----
TUnit/TUnit.csproj | 1 -
5 files changed, 15 insertions(+), 18 deletions(-)
delete mode 100644 TUnit.TestAdapter/TUnit.TestAdapter.props
create mode 100644 TUnit.TestAdapter/TUnit.TestAdapter.targets
diff --git a/TUnit.TestAdapter/TUnit.TestAdapter.csproj b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
index de159ad477..2885b31d71 100644
--- a/TUnit.TestAdapter/TUnit.TestAdapter.csproj
+++ b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
@@ -23,7 +23,7 @@
-
+
@@ -32,7 +32,7 @@
-
+
PreserveNewest
diff --git a/TUnit.TestAdapter/TUnit.TestAdapter.props b/TUnit.TestAdapter/TUnit.TestAdapter.props
deleted file mode 100644
index 815f953031..0000000000
--- a/TUnit.TestAdapter/TUnit.TestAdapter.props
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
- TUnit.TestAdapter.dll
- PreserveNewest
- true
-
-
-
\ No newline at end of file
diff --git a/TUnit.TestAdapter/TUnit.TestAdapter.targets b/TUnit.TestAdapter/TUnit.TestAdapter.targets
new file mode 100644
index 0000000000..3f46f4825c
--- /dev/null
+++ b/TUnit.TestAdapter/TUnit.TestAdapter.targets
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TUnit.TestProject/TUnit.TestProject.csproj b/TUnit.TestProject/TUnit.TestProject.csproj
index 82ce5f0830..9fe0f45764 100644
--- a/TUnit.TestProject/TUnit.TestProject.csproj
+++ b/TUnit.TestProject/TUnit.TestProject.csproj
@@ -11,14 +11,13 @@
-
-
-
-
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
-
diff --git a/TUnit/TUnit.csproj b/TUnit/TUnit.csproj
index 73ce60e529..45f6ff7b23 100644
--- a/TUnit/TUnit.csproj
+++ b/TUnit/TUnit.csproj
@@ -9,7 +9,6 @@
-
From 4ba0d5c6c1986db882d50b550e95ce42721cdb86 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 19:30:19 +0000
Subject: [PATCH 006/124] Redundant
---
TUnit.TestAdapter/TUnit.TestAdapter.csproj | 16 ----------------
TUnit.TestAdapter/TUnit.TestAdapter.targets | 9 ---------
TUnit.TestProject/TUnit.TestProject.csproj | 6 +++---
3 files changed, 3 insertions(+), 28 deletions(-)
delete mode 100644 TUnit.TestAdapter/TUnit.TestAdapter.targets
diff --git a/TUnit.TestAdapter/TUnit.TestAdapter.csproj b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
index 2885b31d71..794c4b1b65 100644
--- a/TUnit.TestAdapter/TUnit.TestAdapter.csproj
+++ b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
@@ -17,24 +17,8 @@
-
- $(TargetsForTfmSpecificContentInPackage);_AddToOutput
-
-
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
diff --git a/TUnit.TestAdapter/TUnit.TestAdapter.targets b/TUnit.TestAdapter/TUnit.TestAdapter.targets
deleted file mode 100644
index 3f46f4825c..0000000000
--- a/TUnit.TestAdapter/TUnit.TestAdapter.targets
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/TUnit.TestProject/TUnit.TestProject.csproj b/TUnit.TestProject/TUnit.TestProject.csproj
index 9fe0f45764..07556ec8e6 100644
--- a/TUnit.TestProject/TUnit.TestProject.csproj
+++ b/TUnit.TestProject/TUnit.TestProject.csproj
@@ -11,9 +11,9 @@
-
- all
- runtime; build; native; contentfiles; analyzers
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
From 67a94ff9d43e5a8a5559dd04b87d230d4f8a9c22 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 19:36:54 +0000
Subject: [PATCH 007/124] Remove debugger call
---
TUnit.TestAdapter/TestDiscoverer.cs | 5 -----
1 file changed, 5 deletions(-)
diff --git a/TUnit.TestAdapter/TestDiscoverer.cs b/TUnit.TestAdapter/TestDiscoverer.cs
index e68c77a891..00aa9446c8 100644
--- a/TUnit.TestAdapter/TestDiscoverer.cs
+++ b/TUnit.TestAdapter/TestDiscoverer.cs
@@ -18,11 +18,6 @@ public void DiscoverTests(IEnumerable sources,
IMessageLogger logger,
ITestCaseDiscoverySink discoverySink)
{
- if (!Debugger.IsAttached)
- {
- Debugger.Launch();
- }
-
var testCollector = new TestCollector(logger);
foreach (var test in testCollector.TestsFromSources(sources))
From 94ffc9d072d66254b536ef158ac5d3e1ea8df3d5 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 20:24:17 +0000
Subject: [PATCH 008/124] Fix
---
TUnit.Core/ParameterArgument.cs | 3 ++
TUnit.Core/Test.cs | 14 ++++----
TUnit.TestAdapter/AsyncTestExecutor.cs | 2 +-
.../Extensions/TestExtensions.cs | 15 ++++++--
TUnit.TestAdapter/SourceLocationHelper.cs | 36 ++++---------------
TUnit.TestAdapter/TestCollector.cs | 11 ++----
TUnit.TestAdapter/TestDiscoverer.cs | 2 +-
TUnit.TestAdapter/TestsLoader.cs | 11 ++----
TUnit.TestProject/TUnit.TestProject.csproj | 2 +-
9 files changed, 38 insertions(+), 58 deletions(-)
create mode 100644 TUnit.Core/ParameterArgument.cs
diff --git a/TUnit.Core/ParameterArgument.cs b/TUnit.Core/ParameterArgument.cs
new file mode 100644
index 0000000000..32b782aba1
--- /dev/null
+++ b/TUnit.Core/ParameterArgument.cs
@@ -0,0 +1,3 @@
+namespace TUnit.Core;
+
+public record ParameterArgument(Type Type, object? Value);
\ No newline at end of file
diff --git a/TUnit.Core/Test.cs b/TUnit.Core/Test.cs
index acf720e459..718a28b08b 100644
--- a/TUnit.Core/Test.cs
+++ b/TUnit.Core/Test.cs
@@ -7,7 +7,7 @@ public record Test
{
public Test(MethodInfo MethodInfo,
SourceLocation SourceLocation,
- object?[]? Arguments)
+ ParameterArgument[]? Arguments)
{
var classType = MethodInfo.DeclaringType!;
@@ -20,7 +20,7 @@ public Test(MethodInfo MethodInfo,
FullyQualifiedClassName = classType.FullName!;
Assembly = classType.Assembly;
Source = classType.Assembly.Location;
- FullName = $"{classType.FullName}.{MethodInfo.Name}{GetArguments(Arguments)}";
+ FullyQualifiedName = $"{classType.FullName}.{MethodInfo.Name}{GetParameterTypes(Arguments)}";
IsSkipped = MethodInfo.CustomAttributes
.Concat(classType.CustomAttributes)
.Any(x => x.AttributeType == typeof(SkipAttribute));
@@ -41,12 +41,12 @@ public Test(MethodInfo MethodInfo,
public Assembly Assembly { get; }
public string Source { get; }
- public string FullName { get; }
+ public string FullyQualifiedName { get; }
public MethodInfo MethodInfo { get; init; }
public string? FileName { get; set; }
public int MinLineNumber { get; set; }
public int MaxLineNumber { get; set; }
- public object?[]? Arguments { get; init; }
+ public ParameterArgument[]? Arguments { get; init; }
public SourceLocation SourceLocation { get; }
public bool IsSkipped { get; }
@@ -57,16 +57,16 @@ public void Deconstruct(out MethodInfo methodInfo, out object?[]? arguments)
arguments = Arguments;
}
- private static string GetArguments(object?[]? arguments)
+ public static string GetParameterTypes(ParameterArgument[]? arguments)
{
if (arguments is null)
{
return string.Empty;
}
- var argsAsString = arguments.Select(StringifyArgument);
+ var argsAsString = arguments.Select(arg => arg.Type.FullName!);
- return $"({string.Join(", ", argsAsString)})";
+ return $"({string.Join(',', argsAsString)})";
}
private static string StringifyArgument(object? obj)
diff --git a/TUnit.TestAdapter/AsyncTestExecutor.cs b/TUnit.TestAdapter/AsyncTestExecutor.cs
index fad46e1213..5a1e581754 100644
--- a/TUnit.TestAdapter/AsyncTestExecutor.cs
+++ b/TUnit.TestAdapter/AsyncTestExecutor.cs
@@ -63,7 +63,7 @@ private void SetupRunOneTimeTearDownForClass(ProcessingTest processingTest,
var lastTestForClass = allTestsOrderedByClass.Last(x =>
x.FullyQualifiedClassName == processingTest.Test.FullyQualifiedClassName);
- if (processingTest.Test.FullName != lastTestForClass.FullName)
+ if (processingTest.Test.FullyQualifiedName != lastTestForClass.FullyQualifiedName)
{
return;
}
diff --git a/TUnit.TestAdapter/Extensions/TestExtensions.cs b/TUnit.TestAdapter/Extensions/TestExtensions.cs
index 6a2da9ac39..c07b4056bd 100644
--- a/TUnit.TestAdapter/Extensions/TestExtensions.cs
+++ b/TUnit.TestAdapter/Extensions/TestExtensions.cs
@@ -8,12 +8,23 @@ public static class TestExtensions
{
public static TestCase ToTestCase(this Test test)
{
- return new TestCase(test.FullName, TestAdapterConstants.ExecutorUri, test.Source)
+ var testCase = new TestCase(test.FullyQualifiedName, TestAdapterConstants.ExecutorUri, test.Source)
{
DisplayName = test.TestName,
Id = test.Id,
CodeFilePath = test.FileName,
- LineNumber = test.MinLineNumber
+ LineNumber = test.MinLineNumber,
};
+
+ testCase.SetPropertyValue(GetOrRegisterTestProperty("ManagedType"), test.FullyQualifiedClassName);
+ testCase.SetPropertyValue(GetOrRegisterTestProperty("ManagedMethod"), test.MethodInfo.Name + Test.GetParameterTypes(test.Arguments));
+
+ return testCase;
+ }
+
+ private static TestProperty GetOrRegisterTestProperty(string name)
+ {
+ return TestProperty.Find(name)
+ ?? TestProperty.Register(name, name, typeof(string), typeof(TestCase));
}
}
\ No newline at end of file
diff --git a/TUnit.TestAdapter/SourceLocationHelper.cs b/TUnit.TestAdapter/SourceLocationHelper.cs
index 31c07c271a..d9c16563d3 100644
--- a/TUnit.TestAdapter/SourceLocationHelper.cs
+++ b/TUnit.TestAdapter/SourceLocationHelper.cs
@@ -1,24 +1,16 @@
-using System.Reflection;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using TUnit.Core;
namespace TUnit.TestAdapter;
-public class SourceLocationHelper : IDisposable
+public class SourceLocationHelper(IMessageLogger? logger) : IDisposable
{
- private readonly IMessageLogger? _logger;
private static readonly SourceLocation EmptySourceLocation = new(null, 0, 0);
- private readonly ReflectionMetadataProvider _metadataProvider;
+ private readonly ReflectionMetadataProvider _metadataProvider = new();
private readonly Dictionary _sessionsByAssemblyPath = new (StringComparer.OrdinalIgnoreCase);
- public SourceLocationHelper(IMessageLogger? logger)
- {
- _logger = logger;
- _metadataProvider = new ReflectionMetadataProvider();
- }
-
public SourceLocation GetSourceLocation(string assemblyLocation, string className, string methodName)
{
try
@@ -27,8 +19,8 @@ public SourceLocation GetSourceLocation(string assemblyLocation, string classNam
if (navigationData is null)
{
- _logger?.SendMessage(TestMessageLevel.Error, $"No navigation data found for {className}.{methodName}");
- _logger?.SendMessage(TestMessageLevel.Error, $"Assembly: {assemblyLocation}");
+ logger?.SendMessage(TestMessageLevel.Error, $"No navigation data found for {className}.{methodName}");
+ logger?.SendMessage(TestMessageLevel.Error, $"Assembly: {assemblyLocation}");
return EmptySourceLocation;
}
@@ -37,8 +29,8 @@ public SourceLocation GetSourceLocation(string assemblyLocation, string classNam
}
catch (Exception e)
{
- _logger?.SendMessage(TestMessageLevel.Error, $"Error retrieving source location for {className}.{methodName}");
- _logger?.SendMessage(TestMessageLevel.Error, e.ToString());
+ logger?.SendMessage(TestMessageLevel.Error, $"Error retrieving source location for {className}.{methodName}");
+ logger?.SendMessage(TestMessageLevel.Error, e.ToString());
return EmptySourceLocation;
}
@@ -81,20 +73,6 @@ public SourceLocation GetSourceLocation(string assemblyLocation, string classNam
return null;
}
-
- private Type? DoWithBreaker(string assemblyLocation, Func method, string declaringTypeName, string methodName)
- {
- try
- {
- return method.Invoke(assemblyLocation, declaringTypeName, methodName);
- }
- catch
- {
- // Ignored
- }
-
- return null;
- }
private SourceLocation? TryGetSessionData(string assemblyPath, string declaringTypeFullName, string methodName)
{
diff --git a/TUnit.TestAdapter/TestCollector.cs b/TUnit.TestAdapter/TestCollector.cs
index ba26cb590c..1e8cda95be 100644
--- a/TUnit.TestAdapter/TestCollector.cs
+++ b/TUnit.TestAdapter/TestCollector.cs
@@ -4,15 +4,8 @@
namespace TUnit.TestAdapter;
-public class TestCollector
+public class TestCollector(IMessageLogger? messageLogger)
{
- private readonly IMessageLogger? _messageLogger;
-
- public TestCollector(IMessageLogger? messageLogger)
- {
- _messageLogger = messageLogger;
- }
-
public TestCollection CollectionFromSources(IEnumerable sources)
{
var sourcesAsList = sources.ToList();
@@ -23,7 +16,7 @@ public TestCollection CollectionFromSources(IEnumerable sources)
public IEnumerable TestsFromSources(IEnumerable sources)
{
var assemblyLoader = new AssemblyLoader();
- var testsLoader = new TestsLoader(_messageLogger);
+ var testsLoader = new TestsLoader(messageLogger);
var tests = sources
.Select(source => Path.IsPathRooted(source) ? source : Path.Combine(Directory.GetCurrentDirectory(), source))
diff --git a/TUnit.TestAdapter/TestDiscoverer.cs b/TUnit.TestAdapter/TestDiscoverer.cs
index 00aa9446c8..2e5b18a971 100644
--- a/TUnit.TestAdapter/TestDiscoverer.cs
+++ b/TUnit.TestAdapter/TestDiscoverer.cs
@@ -22,7 +22,7 @@ public void DiscoverTests(IEnumerable sources,
foreach (var test in testCollector.TestsFromSources(sources))
{
- logger.SendMessage(TestMessageLevel.Informational, "Test found: " + test.FullName);
+ logger.SendMessage(TestMessageLevel.Informational, "Test found: " + test.FullyQualifiedName);
discoverySink.SendTestCase(test.ToTestCase());
}
}
diff --git a/TUnit.TestAdapter/TestsLoader.cs b/TUnit.TestAdapter/TestsLoader.cs
index 56df6f5b98..44b013589b 100644
--- a/TUnit.TestAdapter/TestsLoader.cs
+++ b/TUnit.TestAdapter/TestsLoader.cs
@@ -5,15 +5,10 @@
namespace TUnit.TestAdapter;
-public class TestsLoader
+public class TestsLoader(IMessageLogger? messageLogger)
{
private static readonly Type[] TestAttributes = [typeof(TestAttribute), typeof(TestWithDataAttribute)];
- private readonly SourceLocationHelper _sourceLocationHelper;
-
- public TestsLoader(IMessageLogger? messageLogger)
- {
- _sourceLocationHelper = new SourceLocationHelper(messageLogger);
- }
+ private readonly SourceLocationHelper _sourceLocationHelper = new(messageLogger);
public IEnumerable GetTests(TypeInformation typeInformation)
{
@@ -31,7 +26,7 @@ public IEnumerable GetTests(TypeInformation typeInformation)
foreach (var testWithDataAttribute in methodInfo.CustomAttributes.Where(x => x.AttributeType == typeof(TestWithDataAttribute)))
{
- var arguments = testWithDataAttribute.ConstructorArguments.Select(x => x.Value);
+ var arguments = testWithDataAttribute.ConstructorArguments.Select(x => new ParameterArgument(x.ArgumentType, x.Value));
yield return new Test(
MethodInfo: methodInfo,
diff --git a/TUnit.TestProject/TUnit.TestProject.csproj b/TUnit.TestProject/TUnit.TestProject.csproj
index 07556ec8e6..b60af5d4e6 100644
--- a/TUnit.TestProject/TUnit.TestProject.csproj
+++ b/TUnit.TestProject/TUnit.TestProject.csproj
@@ -11,7 +11,7 @@
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
From 7065bf3ec38abb533ebda34a73a4c446e37f7c09 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 20:38:27 +0000
Subject: [PATCH 009/124] Fix
---
TUnit.TestAdapter/AsyncTestExecutor.cs | 4 +--
TUnit.TestAdapter/TUnit.TestAdapter.csproj | 1 +
TUnit.TestProject/TUnit.TestProject.csproj | 9 ++++---
TUnit.TestProject/Tests.cs | 31 ++++++++++++++++------
4 files changed, 31 insertions(+), 14 deletions(-)
diff --git a/TUnit.TestAdapter/AsyncTestExecutor.cs b/TUnit.TestAdapter/AsyncTestExecutor.cs
index 5a1e581754..4668e2177c 100644
--- a/TUnit.TestAdapter/AsyncTestExecutor.cs
+++ b/TUnit.TestAdapter/AsyncTestExecutor.cs
@@ -170,7 +170,7 @@ private async Task ExecuteTest(Test test, object @class)
{
await ExecuteSetUps(@class);
- await InvokeMethod(@class, test.MethodInfo, BindingFlags.Default, test.Arguments);
+ await InvokeMethod(@class, test.MethodInfo, BindingFlags.Default, test.Arguments?.Select(x => x.Value).ToArray());
await ExecuteTearDowns(@class);
}
@@ -283,7 +283,7 @@ private async Task GetCpuUsageForProcess()
return cpuUsageTotal * 100;
}
- public async Task InvokeMethod(object @class, MethodInfo methodInfo, BindingFlags bindingFlags, object?[]? arguments)
+ private async Task InvokeMethod(object @class, MethodInfo methodInfo, BindingFlags bindingFlags, object?[]? arguments)
{
try
{
diff --git a/TUnit.TestAdapter/TUnit.TestAdapter.csproj b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
index 794c4b1b65..785d3f930e 100644
--- a/TUnit.TestAdapter/TUnit.TestAdapter.csproj
+++ b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
@@ -10,6 +10,7 @@
+
diff --git a/TUnit.TestProject/TUnit.TestProject.csproj b/TUnit.TestProject/TUnit.TestProject.csproj
index b60af5d4e6..ecdf62100c 100644
--- a/TUnit.TestProject/TUnit.TestProject.csproj
+++ b/TUnit.TestProject/TUnit.TestProject.csproj
@@ -11,13 +11,14 @@
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
+
+
+
+
+
diff --git a/TUnit.TestProject/Tests.cs b/TUnit.TestProject/Tests.cs
index 51d7657552..6e6f21b708 100644
--- a/TUnit.TestProject/Tests.cs
+++ b/TUnit.TestProject/Tests.cs
@@ -13,30 +13,45 @@ public class Tests
[Test]
public void Test1()
{
- var one = "1";
- Assert.That(one, Is.EqualTo("1"));
+ var value = "1";
+ Assert.That(value, Is.EqualTo("1"));
}
[Test]
public void Test2()
{
- var one = "2";
- Assert.That(one, Is.EqualTo("1"));
+ var value = "2";
+ Assert.That(value, Is.EqualTo("1"));
}
[Test]
public async Task Test3()
{
await Task.Yield();
- var one = "1";
- Assert.That(one, Is.EqualTo("1"));
+ var value = "1";
+ Assert.That(value, Is.EqualTo("1"));
}
[Test]
public async Task Test4()
{
await Task.Yield();
- var one = "2";
- Assert.That(one, Is.EqualTo("1"));
+ var value = "2";
+ Assert.That(value, Is.EqualTo("1"));
+ }
+
+ [TestWithData("1")]
+ [TestWithData("2")]
+ public void ParameterisedTests1(string value)
+ {
+ Assert.That(value, Is.EqualTo("1"));
+ }
+
+ [TestWithData("1")]
+ [TestWithData("2")]
+ public async Task ParameterisedTests2(string value)
+ {
+ await Task.Yield();
+ Assert.That(value, Is.EqualTo("1"));
}
}
\ No newline at end of file
From b92d1261333fb0e4bd3b5dd96c77264a5bb5af99 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 20:43:35 +0000
Subject: [PATCH 010/124] Tweak
---
TUnit.Core/Test.cs | 23 ++++++++-----------
TUnit.TestAdapter/AsyncTestExecutor.cs | 2 +-
.../Extensions/TestExtensions.cs | 2 +-
TUnit.TestAdapter/TestsLoader.cs | 4 ++--
4 files changed, 14 insertions(+), 17 deletions(-)
diff --git a/TUnit.Core/Test.cs b/TUnit.Core/Test.cs
index 718a28b08b..60b196ed6c 100644
--- a/TUnit.Core/Test.cs
+++ b/TUnit.Core/Test.cs
@@ -7,20 +7,22 @@ public record Test
{
public Test(MethodInfo MethodInfo,
SourceLocation SourceLocation,
- ParameterArgument[]? Arguments)
+ ParameterArgument[]? arguments)
{
var classType = MethodInfo.DeclaringType!;
this.MethodInfo = MethodInfo;
- this.Arguments = Arguments;
this.SourceLocation = SourceLocation;
+ ParameterTypes = arguments?.Select(x => x.Type).ToArray();
+ ArgumentValues = arguments?.Select(x => x.Value).ToArray();
+
TestName = MethodInfo.Name;
ClassName = classType.Name;
FullyQualifiedClassName = classType.FullName!;
Assembly = classType.Assembly;
Source = classType.Assembly.Location;
- FullyQualifiedName = $"{classType.FullName}.{MethodInfo.Name}{GetParameterTypes(Arguments)}";
+ FullyQualifiedName = $"{classType.FullName}.{MethodInfo.Name}{GetParameterTypes(ParameterTypes)}";
IsSkipped = MethodInfo.CustomAttributes
.Concat(classType.CustomAttributes)
.Any(x => x.AttributeType == typeof(SkipAttribute));
@@ -46,25 +48,20 @@ public Test(MethodInfo MethodInfo,
public string? FileName { get; set; }
public int MinLineNumber { get; set; }
public int MaxLineNumber { get; set; }
- public ParameterArgument[]? Arguments { get; init; }
+ public Type[]? ParameterTypes { get; init; }
+ public object?[]? ArgumentValues { get; init; }
public SourceLocation SourceLocation { get; }
public bool IsSkipped { get; }
- public void Deconstruct(out MethodInfo methodInfo, out object?[]? arguments)
- {
- methodInfo = MethodInfo;
- arguments = Arguments;
- }
-
- public static string GetParameterTypes(ParameterArgument[]? arguments)
+ public static string GetParameterTypes(Type[]? types)
{
- if (arguments is null)
+ if (types is null)
{
return string.Empty;
}
- var argsAsString = arguments.Select(arg => arg.Type.FullName!);
+ var argsAsString = types.Select(arg => arg.FullName!);
return $"({string.Join(',', argsAsString)})";
}
diff --git a/TUnit.TestAdapter/AsyncTestExecutor.cs b/TUnit.TestAdapter/AsyncTestExecutor.cs
index 4668e2177c..e0476c8dd6 100644
--- a/TUnit.TestAdapter/AsyncTestExecutor.cs
+++ b/TUnit.TestAdapter/AsyncTestExecutor.cs
@@ -170,7 +170,7 @@ private async Task ExecuteTest(Test test, object @class)
{
await ExecuteSetUps(@class);
- await InvokeMethod(@class, test.MethodInfo, BindingFlags.Default, test.Arguments?.Select(x => x.Value).ToArray());
+ await InvokeMethod(@class, test.MethodInfo, BindingFlags.Default, test.ArgumentValues?.ToArray());
await ExecuteTearDowns(@class);
}
diff --git a/TUnit.TestAdapter/Extensions/TestExtensions.cs b/TUnit.TestAdapter/Extensions/TestExtensions.cs
index c07b4056bd..c7a25ca531 100644
--- a/TUnit.TestAdapter/Extensions/TestExtensions.cs
+++ b/TUnit.TestAdapter/Extensions/TestExtensions.cs
@@ -17,7 +17,7 @@ public static TestCase ToTestCase(this Test test)
};
testCase.SetPropertyValue(GetOrRegisterTestProperty("ManagedType"), test.FullyQualifiedClassName);
- testCase.SetPropertyValue(GetOrRegisterTestProperty("ManagedMethod"), test.MethodInfo.Name + Test.GetParameterTypes(test.Arguments));
+ testCase.SetPropertyValue(GetOrRegisterTestProperty("ManagedMethod"), test.MethodInfo.Name + Test.GetParameterTypes(test.ParameterTypes));
return testCase;
}
diff --git a/TUnit.TestAdapter/TestsLoader.cs b/TUnit.TestAdapter/TestsLoader.cs
index 44b013589b..b90b4ebf5a 100644
--- a/TUnit.TestAdapter/TestsLoader.cs
+++ b/TUnit.TestAdapter/TestsLoader.cs
@@ -31,7 +31,7 @@ public IEnumerable GetTests(TypeInformation typeInformation)
yield return new Test(
MethodInfo: methodInfo,
SourceLocation: sourceLocation,
- Arguments: arguments.ToArray()
+ arguments: arguments.ToArray()
);
}
@@ -40,7 +40,7 @@ public IEnumerable GetTests(TypeInformation typeInformation)
yield return new Test(
MethodInfo: methodInfo,
SourceLocation: sourceLocation,
- Arguments: null
+ arguments: null
);
}
}
From 1109f69e4e8d2a7245ff7510c571f639f9a0a26e Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 20:56:13 +0000
Subject: [PATCH 011/124] Fix
---
TUnit.TestAdapter/TestsLoader.cs | 19 +++++++++++++------
1 file changed, 13 insertions(+), 6 deletions(-)
diff --git a/TUnit.TestAdapter/TestsLoader.cs b/TUnit.TestAdapter/TestsLoader.cs
index b90b4ebf5a..ea96745e5d 100644
--- a/TUnit.TestAdapter/TestsLoader.cs
+++ b/TUnit.TestAdapter/TestsLoader.cs
@@ -1,4 +1,5 @@
using System.Reflection;
+using System.Reflection.Metadata;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using TUnit.Core;
using TUnit.Core.Attributes;
@@ -26,13 +27,19 @@ public IEnumerable GetTests(TypeInformation typeInformation)
foreach (var testWithDataAttribute in methodInfo.CustomAttributes.Where(x => x.AttributeType == typeof(TestWithDataAttribute)))
{
- var arguments = testWithDataAttribute.ConstructorArguments.Select(x => new ParameterArgument(x.ArgumentType, x.Value));
+ foreach (var customAttributeTypedArgument in testWithDataAttribute.ConstructorArguments)
+ {
+ var arguments =
+ (customAttributeTypedArgument.Value as IEnumerable)
+ ?.Select(x => new ParameterArgument(x.Value?.GetType()!, x.Value))
+ .ToArray();
- yield return new Test(
- MethodInfo: methodInfo,
- SourceLocation: sourceLocation,
- arguments: arguments.ToArray()
- );
+ yield return new Test(
+ MethodInfo: methodInfo,
+ SourceLocation: sourceLocation,
+ arguments: arguments
+ );
+ }
}
if(methodInfo.CustomAttributes.Any(x => x.AttributeType == typeof(TestAttribute)))
From 65af1207d2219201a3d36956a1b52f47d1a4df61 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 20:59:54 +0000
Subject: [PATCH 012/124] Fix
---
TUnit.Core/Test.cs | 12 ++++++++++++
TUnit.TestAdapter/Extensions/TestExtensions.cs | 2 +-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/TUnit.Core/Test.cs b/TUnit.Core/Test.cs
index 60b196ed6c..105b4626c7 100644
--- a/TUnit.Core/Test.cs
+++ b/TUnit.Core/Test.cs
@@ -18,6 +18,7 @@ public Test(MethodInfo MethodInfo,
ArgumentValues = arguments?.Select(x => x.Value).ToArray();
TestName = MethodInfo.Name;
+ DisplayName = MethodInfo.Name + GetArgumentValues();
ClassName = classType.Name;
FullyQualifiedClassName = classType.FullName!;
Assembly = classType.Assembly;
@@ -32,6 +33,16 @@ public Test(MethodInfo MethodInfo,
MaxLineNumber = SourceLocation.MaxLineNumber;
}
+ private string GetArgumentValues()
+ {
+ if (ArgumentValues == null)
+ {
+ return string.Empty;
+ }
+
+ return $"({string.Join(',', ArgumentValues.Select(StringifyArgument))})";
+ }
+
public Guid Id { get; } = Guid.NewGuid();
public string TestName { get; }
@@ -53,6 +64,7 @@ public Test(MethodInfo MethodInfo,
public SourceLocation SourceLocation { get; }
public bool IsSkipped { get; }
+ public string DisplayName { get; }
public static string GetParameterTypes(Type[]? types)
{
diff --git a/TUnit.TestAdapter/Extensions/TestExtensions.cs b/TUnit.TestAdapter/Extensions/TestExtensions.cs
index c7a25ca531..c298dd3fc3 100644
--- a/TUnit.TestAdapter/Extensions/TestExtensions.cs
+++ b/TUnit.TestAdapter/Extensions/TestExtensions.cs
@@ -10,7 +10,7 @@ public static TestCase ToTestCase(this Test test)
{
var testCase = new TestCase(test.FullyQualifiedName, TestAdapterConstants.ExecutorUri, test.Source)
{
- DisplayName = test.TestName,
+ DisplayName = test.DisplayName,
Id = test.Id,
CodeFilePath = test.FileName,
LineNumber = test.MinLineNumber,
From 09ab4d9f43a44c5e55804ed3a02f755ba6194945 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 23:04:42 +0000
Subject: [PATCH 013/124] Fix
---
TUnit.TestAdapter/AsyncTestExecutor.cs | 29 ++++++++++---------
.../Extensions/TestExtensions.cs | 1 +
TUnit.TestAdapter/TestCollector.cs | 28 +++++++++++++++++-
TUnit.TestAdapter/TestExecutor.cs | 17 ++++++++---
TUnit.TestAdapter/TestWithTestCase.cs | 6 ++++
5 files changed, 62 insertions(+), 19 deletions(-)
create mode 100644 TUnit.TestAdapter/TestWithTestCase.cs
diff --git a/TUnit.TestAdapter/AsyncTestExecutor.cs b/TUnit.TestAdapter/AsyncTestExecutor.cs
index e0476c8dd6..4994c80590 100644
--- a/TUnit.TestAdapter/AsyncTestExecutor.cs
+++ b/TUnit.TestAdapter/AsyncTestExecutor.cs
@@ -18,14 +18,14 @@ public class AsyncTestExecutor(CancellationTokenSource cancellationTokenSource)
private readonly ConcurrentDictionary _oneTimeSetUpRegistry = new();
private readonly ConcurrentDictionary _oneTimeTearDownRegistry = new();
- public async Task RunInAsyncContext(IEnumerable tests, IRunContext? runContext, IFrameworkHandle? frameworkHandle)
+ public async Task RunInAsyncContext(IEnumerable tests, IRunContext? runContext, IFrameworkHandle? frameworkHandle)
{
var allTestsOrderedByClass = tests
- .GroupBy(x => x.FullyQualifiedClassName)
+ .GroupBy(x => x.Test.FullyQualifiedClassName)
.SelectMany(x => x.ToList())
.ToList();
- var queue = new Queue(allTestsOrderedByClass);
+ var queue = new Queue(allTestsOrderedByClass);
if (queue.Count is 0)
{
@@ -57,13 +57,13 @@ public async Task RunInAsyncContext(IEnumerable tests, IRunContext? runCon
}
private void SetupRunOneTimeTearDownForClass(ProcessingTest processingTest,
- IEnumerable allTestsOrderedByClass,
+ IEnumerable allTestsOrderedByClass,
IEnumerable executingTests)
{
var lastTestForClass = allTestsOrderedByClass.Last(x =>
- x.FullyQualifiedClassName == processingTest.Test.FullyQualifiedClassName);
+ x.Test.FullyQualifiedClassName == processingTest.Test.FullyQualifiedClassName);
- if (processingTest.Test.FullyQualifiedName != lastTestForClass.FullyQualifiedName)
+ if (processingTest.Test.FullyQualifiedName != lastTestForClass.Test.FullyQualifiedName)
{
return;
}
@@ -82,7 +82,7 @@ private void SetupRunOneTimeTearDownForClass(ProcessingTest processingTest,
});
}
- private async IAsyncEnumerable ProcessQueue(Queue queue, ITestExecutionRecorder? frameworkHandle)
+ private async IAsyncEnumerable ProcessQueue(Queue queue, ITestExecutionRecorder? frameworkHandle)
{
while (queue.Count > 0)
{
@@ -90,9 +90,9 @@ private async IAsyncEnumerable ProcessQueue(Queue queue, I
{
var test = queue.Dequeue();
- var @class = CreateTestClass(test);
+ var @class = CreateTestClass(test.Test);
- yield return new ProcessingTest(test, @class, ProcessTest(test, @class, frameworkHandle));
+ yield return new ProcessingTest(test.Test, @class, ProcessTest(test, @class, frameworkHandle));
}
else if (cancellationTokenSource.IsCancellationRequested)
{
@@ -105,9 +105,9 @@ private async IAsyncEnumerable ProcessQueue(Queue queue, I
}
}
- private async Task ProcessTest(Test test, object @class, ITestExecutionRecorder? frameworkHandle)
+ private async Task ProcessTest(TestWithTestCase testWithTestCase, object @class, ITestExecutionRecorder? frameworkHandle)
{
- await ExecuteTestMethod(test, @class, frameworkHandle);
+ await ExecuteTestMethod(testWithTestCase, @class, frameworkHandle);
}
private static object CreateTestClass(Test test)
@@ -115,10 +115,11 @@ private static object CreateTestClass(Test test)
return Activator.CreateInstance(test.MethodInfo.DeclaringType!)!;
}
- private async ValueTask ExecuteTestMethod(Test test, object @class, ITestExecutionRecorder? frameworkHandle)
+ private async ValueTask ExecuteTestMethod(TestWithTestCase testWithTestCase, object @class, ITestExecutionRecorder? frameworkHandle)
{
- var testCase = test.ToTestCase();
-
+ var test = testWithTestCase.Test;
+ var testCase = testWithTestCase.TestCase;
+
if (test.IsSkipped)
{
frameworkHandle?.RecordEnd(testCase, TestOutcome.Skipped);
diff --git a/TUnit.TestAdapter/Extensions/TestExtensions.cs b/TUnit.TestAdapter/Extensions/TestExtensions.cs
index c298dd3fc3..17d249f1f4 100644
--- a/TUnit.TestAdapter/Extensions/TestExtensions.cs
+++ b/TUnit.TestAdapter/Extensions/TestExtensions.cs
@@ -14,6 +14,7 @@ public static TestCase ToTestCase(this Test test)
Id = test.Id,
CodeFilePath = test.FileName,
LineNumber = test.MinLineNumber,
+ LocalExtensionData = test
};
testCase.SetPropertyValue(GetOrRegisterTestProperty("ManagedType"), test.FullyQualifiedClassName);
diff --git a/TUnit.TestAdapter/TestCollector.cs b/TUnit.TestAdapter/TestCollector.cs
index 1e8cda95be..164415c9f1 100644
--- a/TUnit.TestAdapter/TestCollector.cs
+++ b/TUnit.TestAdapter/TestCollector.cs
@@ -1,6 +1,8 @@
using System.Reflection;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using TUnit.Core;
+using TUnit.TestAdapter.Extensions;
namespace TUnit.TestAdapter;
@@ -12,7 +14,31 @@ public TestCollection CollectionFromSources(IEnumerable sources)
return new TestCollection(sourcesAsList, TestsFromSources(sourcesAsList));
}
-
+
+ public IEnumerable TestsFromTestCases(IEnumerable testCases)
+ {
+ var assemblyLoader = new AssemblyLoader();
+ var testsLoader = new TestsLoader(messageLogger);
+
+ foreach (var testCase in testCases)
+ {
+ var source = testCase.Source;
+ var assembly = assemblyLoader.LoadByPath(source);
+
+ if (assembly is null)
+ {
+ continue;
+ }
+
+ var tests = testsLoader.GetTests(new TypeInformation(assembly));
+
+ var matchingTest = tests.First(x => x.FullyQualifiedName == testCase.FullyQualifiedName
+ && x.DisplayName == testCase.DisplayName);
+
+ yield return new TestWithTestCase(matchingTest, testCase);
+ }
+ }
+
public IEnumerable TestsFromSources(IEnumerable sources)
{
var assemblyLoader = new AssemblyLoader();
diff --git a/TUnit.TestAdapter/TestExecutor.cs b/TUnit.TestAdapter/TestExecutor.cs
index 6c4870509b..74ef602d53 100644
--- a/TUnit.TestAdapter/TestExecutor.cs
+++ b/TUnit.TestAdapter/TestExecutor.cs
@@ -1,6 +1,8 @@
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
+using TUnit.Core;
using TUnit.TestAdapter.Constants;
+using TUnit.TestAdapter.Extensions;
namespace TUnit.TestAdapter;
@@ -19,10 +21,16 @@ public TestExecutor()
_asyncTestExecutor = new AsyncTestExecutor(_cancellationTokenSource);
}
- public void RunTests(IEnumerable? tests, IRunContext? runContext, IFrameworkHandle? frameworkHandle)
+ public void RunTests(IEnumerable? testCases, IRunContext? runContext, IFrameworkHandle? frameworkHandle)
{
- RunTests(tests?.Select(x => x.Source), runContext, frameworkHandle);
- }
+ if (testCases is null)
+ {
+ return;
+ }
+
+ var testsWithTestCases = new TestCollector(frameworkHandle).TestsFromTestCases(testCases);
+
+ _asyncTestExecutor.RunInAsyncContext(testsWithTestCases, runContext, frameworkHandle).GetAwaiter().GetResult(); }
public void RunTests(IEnumerable? sources, IRunContext? runContext, IFrameworkHandle? frameworkHandle)
{
@@ -31,7 +39,8 @@ public void RunTests(IEnumerable? sources, IRunContext? runContext, IFra
return;
}
- var tests = new TestCollector(frameworkHandle).TestsFromSources(sources);
+ var tests = new TestCollector(frameworkHandle).TestsFromSources(sources)
+ .Select(x => new TestWithTestCase(x, x.ToTestCase()));
_asyncTestExecutor.RunInAsyncContext(tests, runContext, frameworkHandle).GetAwaiter().GetResult();
}
diff --git a/TUnit.TestAdapter/TestWithTestCase.cs b/TUnit.TestAdapter/TestWithTestCase.cs
new file mode 100644
index 0000000000..5fe8dbf9dd
--- /dev/null
+++ b/TUnit.TestAdapter/TestWithTestCase.cs
@@ -0,0 +1,6 @@
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using TUnit.Core;
+
+namespace TUnit.TestAdapter;
+
+public record TestWithTestCase(Test Test, TestCase TestCase);
\ No newline at end of file
From 0c34995102b0fc50336b519a45ca766cae7b0600 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 23:13:41 +0000
Subject: [PATCH 014/124] Tweaks
---
TUnit.TestAdapter/TUnit.TestAdapter.csproj | 3 ++-
TUnit.TestProject/TUnit.TestProject.csproj | 10 +++++-----
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/TUnit.TestAdapter/TUnit.TestAdapter.csproj b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
index 785d3f930e..52cea0b729 100644
--- a/TUnit.TestAdapter/TUnit.TestAdapter.csproj
+++ b/TUnit.TestAdapter/TUnit.TestAdapter.csproj
@@ -7,10 +7,11 @@
default
true
Library
+ true
-
+
diff --git a/TUnit.TestProject/TUnit.TestProject.csproj b/TUnit.TestProject/TUnit.TestProject.csproj
index ecdf62100c..2da8dd7aca 100644
--- a/TUnit.TestProject/TUnit.TestProject.csproj
+++ b/TUnit.TestProject/TUnit.TestProject.csproj
@@ -11,14 +11,14 @@
-
-
-
-
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
-
+
From c6edd6c4c48c039fceebbd959f85c02afa5af1e4 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 23:36:29 +0000
Subject: [PATCH 015/124] Fix queuing tests logic
---
TUnit.TestAdapter/AsyncTestExecutor.cs | 11 ++++++++---
TUnit.TestProject/TUnit.TestProject.csproj | 10 +++++-----
2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/TUnit.TestAdapter/AsyncTestExecutor.cs b/TUnit.TestAdapter/AsyncTestExecutor.cs
index 4994c80590..3dcd23582e 100644
--- a/TUnit.TestAdapter/AsyncTestExecutor.cs
+++ b/TUnit.TestAdapter/AsyncTestExecutor.cs
@@ -288,11 +288,11 @@ private async Task InvokeMethod(object @class, MethodInfo methodInfo, BindingFla
{
try
{
- var result = methodInfo.Invoke(@class, bindingFlags, null, arguments, CultureInfo.InvariantCulture);
+ var result = await Task.Run(() => methodInfo.Invoke(@class, bindingFlags, null, arguments, CultureInfo.InvariantCulture) as Task);
- if (result is Task task)
+ if (result != null)
{
- await task;
+ await result;
}
}
catch (TargetInvocationException e)
@@ -305,4 +305,9 @@ private async Task InvokeMethod(object @class, MethodInfo methodInfo, BindingFla
ExceptionDispatchInfo.Capture(e.InnerException).Throw();
}
}
+
+ private bool IsTask(Type type)
+ {
+ return type.IsAssignableTo(typeof(Task));
+ }
}
\ No newline at end of file
diff --git a/TUnit.TestProject/TUnit.TestProject.csproj b/TUnit.TestProject/TUnit.TestProject.csproj
index 2da8dd7aca..6354dae533 100644
--- a/TUnit.TestProject/TUnit.TestProject.csproj
+++ b/TUnit.TestProject/TUnit.TestProject.csproj
@@ -11,14 +11,14 @@
-
- runtime; build; native; contentfiles; analyzers; buildtransitive
- all
-
+
+
+
+
-
+
From 2347fababcf37aa52ca16e13103091513959b94b Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 23:41:25 +0000
Subject: [PATCH 016/124] Skip logic fix
---
TUnit.TestAdapter/AsyncTestExecutor.cs | 10 ++++++++++
TUnit.TestProject/Tests.cs | 15 +++++++++++++++
2 files changed, 25 insertions(+)
diff --git a/TUnit.TestAdapter/AsyncTestExecutor.cs b/TUnit.TestAdapter/AsyncTestExecutor.cs
index 3dcd23582e..2f5a4b6912 100644
--- a/TUnit.TestAdapter/AsyncTestExecutor.cs
+++ b/TUnit.TestAdapter/AsyncTestExecutor.cs
@@ -122,7 +122,17 @@ private async ValueTask ExecuteTestMethod(TestWithTestCase testWithTestCase, obj
if (test.IsSkipped)
{
+ var skipTime = DateTimeOffset.Now;
frameworkHandle?.RecordEnd(testCase, TestOutcome.Skipped);
+ frameworkHandle?.RecordResult(new TestResult(testCase)
+ {
+ Outcome = TestOutcome.Skipped,
+ DisplayName = test.TestName,
+ StartTime = skipTime,
+ EndTime = skipTime,
+ Duration = TimeSpan.Zero,
+ ComputerName = Environment.MachineName,
+ });
return;
}
diff --git a/TUnit.TestProject/Tests.cs b/TUnit.TestProject/Tests.cs
index 6e6f21b708..2a2b83431c 100644
--- a/TUnit.TestProject/Tests.cs
+++ b/TUnit.TestProject/Tests.cs
@@ -54,4 +54,19 @@ public async Task ParameterisedTests2(string value)
await Task.Yield();
Assert.That(value, Is.EqualTo("1"));
}
+
+ [Test, Skip]
+ public void Skip1()
+ {
+ var value = "1";
+ Assert.That(value, Is.EqualTo("1"));
+ }
+
+ [Test, Skip]
+ public async Task Skip2()
+ {
+ await Task.Yield();
+ var value = "1";
+ Assert.That(value, Is.EqualTo("1"));
+ }
}
\ No newline at end of file
From aad383fbdfde3c75aca5ce1847acc7ce0344f9a9 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 23:48:28 +0000
Subject: [PATCH 017/124] Tweaks
---
TUnit.TestAdapter/AsyncTestExecutor.cs | 1 -
TUnit.TestAdapter/TestCollector.cs | 28 +++++++++++++++++++++++---
TUnit.TestAdapter/TestDiscoverer.cs | 3 +--
TUnit.TestAdapter/TestExecutor.cs | 10 +++++----
TUnit.TestAdapter/TestsLoader.cs | 1 -
5 files changed, 32 insertions(+), 11 deletions(-)
diff --git a/TUnit.TestAdapter/AsyncTestExecutor.cs b/TUnit.TestAdapter/AsyncTestExecutor.cs
index 2f5a4b6912..0c13f454ad 100644
--- a/TUnit.TestAdapter/AsyncTestExecutor.cs
+++ b/TUnit.TestAdapter/AsyncTestExecutor.cs
@@ -7,7 +7,6 @@
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using TUnit.Core;
using TUnit.Core.Attributes;
-using TUnit.TestAdapter.Extensions;
namespace TUnit.TestAdapter;
diff --git a/TUnit.TestAdapter/TestCollector.cs b/TUnit.TestAdapter/TestCollector.cs
index 164415c9f1..b63a41e54c 100644
--- a/TUnit.TestAdapter/TestCollector.cs
+++ b/TUnit.TestAdapter/TestCollector.cs
@@ -1,8 +1,8 @@
using System.Reflection;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using TUnit.Core;
-using TUnit.TestAdapter.Extensions;
namespace TUnit.TestAdapter;
@@ -15,7 +15,7 @@ public TestCollection CollectionFromSources(IEnumerable sources)
return new TestCollection(sourcesAsList, TestsFromSources(sourcesAsList));
}
- public IEnumerable TestsFromTestCases(IEnumerable testCases)
+ public IEnumerable TestsFromTestCases(IEnumerable testCases, ITestExecutionRecorder? testExecutionRecorder)
{
var assemblyLoader = new AssemblyLoader();
var testsLoader = new TestsLoader(messageLogger);
@@ -27,18 +27,40 @@ public IEnumerable TestsFromTestCases(IEnumerable te
if (assembly is null)
{
+ MarkNotFound(testCase, testExecutionRecorder);
continue;
}
var tests = testsLoader.GetTests(new TypeInformation(assembly));
- var matchingTest = tests.First(x => x.FullyQualifiedName == testCase.FullyQualifiedName
+ var matchingTest = tests.FirstOrDefault(x => x.FullyQualifiedName == testCase.FullyQualifiedName
&& x.DisplayName == testCase.DisplayName);
+ if (matchingTest is null)
+ {
+ MarkNotFound(testCase, testExecutionRecorder);
+ continue;
+ }
+
yield return new TestWithTestCase(matchingTest, testCase);
}
}
+ private void MarkNotFound(TestCase testCase, ITestExecutionRecorder? testExecutionRecorder)
+ {
+ var now = DateTimeOffset.Now;
+
+ testExecutionRecorder?.RecordResult(new TestResult(testCase)
+ {
+ DisplayName = testCase.DisplayName,
+ Outcome = TestOutcome.NotFound,
+ Duration = TimeSpan.Zero,
+ StartTime = now,
+ EndTime = now,
+ ComputerName = Environment.MachineName
+ });
+ }
+
public IEnumerable TestsFromSources(IEnumerable sources)
{
var assemblyLoader = new AssemblyLoader();
diff --git a/TUnit.TestAdapter/TestDiscoverer.cs b/TUnit.TestAdapter/TestDiscoverer.cs
index 2e5b18a971..d08bac4ec3 100644
--- a/TUnit.TestAdapter/TestDiscoverer.cs
+++ b/TUnit.TestAdapter/TestDiscoverer.cs
@@ -1,5 +1,4 @@
-using System.Diagnostics;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
+using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using TUnit.TestAdapter.Constants;
diff --git a/TUnit.TestAdapter/TestExecutor.cs b/TUnit.TestAdapter/TestExecutor.cs
index 74ef602d53..857e5bc705 100644
--- a/TUnit.TestAdapter/TestExecutor.cs
+++ b/TUnit.TestAdapter/TestExecutor.cs
@@ -1,6 +1,5 @@
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Adapter;
-using TUnit.Core;
using TUnit.TestAdapter.Constants;
using TUnit.TestAdapter.Extensions;
@@ -17,7 +16,6 @@ public class TestExecutor : ITestExecutor2
public TestExecutor()
{
- Console.CancelKeyPress += (_, _) => _cancellationTokenSource.Cancel();
_asyncTestExecutor = new AsyncTestExecutor(_cancellationTokenSource);
}
@@ -28,9 +26,13 @@ public void RunTests(IEnumerable? testCases, IRunContext? runContext,
return;
}
- var testsWithTestCases = new TestCollector(frameworkHandle).TestsFromTestCases(testCases);
+ var testsWithTestCases = new TestCollector(frameworkHandle)
+ .TestsFromTestCases(testCases, frameworkHandle);
- _asyncTestExecutor.RunInAsyncContext(testsWithTestCases, runContext, frameworkHandle).GetAwaiter().GetResult(); }
+ _asyncTestExecutor.RunInAsyncContext(testsWithTestCases, runContext, frameworkHandle)
+ .GetAwaiter()
+ .GetResult();
+ }
public void RunTests(IEnumerable? sources, IRunContext? runContext, IFrameworkHandle? frameworkHandle)
{
diff --git a/TUnit.TestAdapter/TestsLoader.cs b/TUnit.TestAdapter/TestsLoader.cs
index ea96745e5d..4bd2f97811 100644
--- a/TUnit.TestAdapter/TestsLoader.cs
+++ b/TUnit.TestAdapter/TestsLoader.cs
@@ -1,5 +1,4 @@
using System.Reflection;
-using System.Reflection.Metadata;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging;
using TUnit.Core;
using TUnit.Core.Attributes;
From 86b006d12cb913535990289f04d463a96792dc3c Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Thu, 25 Jan 2024 23:56:57 +0000
Subject: [PATCH 018/124] Fixes
---
TUnit.Core/Attributes/SkipAttribute.cs | 10 +++++++++-
TUnit.Core/Test.cs | 10 ++++++----
TUnit.TestAdapter/AsyncTestExecutor.cs | 2 ++
TUnit.TestProject/Tests.cs | 4 ++--
4 files changed, 19 insertions(+), 7 deletions(-)
diff --git a/TUnit.Core/Attributes/SkipAttribute.cs b/TUnit.Core/Attributes/SkipAttribute.cs
index 22bb734105..311a6cbfd0 100644
--- a/TUnit.Core/Attributes/SkipAttribute.cs
+++ b/TUnit.Core/Attributes/SkipAttribute.cs
@@ -1,4 +1,12 @@
namespace TUnit.Core.Attributes;
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
-public class SkipAttribute : TUnitAttribute;
\ No newline at end of file
+public class SkipAttribute : TUnitAttribute
+{
+ public string Reason { get; }
+
+ public SkipAttribute(string reason)
+ {
+ Reason = reason;
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Core/Test.cs b/TUnit.Core/Test.cs
index 105b4626c7..cc33ef8d27 100644
--- a/TUnit.Core/Test.cs
+++ b/TUnit.Core/Test.cs
@@ -24,15 +24,16 @@ public Test(MethodInfo MethodInfo,
Assembly = classType.Assembly;
Source = classType.Assembly.Location;
FullyQualifiedName = $"{classType.FullName}.{MethodInfo.Name}{GetParameterTypes(ParameterTypes)}";
- IsSkipped = MethodInfo.CustomAttributes
+ SkipReason = MethodInfo.CustomAttributes
.Concat(classType.CustomAttributes)
- .Any(x => x.AttributeType == typeof(SkipAttribute));
+ .FirstOrDefault(x => x.AttributeType == typeof(SkipAttribute))
+ ?.ConstructorArguments.FirstOrDefault().Value as string;
FileName = SourceLocation.FileName;
MinLineNumber = SourceLocation.MinLineNumber;
MaxLineNumber = SourceLocation.MaxLineNumber;
}
-
+
private string GetArgumentValues()
{
if (ArgumentValues == null)
@@ -63,7 +64,8 @@ private string GetArgumentValues()
public object?[]? ArgumentValues { get; init; }
public SourceLocation SourceLocation { get; }
- public bool IsSkipped { get; }
+ public string? SkipReason { get; }
+ public bool IsSkipped => !string.IsNullOrEmpty(SkipReason);
public string DisplayName { get; }
public static string GetParameterTypes(Type[]? types)
diff --git a/TUnit.TestAdapter/AsyncTestExecutor.cs b/TUnit.TestAdapter/AsyncTestExecutor.cs
index 0c13f454ad..fdd9dd45fd 100644
--- a/TUnit.TestAdapter/AsyncTestExecutor.cs
+++ b/TUnit.TestAdapter/AsyncTestExecutor.cs
@@ -131,7 +131,9 @@ private async ValueTask ExecuteTestMethod(TestWithTestCase testWithTestCase, obj
EndTime = skipTime,
Duration = TimeSpan.Zero,
ComputerName = Environment.MachineName,
+ ErrorMessage = $"Skipped due to: {test.SkipReason}",
});
+
return;
}
diff --git a/TUnit.TestProject/Tests.cs b/TUnit.TestProject/Tests.cs
index 2a2b83431c..e9ebad549d 100644
--- a/TUnit.TestProject/Tests.cs
+++ b/TUnit.TestProject/Tests.cs
@@ -55,14 +55,14 @@ public async Task ParameterisedTests2(string value)
Assert.That(value, Is.EqualTo("1"));
}
- [Test, Skip]
+ [Test, Skip("Reason1")]
public void Skip1()
{
var value = "1";
Assert.That(value, Is.EqualTo("1"));
}
- [Test, Skip]
+ [Test, Skip("Reason2")]
public async Task Skip2()
{
await Task.Yield();
From 1cfd42a3ea31a22743e5147ccddeffb364bf4d41 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Fri, 26 Jan 2024 12:27:57 +0000
Subject: [PATCH 019/124] Fix
---
.../AssertConditions/String/StringEqualsAssertCondition.cs | 4 +++-
TUnit.Assertions/Is_Strings.cs | 7 ++++++-
TUnit.Assertions/TUnit.Assertions.csproj | 1 +
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs b/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs
index 783edef8ab..8f2937ffd3 100644
--- a/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs
+++ b/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs
@@ -11,7 +11,9 @@ public StringEqualsAssertCondition(string expected, StringComparison stringCompa
public override bool Matches(string actualValue)
{
- Message = $"Expected {ExpectedValue} but received {actualValue}";
+ Message = $"""
+ Expected "{ExpectedValue}" but received "{actualValue}"
+ """;
return string.Equals(actualValue, ExpectedValue, _stringComparison);
}
diff --git a/TUnit.Assertions/Is_Strings.cs b/TUnit.Assertions/Is_Strings.cs
index 5ecf030d19..b929918a5e 100644
--- a/TUnit.Assertions/Is_Strings.cs
+++ b/TUnit.Assertions/Is_Strings.cs
@@ -4,7 +4,12 @@ namespace TUnit.Assertions;
public static partial class Is
{
- public static AssertCondition EqualTo(string expected, StringComparison stringComparison = StringComparison.Ordinal)
+ public static AssertCondition EqualTo(string expected)
+ {
+ return new StringEqualsAssertCondition(expected, StringComparison.Ordinal);
+ }
+
+ public static AssertCondition EqualTo(string expected, StringComparison stringComparison)
{
return new StringEqualsAssertCondition(expected, stringComparison);
}
diff --git a/TUnit.Assertions/TUnit.Assertions.csproj b/TUnit.Assertions/TUnit.Assertions.csproj
index ca62071d4f..9b799c2645 100644
--- a/TUnit.Assertions/TUnit.Assertions.csproj
+++ b/TUnit.Assertions/TUnit.Assertions.csproj
@@ -4,6 +4,7 @@
net6.0
enable
enable
+ latest
From 8d35c92cb197370998ef37103b636b642b34c5a3 Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Fri, 26 Jan 2024 12:58:40 +0000
Subject: [PATCH 020/124] Fixes
---
TUnit.Assertions/Assert.cs | 12 ++----------
TUnit.Assertions/AssertCondition.cs | 18 +++++++++++++-----
.../Generic/EqualsAssertCondition.cs | 10 +++++-----
.../Generic/SameReferenceAssertCondition.cs | 7 ++++---
.../Numbers/GreaterThanAssertCondition.cs | 18 ++++++++++++++++++
.../GreaterThanOrEqualToAssertCondition.cs | 18 ++++++++++++++++++
.../Numbers/LessThanAssertCondition.cs | 18 ++++++++++++++++++
.../LessThanOrEqualToAssertCondition.cs | 18 ++++++++++++++++++
.../String/StringEqualsAssertCondition.cs | 12 ++++++------
TUnit.Assertions/IAssertCondition.cs | 6 ++----
TUnit.Assertions/Is_Numbers.cs | 11 +++++++++++
TUnit.Assertions/TUnit.Assertions.csproj | 2 +-
12 files changed, 116 insertions(+), 34 deletions(-)
create mode 100644 TUnit.Assertions/AssertConditions/Numbers/GreaterThanAssertCondition.cs
create mode 100644 TUnit.Assertions/AssertConditions/Numbers/GreaterThanOrEqualToAssertCondition.cs
create mode 100644 TUnit.Assertions/AssertConditions/Numbers/LessThanAssertCondition.cs
create mode 100644 TUnit.Assertions/AssertConditions/Numbers/LessThanOrEqualToAssertCondition.cs
create mode 100644 TUnit.Assertions/Is_Numbers.cs
diff --git a/TUnit.Assertions/Assert.cs b/TUnit.Assertions/Assert.cs
index 609b4a54bc..d5b2f6ffe7 100644
--- a/TUnit.Assertions/Assert.cs
+++ b/TUnit.Assertions/Assert.cs
@@ -6,17 +6,9 @@ public static class Assert
{
public static void That(T value, AssertCondition assertCondition)
{
- if (!assertCondition.Matches(value))
+ if (!assertCondition.Assert(value))
{
- var message = GetMessage(assertCondition, value);
- throw new AssertionException(message);
+ throw new AssertionException(assertCondition.Message);
}
}
-
- private static string GetMessage(AssertCondition assertCondition, T actualValue)
- {
- return assertCondition.MessageFactory != null
- ? assertCondition.MessageFactory((assertCondition.ExpectedValue, actualValue))
- : assertCondition.Message;
- }
}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertCondition.cs b/TUnit.Assertions/AssertCondition.cs
index b56e622ce3..cf880160b4 100644
--- a/TUnit.Assertions/AssertCondition.cs
+++ b/TUnit.Assertions/AssertCondition.cs
@@ -7,14 +7,22 @@ internal AssertCondition(T expected)
ExpectedValue = expected;
}
- internal Func<(T expectedValue, T actualValue), string>? MessageFactory { get; private set; }
+ internal abstract Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
- public T ExpectedValue { get; }
- public abstract bool Matches(T actualValue);
+ internal T ExpectedValue { get; }
+ private T ActualValue { get; set; } = default!;
+
+ public bool Assert(T actualValue)
+ {
+ ActualValue = actualValue;
+ return Passes(actualValue);
+ }
- public abstract string Message { get; protected set; }
+ protected abstract bool Passes(T actualValue);
+
+ public string Message => MessageFactory.Invoke((ExpectedValue, ActualValue));
- public IAssertCondition WithMessage(Func<(T expectedValue, T actualValue), string> messageFactory)
+ public IAssertCondition WithMessage(Func<(T ExpectedValue, T ActualValue), string> messageFactory)
{
MessageFactory = messageFactory;
return this;
diff --git a/TUnit.Assertions/AssertConditions/Generic/EqualsAssertCondition.cs b/TUnit.Assertions/AssertConditions/Generic/EqualsAssertCondition.cs
index 5ea7757dc3..6490c8687d 100644
--- a/TUnit.Assertions/AssertConditions/Generic/EqualsAssertCondition.cs
+++ b/TUnit.Assertions/AssertConditions/Generic/EqualsAssertCondition.cs
@@ -8,12 +8,12 @@ public EqualsAssertCondition(T expected) : base(expected)
{
_expected = expected;
}
-
- public override bool Matches(T actualValue)
+
+ internal override Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
+ = tuple => $"Expected {tuple.ExpectedValue} but received {tuple.ActualValue}";
+
+ protected override bool Passes(T actualValue)
{
- Message = $"Expected {_expected} but received {actualValue}";
return Equals(actualValue, _expected);
}
-
- public override string Message { get; protected set; } = string.Empty;
}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/Generic/SameReferenceAssertCondition.cs b/TUnit.Assertions/AssertConditions/Generic/SameReferenceAssertCondition.cs
index f31a6b8ea0..61105be483 100644
--- a/TUnit.Assertions/AssertConditions/Generic/SameReferenceAssertCondition.cs
+++ b/TUnit.Assertions/AssertConditions/Generic/SameReferenceAssertCondition.cs
@@ -9,10 +9,11 @@ public SameReferenceAssertCondition(T expected) : base(expected)
_expected = expected;
}
- public override bool Matches(T actualValue)
+ internal override Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
+ = tuple => $"The two objects are different references.";
+
+ protected override bool Passes(T actualValue)
{
return ReferenceEquals(actualValue, _expected);
}
-
- public override string Message { get; protected set; } = string.Empty;
}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/Numbers/GreaterThanAssertCondition.cs b/TUnit.Assertions/AssertConditions/Numbers/GreaterThanAssertCondition.cs
new file mode 100644
index 0000000000..651f1e21d3
--- /dev/null
+++ b/TUnit.Assertions/AssertConditions/Numbers/GreaterThanAssertCondition.cs
@@ -0,0 +1,18 @@
+using System.Numerics;
+
+namespace TUnit.Assertions;
+
+public class GreaterThanAssertCondition : AssertCondition where T : INumber
+{
+ public GreaterThanAssertCondition(T expected) : base(expected)
+ {
+ }
+
+ internal override Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
+ = tuple => $"{tuple.ActualValue} is not greater than {tuple.ExpectedValue}";
+
+ protected override bool Passes(T actualValue)
+ {
+ return actualValue > ExpectedValue;
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/Numbers/GreaterThanOrEqualToAssertCondition.cs b/TUnit.Assertions/AssertConditions/Numbers/GreaterThanOrEqualToAssertCondition.cs
new file mode 100644
index 0000000000..4c09dd36be
--- /dev/null
+++ b/TUnit.Assertions/AssertConditions/Numbers/GreaterThanOrEqualToAssertCondition.cs
@@ -0,0 +1,18 @@
+using System.Numerics;
+
+namespace TUnit.Assertions;
+
+public class GreaterThanOrEqualToAssertCondition : AssertCondition where T : INumber
+{
+ public GreaterThanOrEqualToAssertCondition(T expected) : base(expected)
+ {
+ }
+
+ internal override Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
+ = tuple => $"{tuple.ActualValue} is not greater than or equal to {tuple.ExpectedValue}";
+
+ protected override bool Passes(T actualValue)
+ {
+ return actualValue >= ExpectedValue;
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/Numbers/LessThanAssertCondition.cs b/TUnit.Assertions/AssertConditions/Numbers/LessThanAssertCondition.cs
new file mode 100644
index 0000000000..411786edaf
--- /dev/null
+++ b/TUnit.Assertions/AssertConditions/Numbers/LessThanAssertCondition.cs
@@ -0,0 +1,18 @@
+using System.Numerics;
+
+namespace TUnit.Assertions;
+
+public class LessThanAssertCondition : AssertCondition where T : INumber
+{
+ public LessThanAssertCondition(T expected) : base(expected)
+ {
+ }
+
+ internal override Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
+ = tuple => $"{tuple.ActualValue} is not less than {tuple.ExpectedValue}";
+
+ protected override bool Passes(T actualValue)
+ {
+ return actualValue < ExpectedValue;
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/Numbers/LessThanOrEqualToAssertCondition.cs b/TUnit.Assertions/AssertConditions/Numbers/LessThanOrEqualToAssertCondition.cs
new file mode 100644
index 0000000000..0bed6eaae8
--- /dev/null
+++ b/TUnit.Assertions/AssertConditions/Numbers/LessThanOrEqualToAssertCondition.cs
@@ -0,0 +1,18 @@
+using System.Numerics;
+
+namespace TUnit.Assertions;
+
+public class LessThanOrEqualToAssertCondition : AssertCondition where T : INumber
+{
+ public LessThanOrEqualToAssertCondition(T expected) : base(expected)
+ {
+ }
+
+ internal override Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
+ = tuple => $"{tuple.ActualValue} is not less than or equal to {tuple.ExpectedValue}";
+
+ protected override bool Passes(T actualValue)
+ {
+ return actualValue <= ExpectedValue;
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs b/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs
index 8f2937ffd3..eb6064252b 100644
--- a/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs
+++ b/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs
@@ -9,13 +9,13 @@ public StringEqualsAssertCondition(string expected, StringComparison stringCompa
_stringComparison = stringComparison;
}
- public override bool Matches(string actualValue)
+ protected override bool Passes(string actualValue)
{
- Message = $"""
- Expected "{ExpectedValue}" but received "{actualValue}"
- """;
return string.Equals(actualValue, ExpectedValue, _stringComparison);
}
-
- public override string Message { get; protected set; } = string.Empty;
+
+ internal override Func<(string ExpectedValue, string ActualValue), string> MessageFactory { get; set; }
+ = tuple => $"""
+ Expected "{tuple.ExpectedValue}" but received "{tuple.ActualValue}"
+ """;
}
\ No newline at end of file
diff --git a/TUnit.Assertions/IAssertCondition.cs b/TUnit.Assertions/IAssertCondition.cs
index f4d510cb4e..7790086834 100644
--- a/TUnit.Assertions/IAssertCondition.cs
+++ b/TUnit.Assertions/IAssertCondition.cs
@@ -1,10 +1,8 @@
namespace TUnit.Assertions;
-public interface IAssertCondition
+public interface IAssertCondition
{
- internal T ExpectedValue { get; }
-
- public bool Matches(T actualValue);
+ public bool Assert(T actualValue);
internal string Message { get; }
}
\ No newline at end of file
diff --git a/TUnit.Assertions/Is_Numbers.cs b/TUnit.Assertions/Is_Numbers.cs
new file mode 100644
index 0000000000..96ad17f808
--- /dev/null
+++ b/TUnit.Assertions/Is_Numbers.cs
@@ -0,0 +1,11 @@
+using System.Numerics;
+
+namespace TUnit.Assertions;
+
+public static partial class Is
+{
+ public static AssertCondition GreaterThan(T expected) where T : INumber
+ {
+ return new GreaterThanAssertCondition(expected);
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/TUnit.Assertions.csproj b/TUnit.Assertions/TUnit.Assertions.csproj
index 9b799c2645..d537ffdf6c 100644
--- a/TUnit.Assertions/TUnit.Assertions.csproj
+++ b/TUnit.Assertions/TUnit.Assertions.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
enable
enable
latest
From e488bcda0476c403fee8dfae5a01448f2f4f529d Mon Sep 17 00:00:00 2001
From: Tom Longhurst <30480171+thomhurst@users.noreply.github.com>
Date: Fri, 26 Jan 2024 13:35:45 +0000
Subject: [PATCH 021/124] Fixes
---
TUnit.Assertions/AssertCondition.cs | 18 ++++++++--------
.../Generic/EqualsAssertCondition.cs | 13 +++++-------
.../Generic/SameReferenceAssertCondition.cs | 13 +++++-------
.../Numbers/GreaterThanAssertCondition.cs | 13 ++++++------
.../GreaterThanOrEqualToAssertCondition.cs | 13 ++++++------
.../Numbers/IsEvenAssertCondition.cs | 19 +++++++++++++++++
.../Numbers/LessThanAssertCondition.cs | 13 ++++++------
.../LessThanOrEqualToAssertCondition.cs | 13 ++++++------
.../String/StringEqualsAssertCondition.cs | 11 +++++-----
.../ExpectedValueAssertCondition.cs | 21 +++++++++++++++++++
TUnit.Assertions/IAssertCondition.cs | 2 +-
TUnit.Assertions/Is.cs | 4 ++--
TUnit.Assertions/Is_Numbers.cs | 3 ++-
TUnit.Assertions/TUnit.Assertions.csproj | 2 +-
TUnit.Core/TUnit.Core.csproj | 2 +-
TUnit.Pipeline/Modules/BuildProjectsModule.cs | 20 ++++++++++++++++++
TUnit.Pipeline/Modules/CleanProjectsModule.cs | 11 ----------
TUnit.Pipeline/PackedProject.cs | 2 +-
TUnit.Pipeline/TUnit.Pipeline.csproj | 2 +-
TUnit.TestAdapter/TUnit.TestAdapter.csproj | 2 +-
TUnit.TestProject/TUnit.TestProject.csproj | 2 +-
TUnit/TUnit.csproj | 2 +-
22 files changed, 124 insertions(+), 77 deletions(-)
create mode 100644 TUnit.Assertions/AssertConditions/Numbers/IsEvenAssertCondition.cs
create mode 100644 TUnit.Assertions/ExpectedValueAssertCondition.cs
create mode 100644 TUnit.Pipeline/Modules/BuildProjectsModule.cs
diff --git a/TUnit.Assertions/AssertCondition.cs b/TUnit.Assertions/AssertCondition.cs
index cf880160b4..7b32a954ab 100644
--- a/TUnit.Assertions/AssertCondition.cs
+++ b/TUnit.Assertions/AssertCondition.cs
@@ -2,27 +2,27 @@
public abstract class AssertCondition : IAssertCondition
{
- internal AssertCondition(T expected)
+ internal AssertCondition()
{
- ExpectedValue = expected;
}
-
- internal abstract Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
- internal T ExpectedValue { get; }
- private T ActualValue { get; set; } = default!;
+ private Func? MessageFactory { get; set; }
+
+ protected T ActualValue { get; private set; } = default!;
public bool Assert(T actualValue)
{
ActualValue = actualValue;
return Passes(actualValue);
}
-
+
+ public abstract string DefaultMessage { get; }
+
protected abstract bool Passes(T actualValue);
- public string Message => MessageFactory.Invoke((ExpectedValue, ActualValue));
+ public string Message => MessageFactory?.Invoke(ActualValue) ?? DefaultMessage;
- public IAssertCondition WithMessage(Func<(T ExpectedValue, T ActualValue), string> messageFactory)
+ public IAssertCondition WithMessage(Func messageFactory)
{
MessageFactory = messageFactory;
return this;
diff --git a/TUnit.Assertions/AssertConditions/Generic/EqualsAssertCondition.cs b/TUnit.Assertions/AssertConditions/Generic/EqualsAssertCondition.cs
index 6490c8687d..1f4efbb591 100644
--- a/TUnit.Assertions/AssertConditions/Generic/EqualsAssertCondition.cs
+++ b/TUnit.Assertions/AssertConditions/Generic/EqualsAssertCondition.cs
@@ -1,19 +1,16 @@
namespace TUnit.Assertions.AssertConditions.Generic;
-public class EqualsAssertCondition : AssertCondition
+public class EqualsAssertCondition : ExpectedValueAssertCondition
{
- private readonly T _expected;
- public EqualsAssertCondition(T expected) : base(expected)
+ public EqualsAssertCondition(TExpected expected) : base(expected)
{
- _expected = expected;
}
- internal override Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
- = tuple => $"Expected {tuple.ExpectedValue} but received {tuple.ActualValue}";
+ public override string DefaultMessage => $"Expected {ExpectedValue} but received {ActualValue}";
- protected override bool Passes(T actualValue)
+ protected override bool Passes(TActual actualValue)
{
- return Equals(actualValue, _expected);
+ return Equals(actualValue, ExpectedValue);
}
}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/Generic/SameReferenceAssertCondition.cs b/TUnit.Assertions/AssertConditions/Generic/SameReferenceAssertCondition.cs
index 61105be483..937a072b15 100644
--- a/TUnit.Assertions/AssertConditions/Generic/SameReferenceAssertCondition.cs
+++ b/TUnit.Assertions/AssertConditions/Generic/SameReferenceAssertCondition.cs
@@ -1,19 +1,16 @@
namespace TUnit.Assertions.AssertConditions.Generic;
-public class SameReferenceAssertCondition : AssertCondition
+public class SameReferenceAssertCondition : ExpectedValueAssertCondition
{
- private readonly T _expected;
- public SameReferenceAssertCondition(T expected) : base(expected)
+ public SameReferenceAssertCondition(TExpected expected) : base(expected)
{
- _expected = expected;
}
- internal override Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
- = tuple => $"The two objects are different references.";
+ public override string DefaultMessage => "The two objects are different references.";
- protected override bool Passes(T actualValue)
+ protected override bool Passes(TActual actualValue)
{
- return ReferenceEquals(actualValue, _expected);
+ return ReferenceEquals(actualValue, ExpectedValue);
}
}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/Numbers/GreaterThanAssertCondition.cs b/TUnit.Assertions/AssertConditions/Numbers/GreaterThanAssertCondition.cs
index 651f1e21d3..6af5196336 100644
--- a/TUnit.Assertions/AssertConditions/Numbers/GreaterThanAssertCondition.cs
+++ b/TUnit.Assertions/AssertConditions/Numbers/GreaterThanAssertCondition.cs
@@ -1,17 +1,18 @@
using System.Numerics;
-namespace TUnit.Assertions;
+namespace TUnit.Assertions.AssertConditions.Numbers;
-public class GreaterThanAssertCondition : AssertCondition where T : INumber
+public class GreaterThanAssertCondition : ExpectedValueAssertCondition
+ where TExpected : INumber
+ where TActual : INumber, TExpected
{
- public GreaterThanAssertCondition(T expected) : base(expected)
+ public GreaterThanAssertCondition(TExpected expected) : base(expected)
{
}
- internal override Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
- = tuple => $"{tuple.ActualValue} is not greater than {tuple.ExpectedValue}";
+ public override string DefaultMessage => $"{ActualValue} is not greater than {ExpectedValue}";
- protected override bool Passes(T actualValue)
+ protected override bool Passes(TActual actualValue)
{
return actualValue > ExpectedValue;
}
diff --git a/TUnit.Assertions/AssertConditions/Numbers/GreaterThanOrEqualToAssertCondition.cs b/TUnit.Assertions/AssertConditions/Numbers/GreaterThanOrEqualToAssertCondition.cs
index 4c09dd36be..4a2d65edf7 100644
--- a/TUnit.Assertions/AssertConditions/Numbers/GreaterThanOrEqualToAssertCondition.cs
+++ b/TUnit.Assertions/AssertConditions/Numbers/GreaterThanOrEqualToAssertCondition.cs
@@ -1,17 +1,18 @@
using System.Numerics;
-namespace TUnit.Assertions;
+namespace TUnit.Assertions.AssertConditions.Numbers;
-public class GreaterThanOrEqualToAssertCondition : AssertCondition where T : INumber
+public class GreaterThanOrEqualToAssertCondition : ExpectedValueAssertCondition
+ where TExpected : INumber
+ where TActual : INumber, TExpected
{
- public GreaterThanOrEqualToAssertCondition(T expected) : base(expected)
+ public GreaterThanOrEqualToAssertCondition(TExpected expected) : base(expected)
{
}
- internal override Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
- = tuple => $"{tuple.ActualValue} is not greater than or equal to {tuple.ExpectedValue}";
+ public override string DefaultMessage => $"{ActualValue} is not greater than or equal to {ExpectedValue}";
- protected override bool Passes(T actualValue)
+ protected override bool Passes(TActual actualValue)
{
return actualValue >= ExpectedValue;
}
diff --git a/TUnit.Assertions/AssertConditions/Numbers/IsEvenAssertCondition.cs b/TUnit.Assertions/AssertConditions/Numbers/IsEvenAssertCondition.cs
new file mode 100644
index 0000000000..7b29d0e2b6
--- /dev/null
+++ b/TUnit.Assertions/AssertConditions/Numbers/IsEvenAssertCondition.cs
@@ -0,0 +1,19 @@
+using System.Numerics;
+
+namespace TUnit.Assertions.AssertConditions.Numbers;
+
+public class IsEvenAssertCondition : ExpectedValueAssertCondition
+ where TExpected : INumber
+ where TActual : INumber, TExpected
+{
+ public IsEvenAssertCondition(TExpected expected) : base(expected)
+ {
+ }
+
+ public override string DefaultMessage => $"{ActualValue} is not greater than {ExpectedValue}";
+
+ protected override bool Passes(TActual actualValue)
+ {
+ return actualValue > ExpectedValue;
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/AssertConditions/Numbers/LessThanAssertCondition.cs b/TUnit.Assertions/AssertConditions/Numbers/LessThanAssertCondition.cs
index 411786edaf..bfc5a595c7 100644
--- a/TUnit.Assertions/AssertConditions/Numbers/LessThanAssertCondition.cs
+++ b/TUnit.Assertions/AssertConditions/Numbers/LessThanAssertCondition.cs
@@ -1,17 +1,18 @@
using System.Numerics;
-namespace TUnit.Assertions;
+namespace TUnit.Assertions.AssertConditions.Numbers;
-public class LessThanAssertCondition : AssertCondition where T : INumber
+public class LessThanAssertCondition : ExpectedValueAssertCondition
+ where TExpected : INumber
+ where TActual : INumber, TExpected
{
- public LessThanAssertCondition(T expected) : base(expected)
+ public LessThanAssertCondition(TExpected expected) : base(expected)
{
}
- internal override Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
- = tuple => $"{tuple.ActualValue} is not less than {tuple.ExpectedValue}";
+ public override string DefaultMessage => $"{ActualValue} is not less than {ExpectedValue}";
- protected override bool Passes(T actualValue)
+ protected override bool Passes(TActual actualValue)
{
return actualValue < ExpectedValue;
}
diff --git a/TUnit.Assertions/AssertConditions/Numbers/LessThanOrEqualToAssertCondition.cs b/TUnit.Assertions/AssertConditions/Numbers/LessThanOrEqualToAssertCondition.cs
index 0bed6eaae8..4d27b9a86e 100644
--- a/TUnit.Assertions/AssertConditions/Numbers/LessThanOrEqualToAssertCondition.cs
+++ b/TUnit.Assertions/AssertConditions/Numbers/LessThanOrEqualToAssertCondition.cs
@@ -1,17 +1,18 @@
using System.Numerics;
-namespace TUnit.Assertions;
+namespace TUnit.Assertions.AssertConditions.Numbers;
-public class LessThanOrEqualToAssertCondition : AssertCondition where T : INumber
+public class LessThanOrEqualToAssertCondition : ExpectedValueAssertCondition
+ where TExpected : INumber
+ where TActual : INumber, TExpected
{
- public LessThanOrEqualToAssertCondition(T expected) : base(expected)
+ public LessThanOrEqualToAssertCondition(TExpected expected) : base(expected)
{
}
- internal override Func<(T ExpectedValue, T ActualValue), string> MessageFactory { get; set; }
- = tuple => $"{tuple.ActualValue} is not less than or equal to {tuple.ExpectedValue}";
+ public override string DefaultMessage => $"{ActualValue} is not less than or equal to {ExpectedValue}";
- protected override bool Passes(T actualValue)
+ protected override bool Passes(TActual actualValue)
{
return actualValue <= ExpectedValue;
}
diff --git a/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs b/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs
index eb6064252b..887266c104 100644
--- a/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs
+++ b/TUnit.Assertions/AssertConditions/String/StringEqualsAssertCondition.cs
@@ -1,6 +1,6 @@
namespace TUnit.Assertions.AssertConditions.String;
-public class StringEqualsAssertCondition : AssertCondition
+public class StringEqualsAssertCondition : ExpectedValueAssertCondition
{
private readonly StringComparison _stringComparison;
@@ -13,9 +13,8 @@ protected override bool Passes(string actualValue)
{
return string.Equals(actualValue, ExpectedValue, _stringComparison);
}
-
- internal override Func<(string ExpectedValue, string ActualValue), string> MessageFactory { get; set; }
- = tuple => $"""
- Expected "{tuple.ExpectedValue}" but received "{tuple.ActualValue}"
- """;
+
+ public override string DefaultMessage => $"""
+ Expected "{ExpectedValue}" but received "{ActualValue}"
+ """;
}
\ No newline at end of file
diff --git a/TUnit.Assertions/ExpectedValueAssertCondition.cs b/TUnit.Assertions/ExpectedValueAssertCondition.cs
new file mode 100644
index 0000000000..bdff7ea179
--- /dev/null
+++ b/TUnit.Assertions/ExpectedValueAssertCondition.cs
@@ -0,0 +1,21 @@
+namespace TUnit.Assertions;
+
+public abstract class ExpectedValueAssertCondition : AssertCondition
+{
+ internal TExpected ExpectedValue { get; }
+
+ internal ExpectedValueAssertCondition(TExpected expected)
+ {
+ ExpectedValue = expected;
+ }
+
+ public new string Message => MessageFactory?.Invoke((ExpectedValue, ActualValue)) ?? DefaultMessage;
+
+ private Func<(TExpected ExpectedValue, TActual ActualValue), string>? MessageFactory { get; set; }
+
+ public IAssertCondition WithMessage(Func<(TExpected ExpectedValue, TActual ActualValue), string> messageFactory)
+ {
+ MessageFactory = messageFactory!;
+ return this;
+ }
+}
\ No newline at end of file
diff --git a/TUnit.Assertions/IAssertCondition.cs b/TUnit.Assertions/IAssertCondition.cs
index 7790086834..1da65b17cf 100644
--- a/TUnit.Assertions/IAssertCondition.cs
+++ b/TUnit.Assertions/IAssertCondition.cs
@@ -4,5 +4,5 @@ public interface IAssertCondition
{
public bool Assert(T actualValue);
- internal string Message { get; }
+ internal string DefaultMessage { get; }
}
\ No newline at end of file
diff --git a/TUnit.Assertions/Is.cs b/TUnit.Assertions/Is.cs
index 004b56d802..0a204f0845 100644
--- a/TUnit.Assertions/Is.cs
+++ b/TUnit.Assertions/Is.cs
@@ -6,11 +6,11 @@ public static partial class Is
{
public static AssertCondition EqualTo(T expected)
{
- return new EqualsAssertCondition(expected);
+ return new EqualsAssertCondition