11using System ;
22using System . Collections . Concurrent ;
3+ using System . Linq ;
4+ using Allure . Net . Commons ;
5+ using NUnit . Allure . Attributes ;
36using NUnit . Framework ;
47using NUnit . Framework . Interfaces ;
58using NUnit . Framework . Internal ;
@@ -20,50 +23,96 @@ public AllureNUnitAttribute()
2023 {
2124 }
2225
23- public void BeforeTest ( ITest test )
24- {
25- var helper = new AllureNUnitHelper ( test ) ;
26- _allureNUnitHelper . AddOrUpdate (
27- test . Id ,
28- helper ,
29- ( key , existing ) => helper
30- ) ;
31-
32- if ( test . IsSuite )
33- {
34- helper . SaveOneTimeResultToContext ( ) ;
35- StepsHelper . StopFixture ( ) ;
36- }
37- else
26+ public void BeforeTest ( ITest test ) =>
27+ RunHookInRestoredAllureContext ( test , ( ) =>
3828 {
39- helper . StartTestContainer ( ) ;
40- helper . AddOneTimeSetupResult ( ) ;
41- helper . StartTestCase ( ) ;
42- }
43- }
29+ var helper = new AllureNUnitHelper ( test ) ;
30+ _allureNUnitHelper . AddOrUpdate (
31+ test . Id ,
32+ helper ,
33+ ( key , existing ) => helper
34+ ) ;
4435
45- public void AfterTest ( ITest test )
46- {
47- if ( _allureNUnitHelper . TryGetValue ( test . Id , out var helper ) )
48- {
4936 if ( ! test . IsSuite )
5037 {
51- helper . StopTestCase ( ) ;
38+ helper . StartTestContainer ( ) ; // A container for SetUp/TearDown methods
39+ helper . StartTestCase ( ) ;
5240 }
41+ } ) ;
5342
54- helper . StopTestContainer ( ) ;
55- }
56- }
43+ public void AfterTest ( ITest test ) =>
44+ RunHookInRestoredAllureContext ( test , ( ) =>
45+ {
46+ if ( _allureNUnitHelper . TryGetValue ( test . Id , out var helper ) )
47+ {
48+ if ( ! test . IsSuite )
49+ {
50+ helper . StopTestCase ( ) ;
51+ helper . StopTestContainer ( ) ;
52+ }
53+ else if ( IsSuiteWithNoAfterFixtures ( test ) )
54+ {
55+ // If a test fixture contains a OneTimeTearDown method
56+ // with the [AllureAfter] attribute, the corresponding
57+ // container is closed in StopContainerAspect instead.
58+ helper . StopTestContainer ( ) ;
59+ }
60+ }
61+ } ) ;
5762
5863 public ActionTargets Targets =>
5964 ActionTargets . Test | ActionTargets . Suite ;
6065
6166 public void ApplyToContext ( TestExecutionContext context )
6267 {
6368 var test = context . CurrentTest ;
64- var helper = new AllureNUnitHelper ( test ) ;
65- helper . StartTestContainer ( ) ;
66- StepsHelper . StartBeforeFixture ( $ "fr- { test . Id } " ) ;
69+ // A container for OneTimeSetUp/OneTimeTearDown methods.
70+ new AllureNUnitHelper ( test ) . StartTestContainer ( ) ;
71+ CaptureGlobalAllureContext ( test ) ;
6772 }
73+
74+ static bool IsSuiteWithNoAfterFixtures ( ITest test ) =>
75+ test is TestSuite suite && ! suite . OneTimeTearDownMethods . Any (
76+ m => IsDefined ( m . MethodInfo , typeof ( AllureAfterAttribute ) )
77+ ) ;
78+
79+ #region Allure context manipulation
80+
81+ /*
82+ * The methods this region are to make sure the AllureContext
83+ * flows into setup/teardown/test methods correctly. This is needed
84+ * because NUnit might spread hooks of this class and user's code
85+ * across unrelated threads, hiding changes made to the allure context
86+ * in, say, BeforeTest from, say, a one-time tear down method.
87+ */
88+
89+ static void RunHookInRestoredAllureContext ( ITest test , Action action )
90+ {
91+ RestoreAssociatedAllureContext ( test ) ;
92+ try
93+ {
94+ action ( ) ;
95+ }
96+ finally
97+ {
98+ CaptureGlobalAllureContext ( test ) ;
99+ }
100+ }
101+
102+ static void CaptureGlobalAllureContext ( ITest test ) =>
103+ test . Properties . Set ( ALLURE_CONTEXT_KEY , AllureLifecycle . Instance . Context ) ;
104+
105+ static void RestoreAssociatedAllureContext ( ITest test ) =>
106+ AllureLifecycle . Instance . RestoreContext (
107+ GetAssociatedAllureContext ( test )
108+ ) ;
109+
110+ static AllureContext GetAssociatedAllureContext ( ITest test ) =>
111+ ( AllureContext ) test . Properties . Get ( ALLURE_CONTEXT_KEY )
112+ ?? GetAssociatedAllureContext ( test . Parent ) ;
113+
114+ const string ALLURE_CONTEXT_KEY = "AllureContext" ;
115+
116+ #endregion
68117 }
69118}
0 commit comments