From 4a55fce62a00697e72c306206c75bc70db5ae61c Mon Sep 17 00:00:00 2001 From: Maxim <17935127+delatrie@users.noreply.github.com> Date: Thu, 6 Jul 2023 16:07:58 +0700 Subject: [PATCH] Replace allure-xunit's custom attributes with runner reporter (fixes #344) (#366) * Add support for native xunit attributes * Fix AllureXunitTheoryAttribute base class * Fix missing theories * Fix missing parameters and fixture duplication for some tests * Add support for static test functions * Update xunit examples. Obsolete allure-xunit attributes * Update message sink to match new steps implementation * Remove irrelevant log method from runner * Bump harmony to 2.3-prerelease.2. Warn if no args reported Change Lib.Harmony dependency to 2.3-prerelease.2 for .net 7 support. Add warning if the patch via harmony didn't work for theory args and args aren't reported on the testcase level. * Fix reference to lib.harmony to be listed by dotnet * Factor out allure-related code. Tidying up obsolescence - Allure-xunit's code that creates, updates, starts, stops and writes allure objects is factored out from AllureMessageSink to AllureXunitHelper . - Removal of AllureXunitHelper's public methods was reverted. The methods are marked as obsolete. - Add missing await to an allure-xunit example to suppress the compilation warning. - Mark obsoleted allure-xunit attributes as non-browsable to prevent them from appearing in IDE's suggestions. * Add deprecation info and known issues to allure-xunit README * Fix issue links in allure-xunit readme * Fix obsolescence message for AllureXunitHelper's public methods --- .../ExampleParameterisedTests.cs | 25 +- .../ExampleStepAttributes.cs | 12 +- Allure.XUnit.Examples/ExampleSteps.cs | 16 +- Allure.XUnit.Examples/ExampleUnitTests.cs | 19 +- Allure.XUnit/Allure.XUnit.csproj | 2 + Allure.XUnit/AllureMessageBus.cs | 54 --- Allure.XUnit/AllureMessageSink.cs | 175 +++++++ Allure.XUnit/AllureRunnerReporter.cs | 24 + Allure.XUnit/AllureXunitDiscover.cs | 25 - Allure.XUnit/AllureXunitHelper.cs | 448 ++++++++++++------ Allure.XUnit/AllureXunitPatcher.cs | 97 ++++ Allure.XUnit/AllureXunitTestCase.cs | 128 ----- Allure.XUnit/AllureXunitTestResultAccessor.cs | 14 + Allure.XUnit/AllureXunitTheoryDiscover.cs | 47 -- .../Attributes/AllureXunitAttribute.cs | 8 +- .../Attributes/AllureXunitTheoryAttribute.cs | 8 +- Allure.XUnit/README.md | 73 ++- Allure.XUnit/Steps.cs | 2 +- 18 files changed, 736 insertions(+), 441 deletions(-) delete mode 100644 Allure.XUnit/AllureMessageBus.cs create mode 100644 Allure.XUnit/AllureMessageSink.cs create mode 100644 Allure.XUnit/AllureRunnerReporter.cs delete mode 100644 Allure.XUnit/AllureXunitDiscover.cs create mode 100644 Allure.XUnit/AllureXunitPatcher.cs delete mode 100644 Allure.XUnit/AllureXunitTestCase.cs create mode 100644 Allure.XUnit/AllureXunitTestResultAccessor.cs delete mode 100644 Allure.XUnit/AllureXunitTheoryDiscover.cs diff --git a/Allure.XUnit.Examples/ExampleParameterisedTests.cs b/Allure.XUnit.Examples/ExampleParameterisedTests.cs index 131bfc00..520bc158 100644 --- a/Allure.XUnit.Examples/ExampleParameterisedTests.cs +++ b/Allure.XUnit.Examples/ExampleParameterisedTests.cs @@ -9,13 +9,12 @@ namespace Allure.XUnit.Examples { public class ExampleParameterisedTests { - public ExampleParameterisedTests() { Environment.CurrentDirectory = Path.GetDirectoryName(GetType().Assembly.Location); } - [AllureXunitTheory] + [Theory] [AllureParentSuite("AllTests")] [AllureSuite("Test AllureXunitTheory")] [AllureSubSuite("Test MemberData")] @@ -23,11 +22,11 @@ public ExampleParameterisedTests() public void TestTheoryWithMemberDataProperty(int value1, int value2, int expected) { var result = value1 + value2; - + Assert.Equal(expected, result); } - - [AllureXunitTheory] + + [Theory] [AllureParentSuite("AllTests")] [AllureSuite("Test AllureXunitTheory")] [AllureSubSuite("Test ClassData")] @@ -35,11 +34,11 @@ public void TestTheoryWithMemberDataProperty(int value1, int value2, int expecte public void TestTheoryWithClassData(int value1, int value2, int expected) { var result = value1 + value2; - + Assert.Equal(expected, result); } - - [AllureXunitTheory] + + [Theory] [AllureParentSuite("AllTests")] [AllureSuite("Test AllureXunitTheory")] [AllureSubSuite("Test InlineData")] @@ -49,8 +48,8 @@ public void TestTheory(int a, int b) { Assert.Equal(a, b); } - - [AllureXunitTheory] + + [Theory] [AllureParentSuite("AllTests")] [AllureSuite("Test AllureXunitTheory")] [AllureSubSuite("Test MemberData with Custom Reference Type")] @@ -60,8 +59,8 @@ public void TestTheoryWithMemberData(MyTestClass a, MyTestClass b) { Assert.Equal(a.Test, b.Test); } - - [AllureXunitTheory] + + [Theory] [AllureParentSuite("AllTests")] [AllureSuite("Test AllureXunitTheory")] [AllureSubSuite("Test test MemberData with random data")] @@ -73,7 +72,7 @@ public void TestTheoryWithMemberDataThatReturnsRandomData(int value1, int value2 Assert.Equal(expected, result); } - [AllureXunitTheory] + [Theory] [AllureParentSuite("AllTests")] [AllureSuite("Test AllureXunitTheory")] [AllureSubSuite("Test test with generic arguments")] diff --git a/Allure.XUnit.Examples/ExampleStepAttributes.cs b/Allure.XUnit.Examples/ExampleStepAttributes.cs index daabb6aa..fc960f70 100644 --- a/Allure.XUnit.Examples/ExampleStepAttributes.cs +++ b/Allure.XUnit.Examples/ExampleStepAttributes.cs @@ -16,7 +16,7 @@ public ExampleStepAttributes() NestedStepReturningString("Second"); } - [AllureXunit] + [Fact] public void Test() { WriteHelloStep(42, 4242, "secret"); @@ -25,21 +25,21 @@ public void Test() SomeStep(); } - [AllureXunit] + [Fact] public void TestSecond() { SomeStep(); } - [AllureXunit(DisplayName = "This test should be red")] + [Fact(DisplayName = "This test should be red")] public void TestFailed() { SomeStep(); FailingStep(); SomeStep(); } - - [AllureXunit(DisplayName = "This test should be yellow")] + + [Fact(DisplayName = "This test should be yellow")] public void TestBroken() { SomeStep(); @@ -84,7 +84,7 @@ private void FailingStep() { Assert.True(false); } - + [AllureStep("Check that everytime you call this step it will throw an exception")] private void ExceptionStep() { diff --git a/Allure.XUnit.Examples/ExampleSteps.cs b/Allure.XUnit.Examples/ExampleSteps.cs index 99307ed6..2fdf9b7a 100644 --- a/Allure.XUnit.Examples/ExampleSteps.cs +++ b/Allure.XUnit.Examples/ExampleSteps.cs @@ -3,6 +3,7 @@ using Allure.Xunit; using Allure.Xunit.Attributes; using Xunit; +using Xunit.Abstractions; namespace Allure.XUnit.Examples; @@ -10,6 +11,13 @@ namespace Allure.XUnit.Examples; [AllureSuite("ExampleSteps (Obsolete)")] public class ExampleSteps : IAsyncLifetime { + ITestOutputHelper output; + + public ExampleSteps(ITestOutputHelper output) + { + this.output = output; + } + public Task InitializeAsync() { using (new AllureBefore("Initialization")) @@ -27,14 +35,14 @@ public Task DisposeAsync() return Task.CompletedTask; } - [AllureXunit(Skip = "ExampleSteps is obsolete")] + [Fact(Skip = "ExampleSteps is obsolete")] public async Task TestParameters() { WriteHello(42, 4242, "secret"); await AddAttachment(); } - [AllureXunit(Skip = "ExampleSteps is obsolete")] + [Fact(Skip = "ExampleSteps is obsolete")] public void TestFail() { using (new AllureStep("Test Fail")) @@ -46,11 +54,11 @@ public void TestFail() } } - private static void WriteHello(int parameter, int renameMe, string password) + private void WriteHello(int parameter, int renameMe, string password) { using (new AllureStep("Write Hello").SetParameter(parameter).SetParameter("value", renameMe)) { - AllureMessageBus.TestOutputHelper.Value.WriteLine("Hello from Step"); + this.output.WriteLine("Hello from Step"); } } diff --git a/Allure.XUnit.Examples/ExampleUnitTests.cs b/Allure.XUnit.Examples/ExampleUnitTests.cs index f1fad84e..2741bf25 100644 --- a/Allure.XUnit.Examples/ExampleUnitTests.cs +++ b/Allure.XUnit.Examples/ExampleUnitTests.cs @@ -28,8 +28,8 @@ public ExampleUnitTests() { Environment.CurrentDirectory = Path.GetDirectoryName(GetType().Assembly.Location); } - - [AllureXunit(DisplayName = "Test that 1 is not equals 1")] + + [Fact(DisplayName = "Test that 1 is not equals 1")] [AllureDescription("My long test description; Lorem ipsum dolor sit amet.")] [AllureFeature("qwerty", "123")] [AllureTag("TAG-1")] @@ -41,28 +41,35 @@ public void Test1() } - [AllureXunit(DisplayName = "Test that 1 is equals 1")] + [Fact(DisplayName = "Test that 1 is equals 1")] [AllureSeverity(SeverityLevel.critical)] public async Task Test2() { + await Task.Delay(0); Assert.True(1 == 1); Attachments.File("allureConfig", @"./allureConfig.json"); } - [AllureXunit(DisplayName = "Another Test")] + [Fact(DisplayName = "Another Test")] public void Test3() { Assert.Empty(new List() {1, 2, 3}); } - [AllureXunit] + [Fact] [AllureTag(new[] {"TAG-8", "TAG-9", "TAG-10"}, true)] public void TestMultipleTagsWithOverwrite() { Assert.True(!false); } - [AllureXunit(DisplayName = "Test mapped to existing test case #1 in allure")] + [Fact] + public static void StaticTest() + { + Assert.True(true); + } + + [Fact(DisplayName = "Test mapped to existing test case #1 in allure")] [AllureId("1")] public void TestAllureIdMapping() { diff --git a/Allure.XUnit/Allure.XUnit.csproj b/Allure.XUnit/Allure.XUnit.csproj index 110bb619..c0252748 100644 --- a/Allure.XUnit/Allure.XUnit.csproj +++ b/Allure.XUnit/Allure.XUnit.csproj @@ -22,7 +22,9 @@ + + diff --git a/Allure.XUnit/AllureMessageBus.cs b/Allure.XUnit/AllureMessageBus.cs deleted file mode 100644 index 26111058..00000000 --- a/Allure.XUnit/AllureMessageBus.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Threading; -using Allure.Xunit; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Allure.XUnit -{ - public class AllureMessageBus : IMessageBus - { - public static readonly AsyncLocal TestOutputHelper = new(); - private readonly IMessageBus _inner; - - public AllureMessageBus(IMessageBus inner) - { - _inner = inner; - } - - public void Dispose() => _inner.Dispose(); - - public bool QueueMessage(IMessageSinkMessage message) - { - switch (message) - { - case ITestStarting testStarting: - var testOutputHelper = new TestOutputHelper(); - testOutputHelper.Initialize(this, testStarting.Test); - TestOutputHelper.Value = testOutputHelper; - break; - case ITestCaseStarting testCaseStarting: - AllureXunitHelper.StartTestContainer(testCaseStarting); - break; - case ITestClassConstructionFinished testClassConstructionFinished: - AllureXunitHelper.StartTestCase(testClassConstructionFinished); - break; - case ITestFailed testFailed: - AllureXunitHelper.MarkTestCaseAsFailedOrBroken(testFailed); - break; - case ITestPassed testPassed: - AllureXunitHelper.MarkTestCaseAsPassed(testPassed); - break; - case ITestCaseFinished testCaseFinished: - if (testCaseFinished.TestCase.SkipReason != null) - { - AllureXunitHelper.StartTestCase(testCaseFinished); - AllureXunitHelper.MarkTestCaseAsSkipped(testCaseFinished); - } - AllureXunitHelper.FinishTestCase(testCaseFinished); - break; - } - - return _inner.QueueMessage(message); - } - } -} \ No newline at end of file diff --git a/Allure.XUnit/AllureMessageSink.cs b/Allure.XUnit/AllureMessageSink.cs new file mode 100644 index 00000000..248f2e7d --- /dev/null +++ b/Allure.XUnit/AllureMessageSink.cs @@ -0,0 +1,175 @@ +using Allure.Net.Commons; +using Allure.Net.Commons.Steps; +using Allure.Xunit; +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit; +using Xunit.Abstractions; + +namespace Allure.XUnit +{ + public class AllureMessageSink : TestMessageSink + { + IRunnerLogger logger; + Dictionary allureTestData = new(); + + public AllureMessageSink(IRunnerLogger logger) + { + this.logger = logger; + + this.Runner.TestAssemblyExecutionStartingEvent += + this.OnTestAssemblyExecutionStarting; + + this.Execution.TestStartingEvent += this.OnTestStarting; + this.Execution.TestClassConstructionFinishedEvent += + this.OnTestClassConstructionFinished; + this.Execution.TestFailedEvent += this.OnTestFailed; + this.Execution.TestPassedEvent += this.OnTestPassed; + this.Execution.TestFinishedEvent += this.OnTestFinished; + this.Execution.TestCaseFinishedEvent+= this.OnTestCaseFinished; + } + + void OnTestAssemblyExecutionStarting( + MessageHandlerArgs args + ) + { + args.Message.ExecutionOptions.SetSynchronousMessageReporting(true); + } + + internal void OnTestArgumentsCreated(ITest test, object[] arguments) + { + var accessor = this.GetOrCreateAllureResultAccessor(test); + accessor.Arguments = arguments; + } + + void OnTestStarting(MessageHandlerArgs args) + { + var message = args.Message; + var test = message.Test; + var accessor = this.GetOrCreateAllureResultAccessor(test); + + CoreStepsHelper.TestResultAccessor = accessor; + + if (message.TestMethod.Method.IsStatic) + { + accessor.TestResult = AllureXunitHelper.StartStaticAllureTestCase(test); + } + else + { + accessor.TestResultContainer = AllureXunitHelper.StartNewAllureContainer( + message.TestClass.Class.Name + ); + } + } + + void OnTestClassConstructionFinished( + MessageHandlerArgs args + ) + { + var test = args.Message.Test; + var accessor = this.allureTestData[test]; + var container = accessor.TestResultContainer; + if (accessor.TestResult is null && container is not null) + { + accessor.TestResult = AllureXunitHelper.StartAllureTestCase( + test, + container + ); + } + } + + void OnTestFailed(MessageHandlerArgs args) + { + var message = args.Message; + var test = message.Test; + var testResult = this.allureTestData[test].TestResult; + + if (testResult is not null) + { + AllureXunitHelper.ApplyTestFailure(testResult, message); + } + } + + void OnTestPassed(MessageHandlerArgs args) + { + var message = args.Message; + var test = message.Test; + var testResult = this.allureTestData[test].TestResult; + + if (testResult is not null) + { + AllureXunitHelper.ApplyTestSuccess(testResult, message); + } + } + + void OnTestFinished(MessageHandlerArgs args) + { + var test = args.Message.Test; + var accessor = this.allureTestData[test]; + this.allureTestData.Remove(test); + var testResult = accessor.TestResult; + if (testResult is not null) + { + this.AddAllureParameters(testResult, test, accessor.Arguments); + AllureXunitHelper.ReportTestCase(testResult); + + var container = accessor.TestResultContainer; + if (container is not null) + { + AllureXunitHelper.ReportTestContainer(container); + } + } + } + + void OnTestCaseFinished(MessageHandlerArgs args) + { + var testCase = args.Message.TestCase; + if (testCase.SkipReason != null) + { + AllureXunitHelper.ReportSkippedTestCase(testCase); + } + } + + AllureXunitTestResultAccessor GetOrCreateAllureResultAccessor(ITest test) + { + if (!this.allureTestData.TryGetValue(test, out var accessor)) + { + accessor = new AllureXunitTestResultAccessor(); + this.allureTestData[test] = accessor; + } + return accessor; + } + + void AddAllureParameters( + TestResult testResult, + ITest test, + object[] arguments + ) + { + var testCase = test.TestCase; + var parameters = testCase.TestMethod.Method.GetParameters(); + arguments ??= testCase.TestMethodArguments ?? Array.Empty(); + + if (parameters.Any() && !arguments.Any()) + { + this.LogUnreportedTheoryArgs(test.DisplayName); + } + else + { + AllureXunitHelper.ApplyTestParameters(testResult, parameters, arguments); + } + } + + void LogUnreportedTheoryArgs(string testName) + { + var message = $"Unable to attach arguments of {testName} to " + + "allure report"; +#if !DEBUG + message += ". You may try to compile the project in debug mode " + + "as a workaround"; +#endif + this.logger.LogWarning(message); + } + } +} \ No newline at end of file diff --git a/Allure.XUnit/AllureRunnerReporter.cs b/Allure.XUnit/AllureRunnerReporter.cs new file mode 100644 index 00000000..3d336582 --- /dev/null +++ b/Allure.XUnit/AllureRunnerReporter.cs @@ -0,0 +1,24 @@ +using Xunit; +using Xunit.Abstractions; + +namespace Allure.XUnit +{ + public class AllureRunnerReporter : IRunnerReporter + { + public string Description { get; } = "allure-xunit"; + + public bool IsEnvironmentallyEnabled { get; } = true; + + public string RunnerSwitch { get; } = "allure"; + + public IMessageSink CreateMessageHandler(IRunnerLogger logger) + { + AllureXunitPatcher.PatchXunit(logger); + var sink = new AllureMessageSink(logger); + CurrentSink ??= sink; + return sink; + } + + internal static AllureMessageSink CurrentSink { get; private set; } + } +} \ No newline at end of file diff --git a/Allure.XUnit/AllureXunitDiscover.cs b/Allure.XUnit/AllureXunitDiscover.cs deleted file mode 100644 index dfedf8f6..00000000 --- a/Allure.XUnit/AllureXunitDiscover.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using Allure.XUnit; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace Allure.Xunit -{ - public class AllureXunitDiscover : IXunitTestCaseDiscoverer - { - private readonly IMessageSink _diagnosticMessageSink; - - public AllureXunitDiscover(IMessageSink diagnosticMessageSink) - { - _diagnosticMessageSink = diagnosticMessageSink; - } - - public IEnumerable Discover(ITestFrameworkDiscoveryOptions discoveryOptions, - ITestMethod testMethod, IAttributeInfo factAttribute) - { - var testCase = new AllureXunitTestCase(_diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), - TestMethodDisplayOptions.None, testMethod); - return new[] { testCase }; - } - } -} \ No newline at end of file diff --git a/Allure.XUnit/AllureXunitHelper.cs b/Allure.XUnit/AllureXunitHelper.cs index dad5aab7..8148bfd2 100644 --- a/Allure.XUnit/AllureXunitHelper.cs +++ b/Allure.XUnit/AllureXunitHelper.cs @@ -1,12 +1,13 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using Allure.Net.Commons; using Allure.Net.Commons.Storage; -using Allure.XUnit; using Allure.Xunit.Attributes; +using Allure.XUnit; using Allure.XUnit.Attributes; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Linq; using Xunit; using Xunit.Abstractions; using Xunit.Sdk; @@ -15,148 +16,168 @@ namespace Allure.Xunit { public static class AllureXunitHelper { - internal static List ExceptionTypes = new List { typeof(XunitException) }; + internal static List ExceptionTypes = new() + { + typeof(XunitException) + }; + static AllureXunitHelper() { const string allureConfigEnvVariable = "ALLURE_CONFIG"; const string allureConfigName = "allureConfig.json"; - if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(allureConfigEnvVariable))) + var allureConfigPath = Environment.GetEnvironmentVariable( + allureConfigEnvVariable + ); + if (!string.IsNullOrEmpty(allureConfigPath)) { return; } - var allureConfigPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, allureConfigName); + allureConfigPath = Path.Combine( + AppDomain.CurrentDomain.BaseDirectory, + allureConfigName + ); - Environment.SetEnvironmentVariable(allureConfigEnvVariable, allureConfigPath); + Environment.SetEnvironmentVariable( + allureConfigEnvVariable, + allureConfigPath + ); } - public static void StartTestContainer(ITestCaseStarting testCaseStarting) + internal static TestResult StartStaticAllureTestCase(ITest test) { - if (testCaseStarting.TestCase is not ITestResultAccessor testResults) - { - return; - } - - StartTestContainer(testCaseStarting.TestClass, testResults); + var testResult = CreateTestResultByTest(test); + AllureLifecycle.Instance.StartTestCase(testResult); + return testResult; } - public static void StartTestCase(ITestCaseMessage testCaseMessage) + internal static TestResultContainer StartNewAllureContainer( + string className + ) { - if (testCaseMessage.TestCase is not ITestResultAccessor testResults) - { - return; - } - - var testCase = testCaseMessage.TestCase; - var uuid = NewUuid(testCase.DisplayName); - testResults.TestResult = new() + var container = new TestResultContainer { - uuid = uuid, - name = BuildName(testCase), - historyId = testCase.DisplayName, - fullName = BuildFullName(testCase), - labels = new() - { - Label.Thread(), - Label.Host(), - Label.TestClass(testCase.TestMethod.TestClass.Class.Name), - Label.TestMethod(testCase.DisplayName), - Label.Package(testCase.TestMethod.TestClass.Class.Name) - }, - parameters = testCase.TestMethod.Method.GetParameters() - .Zip(testCase.TestMethodArguments ?? Array.Empty(), (param, value) => new Parameter - { - name = param.Name, - value = value?.ToString() ?? "null" - }) - .ToList() + uuid = NewUuid(className), + name = className }; - UpdateTestDataFromAttributes(testResults.TestResult, testCase); - AllureLifecycle.Instance.StartTestCase(testResults.TestResultContainer.uuid, testResults.TestResult); + AllureLifecycle.Instance.StartTestContainer(container); + return container; } - public static void MarkTestCaseAsFailedOrBroken(ITestFailed testFailed) + internal static TestResult StartAllureTestCase( + ITest test, + TestResultContainer container + ) { - if (testFailed.TestCase is not ITestResultAccessor testResults) - { - return; - } - - var statusDetails = testResults.TestResult.statusDetails ??= new(); - statusDetails.trace = string.Join('\n', testFailed.StackTraces); - statusDetails.message = string.Join('\n', testFailed.Messages); - - if (testFailed.ExceptionTypes.Any(exceptionType => !exceptionType.StartsWith("Xunit.Sdk."))) - { - testResults.TestResult.status = Status.broken; - return; - } - testResults.TestResult.status = Status.failed; + var testResult = CreateTestResultByTest(test); + AllureLifecycle.Instance.StartTestCase(container.uuid, testResult); + return testResult; } - public static void MarkTestCaseAsPassed(ITestPassed testPassed) + internal static void ApplyTestFailure( + TestResult testResult, + IFailureInformation failure + ) { - if (testPassed.TestCase is not ITestResultAccessor testResults) - { - return; - } + var statusDetails = testResult.statusDetails ??= new(); + statusDetails.trace = string.Join('\n', failure.StackTraces); + statusDetails.message = string.Join('\n', failure.Messages); - var statusDetails = testResults.TestResult.statusDetails ??= new(); - statusDetails.message = testPassed.Output; - testResults.TestResult.status = Status.passed; + testResult.status = failure.ExceptionTypes.Any( + exceptionType => !exceptionType.StartsWith("Xunit.Sdk.") + ) ? Status.broken : Status.failed; } - public static void MarkTestCaseAsSkipped(ITestCaseMessage testCaseMessage) + internal static void ApplyTestSuccess( + TestResult testResult, + ITestResultMessage message + ) { - if (testCaseMessage.TestCase is not ITestResultAccessor testResults) + var statusDetails = testResult.statusDetails ??= new(); + statusDetails.message = message.Output; + testResult.status = Status.passed; + } + + internal static void ApplyTestParameters( + TestResult testResult, + IEnumerable parameters, + object[] arguments + ) => testResult.parameters = parameters.Zip( + arguments, + (param, value) => new Parameter { - return; + name = param.Name, + value = value?.ToString() ?? "null" } + ).ToList(); - var statusDetails = testResults.TestResult.statusDetails ??= new(); - statusDetails.message = testCaseMessage.TestCase.SkipReason; - testResults.TestResult.status = Status.skipped; + internal static void ReportTestCase(TestResult testResult) + { + AllureLifecycle.Instance.StopTestCase(testResult.uuid); + AllureLifecycle.Instance.WriteTestCase(testResult.uuid); } - public static void FinishTestCase(ITestCaseMessage testCaseMessage) + internal static void ReportTestContainer(TestResultContainer container) { - if (testCaseMessage.TestCase is not ITestResultAccessor testResults) - { - return; - } - - AllureLifecycle.Instance.StopTestCase(testResults.TestResult.uuid); - AllureLifecycle.Instance.WriteTestCase(testResults.TestResult.uuid); - AllureLifecycle.Instance.StopTestContainer(testResults.TestResultContainer.uuid); - AllureLifecycle.Instance.WriteTestContainer(testResults.TestResultContainer.uuid); + AllureLifecycle.Instance.StopTestContainer(container.uuid); + AllureLifecycle.Instance.WriteTestContainer(container.uuid); } - private static void StartTestContainer(ITestClass testClass, ITestResultAccessor testResult) + internal static void ReportSkippedTestCase(ITestCase testCase) { - var uuid = NewUuid(testClass.Class.Name); - testResult.TestResultContainer = new() - { - uuid = uuid, - name = testClass.Class.Name - }; - AllureLifecycle.Instance.StartTestContainer(testResult.TestResultContainer); + var testResult = CreateTestResultByTestCase(testCase); + ApplyTestSkip(testResult, testCase.SkipReason); + AllureLifecycle.Instance.StartTestCase(testResult); + ReportTestCase(testResult); } - private static string NewUuid(string name) + static TestResult CreateTestResultByTest(ITest test) => + CreateTestResult(test.TestCase, test.DisplayName); + + static TestResult CreateTestResultByTestCase(ITestCase testCase) => + CreateTestResult(testCase, testCase.DisplayName); + + static TestResult CreateTestResult( + ITestCase testCase, + string displayName + ) { - var uuid = string.Concat(Guid.NewGuid().ToString(), "-", name); - return uuid; + var testMethod = testCase.TestMethod; + var testResult = new TestResult + { + uuid = NewUuid(displayName), + name = BuildName(testCase), + historyId = displayName, + fullName = BuildFullName(testCase), + labels = new() + { + Label.Thread(), + Label.Host(), + Label.TestClass(testMethod.TestClass.Class.Name), + Label.TestMethod(displayName), + Label.Package(testMethod.TestClass.Class.Name) + } + }; + UpdateTestDataFromAttributes(testResult, testMethod); + return testResult; } - internal static void Log(string message) + static void ApplyTestSkip( + TestResult testResult, + string reason + ) { - AllureMessageBus.TestOutputHelper.Value.WriteLine("╬════════════════════════"); - AllureMessageBus.TestOutputHelper.Value.WriteLine($"║ {message}"); - AllureMessageBus.TestOutputHelper.Value.WriteLine("╬═══════════════"); + var statusDetails = testResult.statusDetails ??= new(); + statusDetails.message = reason; + testResult.status = Status.skipped; } - private static void AddDistinct(this List