-
Notifications
You must be signed in to change notification settings - Fork 74
Expand file tree
/
Copy pathAllureNUnitAttribute.cs
More file actions
118 lines (102 loc) · 3.97 KB
/
AllureNUnitAttribute.cs
File metadata and controls
118 lines (102 loc) · 3.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
using System;
using System.Collections.Concurrent;
using System.Linq;
using Allure.Net.Commons;
using Allure.NUnit.Attributes;
using Allure.NUnit.Core;
using NUnit.Framework;
using NUnit.Framework.Interfaces;
using NUnit.Framework.Internal;
namespace Allure.NUnit
{
[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class)]
public class AllureNUnitAttribute : PropertyAttribute, ITestAction, IApplyToContext
{
private readonly ConcurrentDictionary<string, AllureNUnitHelper> _allureNUnitHelper = new();
public void BeforeTest(ITest test) =>
RunHookInRestoredAllureContext(test, () =>
{
var helper = new AllureNUnitHelper(test);
_allureNUnitHelper.AddOrUpdate(
test.Id,
helper,
(key, existing) => helper
);
if (!test.IsSuite)
{
helper.PrepareTestContext();
}
});
public void AfterTest(ITest test)
{
if (test.IsDeselected())
{
return;
}
RunHookInRestoredAllureContext(test, () =>
{
if (_allureNUnitHelper.TryGetValue(test.Id, out var helper))
{
if (!test.IsSuite)
{
helper.StopTestCase();
helper.StopTestContainer();
}
else if (IsSuiteWithNoAfterFixtures(test))
{
// If a test class has no class-scope after-feature
// (i.e., a method with both [OneTimeTearDown] and
// [AllureAfter]), the class-scope container is closed
// here. Otherwise, it's closed in StopContainerAspect
// instead.
helper.StopTestContainer();
}
}
});
}
public ActionTargets Targets =>
ActionTargets.Test | ActionTargets.Suite;
public void ApplyToContext(TestExecutionContext context)
{
var test = context.CurrentTest;
// A container for OneTimeSetUp/OneTimeTearDown methods.
new AllureNUnitHelper(test).StartTestContainer();
CaptureGlobalAllureContext(test);
}
static bool IsSuiteWithNoAfterFixtures(ITest test) =>
test is TestSuite suite && !suite.OneTimeTearDownMethods.Any(
m => IsDefined(m.MethodInfo, typeof(AllureAfterAttribute))
);
#region Allure context manipulation
/*
* The methods this region are to make sure the AllureContext
* flows into setup/teardown/test methods correctly. This is needed
* because NUnit might spread hooks of this class and user's code
* across unrelated threads, hiding changes made to the allure context
* in, say, BeforeTest from, say, a one-time tear down method.
*/
static void RunHookInRestoredAllureContext(ITest test, Action action)
{
RestoreAssociatedAllureContext(test);
try
{
action();
}
finally
{
CaptureGlobalAllureContext(test);
}
}
static void CaptureGlobalAllureContext(ITest test) =>
test.Properties.Set(ALLURE_CONTEXT_KEY, AllureLifecycle.Instance.Context);
static void RestoreAssociatedAllureContext(ITest test) =>
AllureLifecycle.Instance.RestoreContext(
GetAssociatedAllureContext(test)
);
static AllureContext GetAssociatedAllureContext(ITest test) =>
(AllureContext)test.Properties.Get(ALLURE_CONTEXT_KEY)
?? GetAssociatedAllureContext(test.Parent);
const string ALLURE_CONTEXT_KEY = "AllureContext";
#endregion
}
}