Skip to content

Commit 18679f7

Browse files
authored
New context-based state management model for allure-csharp (#371)
* Add AllureContext lass to isolate allure state from threads and async tasks * Add concurrency tests. Reformulate context in terms of active/inactive * Add tests on the context capturing by child threads/tasks * Modify lifecycle/storage to use new context model. Add context get/set API * Make CoreStepsHelper use AllureLifecycle's context * Make lifecycle thread safe. Fix multithreading issues in lifecycle tests * Several improvements on context. Allure-xunit migration - Add bool props to test context to context's public API - Fix context string conversion - Migrate allure-xunit's code to the new context implementation * Bump C# language version to 11 for all projects * Enable null checks in some classes of allure-xunit * Make lifecycle methods with explicit uuids obsoleted * Minor changes in context and lifecycle - Improve error messages and doc comments - Add DebuggerDisplay for context - Uuid in string representation of context if names are empty - Old storage uuid-based methods obsoleted - Make RunInContext return modified context - Replace unsupported container nesting with test nested into each container in chain - Container and test starting now add objects into storage by uuids (transition period) * Rewrite Allure.NUnit to support the new context scheme * Fix Allure.XUnit usage of RunInContext * Rewrite Allure.SpecFlowPlugin to support new context scheme Additionally: - Change broken status to failed for some scenario and step conditions - Add extra test case instead of changing state of existing one in case after feature failed - Enable nullable check project-wide * Separate context from storage - AllureContext moved to Allure.Net.Commons namespace - Context and storage are separated; Storage will be removed later - File scope namespaces for lifecycle and context * Fix obsolete messages. Add file scoped namespaces * Revert obsoleted StopFixtureSuppressTestCase to be intact * Add new info to commons README * Fix invalid test start. Remove rudimentary test start check * Remove dotnet SDK 3.1 installation from build pipeline * Change .NET SDK version to 7.0 in build pipeline * Github pipelines fix - bump actions/checkout to 3.5.3 - bump actions/setup-dotnet to 3.2.0 - add dotnet 3.1 and 6.0 SDKs back to build and test - Change --no-restore to --no-build in dotnet test step * Rename a placeholder scenario * Implement requested changes for PR #371 - Add allure directory clean before all tests in Allure.NUnit.Examples - NUnit.Allure.Core.AllureExtensions.AddScreenDiff is back and marked as obsolete (it just directly calls AllureLifecycle.AddScreenDiff now). - Intendation in Allure.SpecFlowPlugin.csproj fixed.
1 parent 4a55fce commit 18679f7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+4424
-1619
lines changed

.github/workflows/build.yml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ jobs:
2121
name: "Build"
2222
runs-on: ubuntu-latest
2323
steps:
24-
- uses: actions/checkout@v3.0.2
24+
- uses: actions/checkout@v3.5.3
2525

2626
- name: 'Setup .NET Core SDK'
27-
uses: actions/setup-dotnet@v2.1.0
27+
uses: actions/setup-dotnet@v3.2.0
2828
with:
2929
dotnet-version: |
3030
3.1.x
3131
6.0.x
32+
7.0.x
3233
3334
- name: 'Restore packages'
3435
run: dotnet restore ${{ env.SOLUTION_PATH }} --packages ${{ env.RESTORE_OUTPUT_PATH }}
@@ -38,5 +39,5 @@ jobs:
3839

3940
- name: 'Run tests'
4041
run: |
41-
dotnet test Allure.Net.Commons.Tests/Allure.Net.Commons.Tests.csproj --no-restore --configuration ${{ env.BUILD_CONFIGURATION }}
42-
dotnet test Allure.SpecFlowPlugin.Tests/Allure.SpecFlowPlugin.Tests.csproj --no-restore --configuration ${{ env.BUILD_CONFIGURATION }}
42+
dotnet test Allure.Net.Commons.Tests/Allure.Net.Commons.Tests.csproj --no-build --configuration ${{ env.BUILD_CONFIGURATION }}
43+
dotnet test Allure.SpecFlowPlugin.Tests/Allure.SpecFlowPlugin.Tests.csproj --no-build --configuration ${{ env.BUILD_CONFIGURATION }}

.github/workflows/publish.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ jobs:
1414
build:
1515
runs-on: ubuntu-latest
1616
steps:
17-
- uses: actions/checkout@v3.0.2
17+
- uses: actions/checkout@v3.5.3
1818

1919
- name: 'Setup .NET Core SDK'
20-
uses: actions/setup-dotnet@v2.1.0
20+
uses: actions/setup-dotnet@v3.2.0
2121
with:
2222
dotnet-version: |
2323
3.1.x
2424
6.0.x
25+
7.0.x
2526
2627
- name: 'Restore packages'
2728
run: dotnet restore ${{ env.SOLUTION_PATH }} --packages ${{ env.RESTORE_OUTPUT_PATH }}

Allure.Features/Allure.Features.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<TargetFramework>netcoreapp3.1</TargetFramework>
4+
<LangVersion>11</LangVersion>
45
<IsPackable>false</IsPackable>
56
<OutputPath>bin</OutputPath>
67
</PropertyGroup>

Allure.Features/TestData/After Feature Failure.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Feature: After Feature Failure
55
Scenario: After Feature Failure 1
66
Given Step is 'passed'
77

8-
@broken
8+
@failed
99
Scenario: After Feature Failure 3
1010
Given Step is 'failed'
1111

Allure.Features/TestData/Before Feature Failure.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
Feature: Before Feature Failure
33

44
@broken
5-
Scenario: Unknown
5+
Scenario: Feature hook failure placeholder

Allure.Features/TestData/Invalid Steps.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
Given Step is 'passed'
1313
And I don't have such step too
1414

15-
@broken
15+
@failed
1616
Scenario: Failed step followed by invalid step
1717
Given Step is 'failed'
1818
Given I don't have such step

Allure.NUnit.Examples/Allure.NUnit.Examples.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFramework>net6.0</TargetFramework>
5+
<LangVersion>11</LangVersion>
56
<IsPackable>false</IsPackable>
67
<OutputType>Library</OutputType>
78
</PropertyGroup>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using Allure.Net.Commons;
2+
using NUnit.Allure.Core;
3+
using NUnit.Framework;
4+
5+
namespace Allure.NUnit.Examples
6+
{
7+
[SetUpFixture]
8+
public class AllureSetUpFixture
9+
{
10+
[OneTimeSetUp]
11+
public static void CleanupResultDirectory() =>
12+
AllureExtensions.WrapSetUpTearDownParams(
13+
AllureLifecycle.Instance.CleanupResultDirectory,
14+
"Clear Allure Results Directory"
15+
);
16+
}
17+
}

Allure.NUnit.Examples/BaseTest.cs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
1-
using Allure.Net.Commons;
2-
using NUnit.Allure.Attributes;
1+
using NUnit.Allure.Attributes;
32
using NUnit.Allure.Core;
4-
using NUnit.Framework;
53

64
namespace Allure.NUnit.Examples
75
{
86
[AllureNUnit]
97
[AllureParentSuite("Root Suite")]
108
public class BaseTest
119
{
12-
[OneTimeSetUp]
13-
public void CleanupResultDirectory()
14-
{
15-
AllureExtensions.WrapSetUpTearDownParams(() => { AllureLifecycle.Instance.CleanupResultDirectory(); },
16-
"Clear Allure Results Directory");
17-
}
1810
}
1911
}

Allure.NUnit/Allure.NUnit.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
5+
<LangVersion>11</LangVersion>
56
<Version>2.10-SNAPSHOT</Version>
67
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
78
<Company>Qameta Software</Company>

Allure.NUnit/Attributes/AllureDisplayIgnoredAttribute.cs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ namespace NUnit.Allure.Attributes
1313
public class AllureDisplayIgnoredAttribute : NUnitAttribute, ITestAction
1414
{
1515
private readonly string _suiteName;
16-
private string _ignoredContainerId;
1716

1817
public AllureDisplayIgnoredAttribute(string suiteNameForIgnoredTests = "Ignored")
1918
{
@@ -22,13 +21,11 @@ public AllureDisplayIgnoredAttribute(string suiteNameForIgnoredTests = "Ignored"
2221

2322
public void BeforeTest(ITest suite)
2423
{
25-
_ignoredContainerId = suite.Id + "-ignored";
26-
var fixture = new TestResultContainer
24+
AllureLifecycle.Instance.StartTestContainer(new()
2725
{
28-
uuid = _ignoredContainerId,
26+
uuid = suite.Id + "-ignored",
2927
name = suite.ClassName
30-
};
31-
AllureLifecycle.Instance.StartTestContainer(fixture);
28+
});
3229
}
3330

3431
public void AfterTest(ITest suite)
@@ -37,12 +34,19 @@ public void AfterTest(ITest suite)
3734
if (suite.HasChildren)
3835
{
3936
var ignoredTests =
40-
GetAllTests(suite).Where(t => t.RunState == RunState.Ignored || t.RunState == RunState.Skipped);
37+
GetAllTests(suite).Where(
38+
t => t.RunState == RunState.Ignored
39+
|| t.RunState == RunState.Skipped
40+
);
4141
foreach (var test in ignoredTests)
4242
{
43-
AllureLifecycle.Instance.UpdateTestContainer(_ignoredContainerId, t => t.children.Add(test.Id));
43+
AllureLifecycle.Instance.UpdateTestContainer(
44+
t => t.children.Add(test.Id)
45+
);
4446

45-
var reason = test.Properties.Get(PropertyNames.SkipReason).ToString();
47+
var reason = test.Properties.Get(
48+
PropertyNames.SkipReason
49+
).ToString();
4650

4751
var ignoredTestResult = new TestResult
4852
{
@@ -66,12 +70,12 @@ public void AfterTest(ITest suite)
6670
}
6771
};
6872
AllureLifecycle.Instance.StartTestCase(ignoredTestResult);
69-
AllureLifecycle.Instance.StopTestCase(ignoredTestResult.uuid);
70-
AllureLifecycle.Instance.WriteTestCase(ignoredTestResult.uuid);
73+
AllureLifecycle.Instance.StopTestCase();
74+
AllureLifecycle.Instance.WriteTestCase();
7175
}
7276

73-
AllureLifecycle.Instance.StopTestContainer(_ignoredContainerId);
74-
AllureLifecycle.Instance.WriteTestContainer(_ignoredContainerId);
77+
AllureLifecycle.Instance.StopTestContainer();
78+
AllureLifecycle.Instance.WriteTestContainer();
7579
}
7680
}
7781

Allure.NUnit/Core/AllureExtendedConfiguration.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ namespace NUnit.Allure.Core
66
{
77
internal class AllureExtendedConfiguration : AllureConfiguration
88
{
9-
public HashSet<string> BrokenTestData { get; set; } = new HashSet<string>();
9+
public HashSet<string> BrokenTestData { get; set; } = new();
1010

1111
[JsonConstructor]
12-
protected AllureExtendedConfiguration(string title, string directory, HashSet<string> links) : base(title,
12+
protected AllureExtendedConfiguration(
13+
string title,
14+
string directory,
15+
HashSet<string> links
16+
) : base(title,
1317
directory, links)
1418
{
1519
}

Allure.NUnit/Core/AllureExtensions.cs

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
using System;
2-
using System.Reflection;
2+
using System.ComponentModel;
33
using System.Runtime.CompilerServices;
4+
using System.Threading.Tasks;
45
using Allure.Net.Commons;
56
using NUnit.Framework.Internal;
6-
using System.Linq;
7-
using System.Threading.Tasks;
87

98
namespace NUnit.Allure.Core
109
{
@@ -58,15 +57,22 @@ public static void WrapSetUpTearDownParams(Action action, string customName = ""
5857
/// Wraps Action into AllureStep.
5958
/// </summary>
6059
[Obsolete("Use [AllureStep] method attribute")]
61-
public static void WrapInStep(this AllureLifecycle lifecycle, Action action, string stepName = "", [CallerMemberName] string callerName = "")
60+
public static void WrapInStep(
61+
this AllureLifecycle lifecycle,
62+
Action action,
63+
string stepName = "",
64+
[CallerMemberName] string callerName = ""
65+
)
6266
{
63-
if (string.IsNullOrEmpty(stepName)) stepName = callerName;
67+
if (string.IsNullOrEmpty(stepName))
68+
{
69+
stepName = callerName;
70+
}
6471

65-
var id = Guid.NewGuid().ToString();
6672
var stepResult = new StepResult {name = stepName};
6773
try
6874
{
69-
lifecycle.StartStep(id, stepResult);
75+
lifecycle.StartStep(stepResult);
7076
action.Invoke();
7177
lifecycle.StopStep(step => stepResult.status = Status.passed);
7278
}
@@ -88,14 +94,22 @@ public static void WrapInStep(this AllureLifecycle lifecycle, Action action, str
8894
/// <summary>
8995
/// Wraps Func into AllureStep.
9096
/// </summary>
91-
public static T WrapInStep<T>(this AllureLifecycle lifecycle, Func<T> func, string stepName = "", [CallerMemberName] string callerName = "")
97+
public static T WrapInStep<T>(
98+
this AllureLifecycle lifecycle,
99+
Func<T> func,
100+
string stepName = "",
101+
[CallerMemberName] string callerName = ""
102+
)
92103
{
93-
if (string.IsNullOrEmpty(stepName)) stepName = callerName;
94-
var id = Guid.NewGuid().ToString();
104+
if (string.IsNullOrEmpty(stepName))
105+
{
106+
stepName = callerName;
107+
}
108+
95109
var stepResult = new StepResult {name = stepName};
96110
try
97111
{
98-
lifecycle.StartStep(id, stepResult);
112+
lifecycle.StartStep(stepResult);
99113
var result = func.Invoke();
100114
lifecycle.StopStep(step => stepResult.status = Status.passed);
101115
return result;
@@ -125,13 +139,15 @@ public static async Task WrapInStepAsync(
125139
[CallerMemberName] string callerName = ""
126140
)
127141
{
128-
if (string.IsNullOrEmpty(stepName)) stepName = callerName;
142+
if (string.IsNullOrEmpty(stepName))
143+
{
144+
stepName = callerName;
145+
}
129146

130-
var id = Guid.NewGuid().ToString();
131147
var stepResult = new StepResult { name = stepName };
132148
try
133149
{
134-
lifecycle.StartStep(id, stepResult);
150+
lifecycle.StartStep(stepResult);
135151
await action();
136152
lifecycle.StopStep(step => stepResult.status = Status.passed);
137153
}
@@ -160,12 +176,15 @@ public static async Task<T> WrapInStepAsync<T>(
160176
[CallerMemberName] string callerName = ""
161177
)
162178
{
163-
if (string.IsNullOrEmpty(stepName)) stepName = callerName;
164-
var id = Guid.NewGuid().ToString();
179+
if (string.IsNullOrEmpty(stepName))
180+
{
181+
stepName = callerName;
182+
}
183+
165184
var stepResult = new StepResult { name = stepName };
166185
try
167186
{
168-
lifecycle.StartStep(id, stepResult);
187+
lifecycle.StartStep(stepResult);
169188
var result = await func();
170189
lifecycle.StopStep(step => stepResult.status = Status.passed);
171190
return result;
@@ -185,15 +204,16 @@ public static async Task<T> WrapInStepAsync<T>(
185204
}
186205
}
187206

188-
/// <summary>
189-
/// AllureNUnit AddScreenDiff wrapper method.
190-
/// </summary>
191-
public static void AddScreenDiff(this AllureLifecycle lifecycle, string expected, string actual, string diff)
192-
{
193-
var storageMain = lifecycle.GetType().GetField("storage", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(lifecycle);
194-
var storageInternal = storageMain.GetType().GetField("storage", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(storageMain);
195-
var keys = (storageInternal as System.Collections.Concurrent.ConcurrentDictionary<string, object>).Keys.ToList();
196-
AllureLifecycle.Instance.AddScreenDiff(keys.Find(key => key.Contains("-tr-")), expected, actual, diff);
197-
}
207+
[Obsolete(
208+
"Use AllureLifecycle.AddScreenDiff instance method instead to " +
209+
"add a screen diff to the current test."
210+
)]
211+
[EditorBrowsable(EditorBrowsableState.Never)]
212+
public static void AddScreenDiff(
213+
this AllureLifecycle lifecycle,
214+
string expected,
215+
string actual,
216+
string diff
217+
) => lifecycle.AddScreenDiff(expected, actual, diff);
198218
}
199219
}

0 commit comments

Comments
 (0)