Skip to content

Commit

Permalink
resolves #166 🚜✨ πŸ”¬βœ¨
Browse files Browse the repository at this point in the history
  • Loading branch information
BryanWilhite committed Feb 7, 2024
1 parent b4502b2 commit 04c19f0
Show file tree
Hide file tree
Showing 11 changed files with 222 additions and 96 deletions.
11 changes: 11 additions & 0 deletions SonghayCore.OrderedTests/Assembly.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
ο»Ώglobal using System.Reflection;

global using Xunit;
global using Xunit.Abstractions;
global using Xunit.Sdk;

global using Songhay.Extensions;
global using Songhay.Tests.Orderers;

[assembly: CollectionBehavior(DisableTestParallelization = true)]
[assembly: TestCaseOrderer(TestCaseOrderer.TypeName, ordererAssemblyName: TestCaseOrderer.AssemblyName)]
47 changes: 47 additions & 0 deletions SonghayCore.OrderedTests/Orderers/OrderedTestBaseFailureTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace Songhay.Tests.Orderers;

public class OrderedTestBaseFailureTests : OrderedTestBase
{
static readonly List<string> MethodCalls = new();

static int _currentValue = 32;

public OrderedTestBaseFailureTests(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}

[Fact, TestOrder(ordinal: 0, reason: "Subtract 8")]
public void ShouldSubtract8()
{
AssertNoXUnitException();
MethodCalls.Add(nameof(ShouldSubtract8));

const int wrongValue = 42;

_currentValue -= wrongValue;

Assert.Equal(24, _currentValue);
}

[Fact, TestOrder(ordinal: 1, reason: "Subtract 4")]
public void ShouldSubtract4()
{
Assert.True(XUnitExceptionHasOccurred);

MethodCalls.Add(nameof(ShouldSubtract4));
}

[Fact, TestOrder(ordinal: 2, reason: "Subtract 4 again")]
public void ShouldSubtract4Again()
{
Assert.True(XUnitExceptionHasOccurred);

MethodCalls.Add(nameof(ShouldSubtract4Again));

_testOutputHelper.WriteLine("method calls:");
MethodCalls.ForEachInEnumerable(m => _testOutputHelper.WriteLine(m));
}

readonly ITestOutputHelper _testOutputHelper;
}
54 changes: 54 additions & 0 deletions SonghayCore.OrderedTests/Orderers/OrderedTestBaseTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Songhay.Extensions;

namespace Songhay.Tests.Orderers;

public class OrderedTestBaseTests : OrderedTestBase
{
const int ExpectedResult = 16;

static readonly List<string> MethodCalls = new();

static int _currentValue = 32;

public OrderedTestBaseTests(ITestOutputHelper testOutputHelper)
{
AssertNoXUnitException();

_testOutputHelper = testOutputHelper;
}

[Fact, TestOrder(ordinal: 0, reason: "Subtract 8")]
public void ShouldSubtract8()
{
_currentValue -= 8;

Assert.Equal(24, _currentValue);

MethodCalls.Add(nameof(ShouldSubtract8));
}

[Fact, TestOrder(ordinal: 1, reason: "Subtract 4")]
public void ShouldSubtract4()
{
_currentValue -= 4;

Assert.Equal(20, _currentValue);

MethodCalls.Add(nameof(ShouldSubtract4));
}

[Fact, TestOrder(ordinal: 2, reason: "Subtract 4 again")]
public void ShouldSubtract4Again()
{
_currentValue -= 4;

Assert.Equal(ExpectedResult, _currentValue);

MethodCalls.Add(nameof(ShouldSubtract4Again));

_testOutputHelper.WriteLine("method calls:");
MethodCalls.ForEachInEnumerable(m => _testOutputHelper.WriteLine(m));
}

readonly ITestOutputHelper _testOutputHelper;
}
38 changes: 38 additions & 0 deletions SonghayCore.OrderedTests/SonghayCore.OrderedTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>

<IsPackable>false</IsPackable>

<RootNamespace>Songhay.Tests</RootNamespace>

<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

</PropertyGroup>

<ItemGroup>
<Compile Include="..\SonghayCore.xUnit\Orderers\OrderedTestBase.cs" Link="Orderers\OrderedTestBase.cs"/>
<Compile Include="..\SonghayCore.xUnit\Orderers\TestCaseOrderer.cs" Link="Orderers\TestCaseOrderer.cs"/>
<Compile Include="..\SonghayCore.xUnit\Orderers\TestOrderAttribute.cs" zLink="Orderers\TestOrderAttribute.cs"/>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0"/>
<PackageReference Include="xunit" Version="2.4.1"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\SonghayCore\SonghayCore.csproj"/>
</ItemGroup>

</Project>
9 changes: 0 additions & 9 deletions SonghayCore.Tests/Assembly.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,7 @@
ο»Ώglobal using System.Diagnostics;
global using Songhay.Extensions;
global using Songhay.Tests.Orderers;
global using System.Reflection;
global using System.Text.Json;
global using Xunit;
global using Xunit.Abstractions;
global using Xunit.Sdk;

[assembly: CollectionBehavior(DisableTestParallelization = true)]
[assembly: TestCaseOrderer(TestCaseOrderer.TypeName, ordererAssemblyName: "SonghayCore.Tests")]
/*
FUNKYKB: 🚧 Note that when the real `SonghayCore.xUnit` package is in use
the `orderAssemblyName` value should be `TestCaseOrderer.AssemblyName`
instead of this project, linking to `SonghayCore.xUnit` external files.
*/
53 changes: 0 additions & 53 deletions SonghayCore.Tests/Orderers/TestCaseOrdererTests.cs

This file was deleted.

3 changes: 0 additions & 3 deletions SonghayCore.Tests/SonghayCore.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@
<ItemGroup>
<Compile Include="..\SonghayCore.xUnit\Extensions\DataAttributeExtensions.cs" Link="Extensions\DataAttributeExtensions.cs"/>
<Compile Include="..\SonghayCore.xUnit\Extensions\IServiceProviderExtensions.cs" Link="Extensions\IServiceProviderExtensions.cs"/>
<Compile Include="..\SonghayCore.xUnit\Orderers\OrderedTestBase.cs" Link="Orderers\OrderedTestBase.cs"/>
<Compile Include="..\SonghayCore.xUnit\Orderers\TestCaseOrderer.cs" Link="Orderers\TestCaseOrderer.cs"/>
<Compile Include="..\SonghayCore.xUnit\Orderers\TestOrderAttribute.cs" zLink="Orderers\TestOrderAttribute.cs"/>
<Compile Include="..\SonghayCore.xUnit\DebuggerAttachedFactAttribute.cs" Link="DebuggerAttachedFactAttribute.cs"/>
<Compile Include="..\SonghayCore.xUnit\DebuggerAttachedTheoryAttribute.cs" Link="DebuggerAttachedTheoryAttribute.cs"/>
<Compile Include="..\SonghayCore.xUnit\ProjectFileDataAttribute.cs" Link="ProjectFileDataAttribute.cs"/>
Expand Down
6 changes: 6 additions & 0 deletions SonghayCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Statiq.Docs.Shell", "Statiq
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SonghayCore.xUnit", "SonghayCore.xUnit\SonghayCore.xUnit.csproj", "{3A24B829-D17B-45C5-BD1E-18D1A497D048}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SonghayCore.OrderedTests", "SonghayCore.OrderedTests\SonghayCore.OrderedTests.csproj", "{3F7DD844-F9E8-4B04-A865-C51B63782BA9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -39,6 +41,10 @@ Global
{3A24B829-D17B-45C5-BD1E-18D1A497D048}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A24B829-D17B-45C5-BD1E-18D1A497D048}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A24B829-D17B-45C5-BD1E-18D1A497D048}.Release|Any CPU.Build.0 = Release|Any CPU
{3F7DD844-F9E8-4B04-A865-C51B63782BA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3F7DD844-F9E8-4B04-A865-C51B63782BA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F7DD844-F9E8-4B04-A865-C51B63782BA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F7DD844-F9E8-4B04-A865-C51B63782BA9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
73 changes: 53 additions & 20 deletions SonghayCore.xUnit/Orderers/OrderedTestBase.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,60 @@
ο»Ώnamespace Songhay.Tests.Orderers
ο»Ώnamespace Songhay.Tests.Orderers;

/// <summary>
/// Provides ordered test assertions.
/// </summary>
/// <remarks>
/// For more detail, see β€œHow to Order xUnit Tests and Collections” by Tom DuPont
/// [http://www.tomdupont.net/2016/04/how-to-order-xunit-tests-and-collections.html]
/// </remarks>
[TestCaseOrderer(TestCaseOrderer.TypeName, TestCaseOrderer.AssemblyName)]
public abstract class OrderedTestBase
{
/// <summary>
/// Provides ordered test assertions.
/// Initializes a new instance of the <see cref="OrderedTestBase"/> class.
/// </summary>
/// <remarks>
/// For more detail, see β€œHow to Order xUnit Tests and Collections” by Tom DuPont
/// [http://www.tomdupont.net/2016/04/how-to-order-xunit-tests-and-collections.html]
/// </remarks>
[TestCaseOrderer(TestCaseOrderer.TypeName, TestCaseOrderer.AssemblyName)]
public abstract class OrderedTestBase
protected OrderedTestBase()
{
/// <summary>
/// The expected ordinal of the current test.
/// </summary>
protected static int TestOrdinal;

protected void AssertTestName(string testName)
AppDomain.CurrentDomain.FirstChanceException += (_, e) =>
{
var type = GetType();
var queue = TestCaseOrderer.QueuedTests[type.FullName!];
var result = queue.TryDequeue(out string? dequeuedName);
Assert.True(result);
Assert.Equal(testName, dequeuedName);
}
if (XUnitExceptionHasOccurred || !IsXunitException(e.Exception)) return;
XUnitExceptionHasOccurred = true;
_lastException = e.Exception;
};
}

/// <summary>
/// Returns <c>true</c> when an XUnit assertion exception has occurred.
/// </summary>
protected static bool XUnitExceptionHasOccurred { get; private set; }

/// <summary>
/// Asserts there are no exceptions of type <c>xUnit.Sdk.*</c>
/// to prevent ordered tests from running.
/// </summary>
/// <remarks>
/// See https://github.com/xunit/xunit/issues/856
/// </remarks>
protected static void AssertNoXUnitException() =>
Assert.False(XUnitExceptionHasOccurred,
$"Assertion Failed: An exception has occurred under test [message: `{_lastException?.Message}`].");

static Exception? _lastException;

/// <summary>
/// Determines whether the specified <see cref="Exception"/> is from an xUnit assertion.
/// </summary>
/// <param name="ex">The ex.</param>
/// <returns>
/// <c>true</c> if it is a xUnit <see cref="Exception"/>; otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// <see cref="AppDomain.FirstChanceException"/> will detect ALL exceptions,
/// including those NOT on the current path of execution!
///
/// This means a huge amount of exceptions can pass through <see cref="AppDomain.FirstChanceException"/>.
/// </remarks>
static bool IsXunitException(Exception? ex) =>
ex?.GetType().FullName?.ToLowerInvariant().StartsWith("xunit.sdk") == true;
}
8 changes: 5 additions & 3 deletions SonghayCore.xUnit/Orderers/TestCaseOrderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ public class TestCaseOrderer : ITestCaseOrderer
public const string TypeName = "Songhay.Tests.Orderers.TestCaseOrderer";

/// <summary>The queued tests</summary>
public static readonly ConcurrentDictionary<string, ConcurrentQueue<string>>
QueuedTests = new();
public static readonly ConcurrentDictionary<string, ConcurrentQueue<string>> QueuedTests = new();

/// <summary>Orders test cases for execution.</summary>
/// <typeparam name="TTestCase"></typeparam>
Expand All @@ -30,7 +29,9 @@ public static readonly ConcurrentDictionary<string, ConcurrentQueue<string>>
public IEnumerable<TTestCase> OrderTestCases<TTestCase>(IEnumerable<TTestCase> testCases)
where TTestCase : ITestCase
{
return testCases.OrderBy(GetOrder);
TTestCase[] orderedCases = testCases.OrderBy(GetOrder).ToArray();

return orderedCases;
}

static int GetOrder<TTestCase>(TTestCase testCase) where TTestCase : ITestCase
Expand All @@ -44,6 +45,7 @@ static int GetOrder<TTestCase>(TTestCase testCase) where TTestCase : ITestCase
var attr = testCase.TestMethod.Method
.ToRuntimeMethod()
.GetCustomAttribute<TestOrderAttribute>();

return attr?.Ordinal ?? 0;
}
}
16 changes: 8 additions & 8 deletions SonghayCore.xUnit/Orderers/TestOrderAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,6 @@
/// <seealso cref="Attribute" />
public class TestOrderAttribute : Attribute
{
/// <summary>Gets the ordinal.</summary>
/// <value>The ordinal.</value>
public int Ordinal { get; }

/// <summary>Gets the reason for choosing the order.</summary>
/// <value>The reason.</value>
public string? Reason { get; }

/// <summary>Initializes a new instance of the <see cref="TestOrderAttribute"/> class.</summary>
/// <param name="ordinal">The ordinal.</param>
public TestOrderAttribute(int ordinal) => Ordinal = ordinal;
Expand All @@ -27,4 +19,12 @@ public TestOrderAttribute(int ordinal, string? reason)
Ordinal = ordinal;
Reason = reason;
}

/// <summary>Gets the ordinal.</summary>
/// <value>The ordinal.</value>
public int Ordinal { get; }

/// <summary>Gets the reason for choosing the order.</summary>
/// <value>The reason.</value>
public string? Reason { get; }
}

0 comments on commit 04c19f0

Please sign in to comment.