1
1
using System ;
2
+ using System . Collections . Concurrent ;
2
3
using System . Collections . Generic ;
4
+ using System . Linq ;
3
5
using CSF . Screenplay . Abilities ;
4
6
using CSF . Screenplay . Actors ;
5
7
using CSF . Screenplay . Reporting . Models ;
@@ -11,10 +13,7 @@ namespace CSF.Screenplay.Reporting.Builders
11
13
/// </summary>
12
14
public class ReportBuilder
13
15
{
14
- List < Models . Scenario > scenarios ;
15
- Models . Scenario currentScenario ;
16
- PerformanceType currentPerformanceType ;
17
- Stack < PerformanceBuilder > builderStack ;
16
+ readonly ConcurrentDictionary < Guid , ScenarioBuilder > scenarios ;
18
17
19
18
/// <summary>
20
19
/// Begins reporting upon a new scenario.
@@ -23,112 +22,113 @@ public class ReportBuilder
23
22
/// <param name="featureName">The feature name.</param>
24
23
/// <param name="idName">The uniquely identifying name for the test.</param>
25
24
/// <param name="featureId">The uniquely identifying name for the feature.</param>
26
- public void BeginNewScenario ( string idName , string friendlyName = null , string featureName = null , string featureId = null )
25
+ /// <param name="scenarioId">The screenplay scenario identity.</param>
26
+ public void BeginNewScenario ( string idName ,
27
+ string friendlyName ,
28
+ string featureName ,
29
+ string featureId ,
30
+ Guid scenarioId )
27
31
{
28
- currentScenario = new Models . Scenario ( idName , friendlyName , featureName , featureId ) ;
29
- scenarios . Add ( currentScenario ) ;
30
- currentPerformanceType = PerformanceType . Unspecified ;
31
- builderStack . Clear ( ) ;
32
+ var success = scenarios . TryAdd ( scenarioId , new ScenarioBuilder ( idName , friendlyName , featureName , featureId ) ) ;
33
+ if ( ! success )
34
+ throw new InvalidOperationException ( "The current scenario already exists in the current builder instance" ) ;
32
35
}
33
36
34
37
/// <summary>
35
38
/// Reports the end of a scenario.
36
39
/// </summary>
37
- /// <param name="isSuccess">Optional. If set to <c>false</c> then the scenario is marked as a failure.</param>
38
- public void EndScenario ( bool isSuccess = true )
40
+ /// <param name="outcome">If set to <c>false</c> then the scenario is marked as a failure.</param>
41
+ /// <param name="scenarioId">The screenplay scenario identity.</param>
42
+ public void EndScenario ( bool ? outcome ,
43
+ Guid scenarioId )
39
44
{
40
- EnsureCurrentScenario ( ) ;
41
-
42
- if ( ! isSuccess )
43
- currentScenario . IsFailure = true ;
44
-
45
- currentScenario = null ;
46
- builderStack . Clear ( ) ;
45
+ var scenario = GetScenario ( scenarioId ) ;
46
+ scenario . Finalise ( outcome ) ;
47
47
}
48
48
49
49
/// <summary>
50
50
/// Begins reporting of a new performable.
51
51
/// </summary>
52
52
/// <param name="actor">Actor.</param>
53
53
/// <param name="performable">Performable.</param>
54
- public void BeginPerformance ( INamed actor , Performables . IPerformable performable )
54
+ /// <param name="scenarioId">The screenplay scenario identity.</param>
55
+ public void BeginPerformance ( INamed actor , Performables . IPerformable performable ,
56
+ Guid scenarioId )
55
57
{
56
- EnsureCurrentScenario ( ) ;
57
-
58
- var builder = new PerformanceBuilder {
59
- Performable = performable ,
60
- Actor = actor ,
61
- PerformanceType = currentPerformanceType ,
62
- } ;
63
- AddPerformanceBuilder ( builder ) ;
58
+ var scenario = GetScenario ( scenarioId ) ;
59
+ scenario . BeginPerformance ( actor , performable ) ;
64
60
}
65
61
66
62
/// <summary>
67
63
/// Begins reporting of a performance of a given type.
68
64
/// </summary>
69
65
/// <param name="performanceType">Performance type.</param>
70
- public void BeginPerformanceType ( PerformanceType performanceType )
66
+ /// <param name="scenarioId">The screenplay scenario identity.</param>
67
+ public void BeginPerformanceType ( PerformanceType performanceType ,
68
+ Guid scenarioId )
71
69
{
72
- EnsureCurrentScenario ( ) ;
73
-
74
- performanceType . RequireDefinedValue ( nameof ( performanceType ) ) ;
75
- currentPerformanceType = performanceType ;
70
+ var scenario = GetScenario ( scenarioId ) ;
71
+ scenario . BeginPerformanceType ( performanceType ) ;
76
72
}
77
73
78
74
/// <summary>
79
75
/// Records that the current performable has received a result.
80
76
/// </summary>
81
77
/// <param name="performable">Performable.</param>
82
78
/// <param name="result">Result.</param>
83
- public void RecordResult ( Performables . IPerformable performable , object result )
79
+ /// <param name="scenarioId">The screenplay scenario identity.</param>
80
+ public void RecordResult ( Performables . IPerformable performable , object result ,
81
+ Guid scenarioId )
84
82
{
85
- var builder = PeekCurrentBuilder ( performable ) ;
86
-
87
- builder . HasResult = true ;
88
- builder . Result = result ;
83
+ var scenario = GetScenario ( scenarioId ) ;
84
+ scenario . RecordResult ( performable , result ) ;
89
85
}
90
86
91
87
/// <summary>
92
88
/// Records that the current performable has failed with an exception.
93
89
/// </summary>
94
90
/// <param name="performable">Performable.</param>
95
91
/// <param name="exception">Exception.</param>
96
- public void RecordFailure ( Performables . IPerformable performable , Exception exception )
92
+ /// <param name="scenarioId">The screenplay scenario identity.</param>
93
+ public void RecordFailure ( Performables . IPerformable performable , Exception exception ,
94
+ Guid scenarioId )
97
95
{
98
- var builder = PeekCurrentBuilder ( performable ) ;
99
-
100
- builder . IsFailure = true ;
101
- builder . Exception = exception ;
102
-
103
- FinalisePerformance ( performable ) ;
96
+ var scenario = GetScenario ( scenarioId ) ;
97
+ scenario . RecordFailure ( performable , exception ) ;
104
98
}
105
99
106
100
/// <summary>
107
101
/// Records that the current performable has completed successfully.
108
102
/// </summary>
109
103
/// <param name="performable">Performable.</param>
110
- public void RecordSuccess ( Performables . IPerformable performable )
104
+ /// <param name="scenarioId">The screenplay scenario identity.</param>
105
+ public void RecordSuccess ( Performables . IPerformable performable ,
106
+ Guid scenarioId )
111
107
{
112
- FinalisePerformance ( performable ) ;
108
+ var scenario = GetScenario ( scenarioId ) ;
109
+ scenario . RecordSuccess ( performable ) ;
113
110
}
114
111
115
112
/// <summary>
116
113
/// Ends the performance of the current type.
117
114
/// </summary>
118
- public void EndPerformanceType ( )
115
+ public void EndPerformanceType ( Guid scenarioId )
119
116
{
120
- currentPerformanceType = PerformanceType . Unspecified ;
117
+ var scenario = GetScenario ( scenarioId ) ;
118
+ scenario . EndPerformanceType ( ) ;
121
119
}
122
120
123
121
/// <summary>
124
122
/// Reports that the given actor has gained the given ability.
125
123
/// </summary>
126
124
/// <param name="actor">Actor.</param>
127
125
/// <param name="ability">Ability.</param>
128
- public void GainAbility ( INamed actor , IAbility ability )
126
+ /// <param name="scenarioId">The screenplay scenario identity.</param>
127
+ public void GainAbility ( INamed actor , IAbility ability ,
128
+ Guid scenarioId )
129
129
{
130
- var item = new GainAbility ( actor , Outcome . Success , ability , currentPerformanceType ) ;
131
- AddReportable ( item ) ;
130
+ var scenario = GetScenario ( scenarioId ) ;
131
+ scenario . GainAbility ( actor , ability ) ;
132
132
}
133
133
134
134
/// <summary>
@@ -137,86 +137,24 @@ public void GainAbility(INamed actor, IAbility ability)
137
137
/// <returns>The report.</returns>
138
138
public Report GetReport ( )
139
139
{
140
- return new Report ( scenarios . ToArray ( ) ) ;
141
- }
142
-
143
- void AddReportable ( Reportable item )
144
- {
145
- if ( item == null )
146
- throw new ArgumentNullException ( nameof ( item ) ) ;
147
-
148
- var list = GetCurrentReportables ( ) ;
149
- list . Add ( item ) ;
150
- }
151
-
152
- void FinalisePerformance ( Performables . IPerformable performable )
153
- {
154
- var builder = PopCurrentBuilder ( performable ) ;
155
- var performance = builder . GetPerformance ( ) ;
156
- AddReportable ( performance ) ;
157
- }
158
-
159
- void AddPerformanceBuilder ( PerformanceBuilder builder )
160
- {
161
- if ( builder == null )
162
- throw new ArgumentNullException ( nameof ( builder ) ) ;
163
-
164
- builderStack . Push ( builder ) ;
165
- }
166
-
167
- PerformanceBuilder PopCurrentBuilder ( Performables . IPerformable expectedPerformable )
168
- {
169
- PeekCurrentBuilder ( expectedPerformable ) ;
170
- return builderStack . Pop ( ) ;
140
+ return new Report ( scenarios . Values . Select ( x => x . GetScenario ( ) ) . ToArray ( ) ) ;
171
141
}
172
142
173
- PerformanceBuilder PeekCurrentBuilder ( Performables . IPerformable expectedPerformable = null )
143
+ ScenarioBuilder GetScenario ( Guid identity )
174
144
{
175
- if ( builderStack . Count == 0 && expectedPerformable == null )
176
- return null ;
177
- else if ( builderStack . Count == 0 )
178
- throw new InvalidOperationException ( "An expected performable was specified but the builder stack was empty, this is not permitted." ) ;
179
-
180
- var current = builderStack . Peek ( ) ;
181
-
182
- if ( expectedPerformable == null )
183
- return current ;
145
+ ScenarioBuilder scenario ;
146
+ if ( ! scenarios . TryGetValue ( identity , out scenario ) )
147
+ throw new InvalidOperationException ( "There is no matching scenario in the current builder." ) ;
184
148
185
- if ( ! ReferenceEquals ( expectedPerformable , current . Performable ) )
186
- {
187
- throw new ArgumentException ( "The expected performable must be the same as the current builder." ,
188
- nameof ( expectedPerformable ) ) ;
189
- }
190
-
191
- return current ;
192
- }
193
-
194
- IList < Reportable > GetCurrentReportables ( )
195
- {
196
- var currentBuilder = PeekCurrentBuilder ( ) ;
197
- if ( currentBuilder != null )
198
- return currentBuilder . Reportables ;
199
-
200
- if ( currentScenario != null )
201
- return currentScenario . Reportables ;
202
-
203
- throw new InvalidOperationException ( "Cannot get the current reportables, there must be either a current builder or a current scenario." ) ;
204
- }
205
-
206
- void EnsureCurrentScenario ( )
207
- {
208
- if ( ReferenceEquals ( currentScenario , null ) )
209
- throw new InvalidOperationException ( "There must be a current scenario in order to report upon performables." ) ;
149
+ return scenario ;
210
150
}
211
151
212
152
/// <summary>
213
153
/// Initializes a new instance of the <see cref="ReportBuilder"/> class.
214
154
/// </summary>
215
155
public ReportBuilder ( )
216
156
{
217
- scenarios = new List < Models . Scenario > ( ) ;
218
- builderStack = new Stack < PerformanceBuilder > ( ) ;
219
- currentPerformanceType = PerformanceType . Unspecified ;
157
+ scenarios = new ConcurrentDictionary < Guid , ScenarioBuilder > ( ) ;
220
158
}
221
159
}
222
160
}
0 commit comments