Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Allure.Reqnroll test output handler support #526

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Allure.Reqnroll/AllurePlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ IObjectContainer container
yield return new StepStartedEventHandler(stateTransportFactory);
yield return new StepFinishedEventHandler(stateTransportFactory);
yield return new ScenarioFinishedEventHandle(stateTransportFactory);
yield return new TestOutputEventHandler(stateTransportFactory);
yield return new FeatureFinishedEventHandler(stateTransportFactory);

CrossBindingContextTransport stateTransportFactory() => new(
Expand Down
21 changes: 21 additions & 0 deletions Allure.Reqnroll/Events/TestOutputEventHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using Allure.Net.Commons;
using Allure.ReqnrollPlugin.State;
using Reqnroll.Events;

namespace Allure.ReqnrollPlugin.Events;

class TestOutputEventHandler : AllureReqnrollEventHandler<OutputAddedEvent>
{
public TestOutputEventHandler(
Func<CrossBindingContextTransport> stateStorageFactory
) : base(stateStorageFactory)
{
}

protected override void HandleInAllureContext(OutputAddedEvent eventData) {
AllureReqnrollStateFacade.AddOutput(
eventData.Text + "\n"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is the only place where AddOutput is called, we may reduce the number of concatenations by using StringBuilder's AppendLine instead of appending the new line character here and calling Append.

);
}
}
62 changes: 56 additions & 6 deletions Allure.Reqnroll/State/AllureReqnrollStateFacade.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text;
using Allure.Net.Commons;
using Allure.Net.Commons.Functions;
using Allure.Net.Commons.TestPlan;
Expand All @@ -17,7 +18,7 @@ static class AllureReqnrollStateFacade

const string ASSERT_EXC_NUNIT =
"NUnit.Framework.AssertionException";
const string ASSERT_EXC_NUNIT_MULTIPLE =
const string ASSERT_EXC_NUNIT_MULTIPLE =
"NUnit.Framework.MultipleAssertException";
const string ASSERT_EXC_XUNIT_NEW = // From v2.4.2 and onward.
"Xunit.Sdk.IAssertionException";
Expand All @@ -38,6 +39,8 @@ static class AllureReqnrollStateFacade

static AllureLifecycle Lifecycle { get => AllureLifecycle.Instance; }

static Dictionary<ExecutableItem, StringBuilder> OutputCache { get; } = new();
Copy link
Author

@coman3 coman3 Jul 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I typically try to avoid static dictionaries, especially in parallel execution contexts, however i couldn't see a path forward not using something like this, without modifying the Allure.Net.Common library.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a valid concern. Please take a look at my review comment for the alternative.


static AllureReqnrollConfiguration Configuration
{
get => AllureReqnrollConfiguration.CurrentConfig;
Expand Down Expand Up @@ -71,8 +74,11 @@ internal static void StartAfterFixture(IHookBinding binding) =>
MappingFunctions.ToFixtureResult(binding)
);

internal static void StopFixture() =>
internal static void StopFixture()
{
AttachOutputCacheAsAttachment();
ExtendedApi.PassFixture();
}

internal static void FixSnapshottedFixtureStatus(
AllureContext stateSnapshot,
Expand Down Expand Up @@ -167,7 +173,7 @@ internal static void StartStep(StepInfo stepInfo)
);
stepResult.parameters = parameters;
Lifecycle.StartStep(stepResult);
foreach (var (title, mediaType, content, extension) in attachmentsData )
foreach (var (title, mediaType, content, extension) in attachmentsData)
{
AllureApi.AddAttachment(title, mediaType, content, extension);
}
Expand All @@ -178,6 +184,8 @@ internal static void StopStep(
IScenarioStepContext stepContext
)
{
AttachOutputCacheAsAttachment();

var reqnrollStatus = ResolveStepStatus(
scenarioContext.ScenarioExecutionStatus,
stepContext.Status
Expand Down Expand Up @@ -213,12 +221,21 @@ IScenarioStepContext stepContext
}
}

internal static void StopTestCase() =>
internal static void StopTestCase()
{
AttachOutputCacheAsAttachment();
Lifecycle.StopTestCase();
}

internal static void StopContainer() =>
internal static void StopContainer()
{
if (OutputCache.Count > 0)
{
Console.WriteLine("Warning: Some output was not attached to a test case, step or fixture.");
}
OutputCache.Clear(); // Reset cache on a per-container basisis
Lifecycle.StopTestContainer();

}
internal static void EmitScenarioFiles(
IScenarioContext scenarioContext
)
Expand All @@ -240,6 +257,22 @@ IScenarioContext scenarioContext
internal static void EmitFeatureFiles() =>
Lifecycle.WriteTestContainer();

internal static void AddOutput(string text)
{
if (!(Lifecycle.Context.HasTest || Lifecycle.Context.HasFixture || Lifecycle.Context.HasStep))
return;
Lifecycle.UpdateExecutableItem(tr =>
{
if (OutputCache.ContainsKey(tr))
{
OutputCache[tr].Append(text);
return;
}

OutputCache.Add(tr, new StringBuilder(text));
});
}

internal static string? GetAllureId(
FeatureInfo featureInfo,
ScenarioContext scenarioContext
Expand All @@ -248,6 +281,23 @@ ScenarioContext scenarioContext
ResolveScenarioMetadata(featureInfo, scenarioContext).labels
);

static void AttachOutputCacheAsAttachment()
{
var output = "";
Lifecycle.UpdateExecutableItem(tr =>
{
if (OutputCache.ContainsKey(tr))
{
output = OutputCache[tr].ToString();
OutputCache.Remove(tr);
}
});

if (!string.IsNullOrWhiteSpace(output))
{
AllureApi.AddAttachment("TestOutput", "text/plain", Encoding.UTF8.GetBytes(output), ".log");
}
}
static (List<Label> labels, List<Link> links) ResolveScenarioMetadata(
FeatureInfo featureInfo,
ScenarioContext scenarioContext
Expand Down