@@ -36,7 +36,7 @@ extension Event {
36
36
37
37
/// A type that contains mutable context for
38
38
/// ``Event/ConsoleOutputRecorder``.
39
- private struct _Context {
39
+ fileprivate struct Context {
40
40
/// The instant at which the run started.
41
41
var runStartInstant : Test . Clock . Instant ?
42
42
@@ -51,6 +51,17 @@ extension Event {
51
51
/// The number of test suites started or skipped during the run.
52
52
var suiteCount = 0
53
53
54
+ /// An enumeration describing the various keys which can be used in a test
55
+ /// data graph for an output recorder.
56
+ enum TestDataKey : Hashable {
57
+ /// A string key, typically containing one key from the key path
58
+ /// representation of a ``Test/ID`` instance.
59
+ case string( String )
60
+
61
+ /// A test case ID.
62
+ case testCaseID( Test . Case . ID )
63
+ }
64
+
54
65
/// A type describing data tracked on a per-test basis.
55
66
struct TestData {
56
67
/// The instant at which the test started.
@@ -62,18 +73,15 @@ extension Event {
62
73
63
74
/// The number of known issues recorded for the test.
64
75
var knownIssueCount = 0
65
-
66
- /// The number of test cases for the test.
67
- var testCasesCount = 0
68
76
}
69
77
70
78
/// Data tracked on a per-test basis.
71
- var testData = Graph < String , TestData ? > ( )
79
+ var testData = Graph < TestDataKey , TestData ? > ( )
72
80
}
73
81
74
82
/// This event recorder's mutable context about events it has received,
75
83
/// which may be used to inform how subsequent events are written.
76
- private var _context = Locked ( rawValue: _Context ( ) )
84
+ private var _context = Locked ( rawValue: Context ( ) )
77
85
78
86
/// Initialize a new human-readable event recorder.
79
87
///
@@ -128,7 +136,9 @@ extension Event.HumanReadableOutputRecorder {
128
136
/// - graph: The graph to walk while counting issues.
129
137
///
130
138
/// - Returns: A tuple containing the number of issues recorded in `graph`.
131
- private func _issueCounts( in graph: Graph < String , Event . HumanReadableOutputRecorder . _Context . TestData ? > ? ) -> ( errorIssueCount: Int , warningIssueCount: Int , knownIssueCount: Int , totalIssueCount: Int , description: String ) {
139
+ private func _issueCounts(
140
+ in graph: Graph < Event . HumanReadableOutputRecorder . Context . TestDataKey , Event . HumanReadableOutputRecorder . Context . TestData ? > ?
141
+ ) -> ( errorIssueCount: Int , warningIssueCount: Int , knownIssueCount: Int , totalIssueCount: Int , description: String ) {
132
142
guard let graph else {
133
143
return ( 0 , 0 , 0 , 0 , " " )
134
144
}
@@ -241,6 +251,7 @@ extension Event.HumanReadableOutputRecorder {
241
251
0
242
252
}
243
253
let test = eventContext. test
254
+ let keyPath = eventContext. keyPath
244
255
let testName = if let test {
245
256
if let displayName = test. displayName {
246
257
if verbosity > 0 {
@@ -271,7 +282,7 @@ extension Event.HumanReadableOutputRecorder {
271
282
272
283
case . testStarted:
273
284
let test = test!
274
- context. testData [ test . id . keyPathRepresentation ] = . init( startInstant: instant)
285
+ context. testData [ keyPath ] = . init( startInstant: instant)
275
286
if test. isSuite {
276
287
context. suiteCount += 1
277
288
} else {
@@ -287,23 +298,17 @@ extension Event.HumanReadableOutputRecorder {
287
298
}
288
299
289
300
case let . issueRecorded( issue) :
290
- let id : [ String ] = if let test {
291
- test. id. keyPathRepresentation
292
- } else {
293
- [ ]
294
- }
295
- var testData = context. testData [ id] ?? . init( startInstant: instant)
301
+ var testData = context. testData [ keyPath] ?? . init( startInstant: instant)
296
302
if issue. isKnown {
297
303
testData. knownIssueCount += 1
298
304
} else {
299
305
let issueCount = testData. issueCount [ issue. severity] ?? 0
300
306
testData. issueCount [ issue. severity] = issueCount + 1
301
307
}
302
- context. testData [ id ] = testData
308
+ context. testData [ keyPath ] = testData
303
309
304
310
case . testCaseStarted:
305
- let test = test!
306
- context. testData [ test. id. keyPathRepresentation] ? . testCasesCount += 1
311
+ context. testData [ keyPath] = . init( startInstant: instant)
307
312
308
313
default :
309
314
// These events do not manipulate the context structure.
@@ -384,13 +389,12 @@ extension Event.HumanReadableOutputRecorder {
384
389
385
390
case . testEnded:
386
391
let test = test!
387
- let id = test. id
388
- let testDataGraph = context. testData. subgraph ( at: id. keyPathRepresentation)
392
+ let testDataGraph = context. testData. subgraph ( at: keyPath)
389
393
let testData = testDataGraph? . value ?? . init( startInstant: instant)
390
394
let issues = _issueCounts ( in: testDataGraph)
391
395
let duration = testData. startInstant. descriptionOfDuration ( to: instant)
392
- let testCasesCount = if test. isParameterized {
393
- " with \( testData . testCasesCount . counting ( " test case " ) ) "
396
+ let testCasesCount = if test. isParameterized, let testDataGraph {
397
+ " with \( testDataGraph . children . count . counting ( " test case " ) ) "
394
398
} else {
395
399
" "
396
400
}
@@ -517,15 +521,37 @@ extension Event.HumanReadableOutputRecorder {
517
521
break
518
522
}
519
523
524
+ let status = verbosity > 0 ? " started " : " "
525
+
520
526
return [
521
527
Message (
522
528
symbol: . default,
523
- stringValue: " Passing \( arguments. count. counting ( " argument " ) ) \( testCase. labeledArguments ( includingQualifiedTypeNames: verbosity > 0 ) ) to \( testName) "
529
+ stringValue: " Passing \( arguments. count. counting ( " argument " ) ) \( testCase. labeledArguments ( includingQualifiedTypeNames: verbosity > 0 ) ) to \( testName) \( status ) . "
524
530
)
525
531
]
526
532
527
533
case . testCaseEnded:
528
- break
534
+ guard verbosity > 0 , let testCase = eventContext. testCase, testCase. isParameterized, let arguments = testCase. arguments else {
535
+ break
536
+ }
537
+
538
+ let testDataGraph = context. testData. subgraph ( at: keyPath)
539
+ let testData = testDataGraph? . value ?? . init( startInstant: instant)
540
+ let issues = _issueCounts ( in: testDataGraph)
541
+ let duration = testData. startInstant. descriptionOfDuration ( to: instant)
542
+
543
+ let message = if issues. errorIssueCount > 0 {
544
+ Message (
545
+ symbol: . fail,
546
+ stringValue: " Passing \( arguments. count. counting ( " argument " ) ) \( testCase. labeledArguments ( includingQualifiedTypeNames: verbosity > 0 ) ) to \( testName) failed after \( duration) \( issues. description) . "
547
+ )
548
+ } else {
549
+ Message (
550
+ symbol: . pass( knownIssueCount: issues. knownIssueCount) ,
551
+ stringValue: " Passing \( arguments. count. counting ( " argument " ) ) \( testCase. labeledArguments ( includingQualifiedTypeNames: verbosity > 0 ) ) to \( testName) passed after \( duration) \( issues. description) . "
552
+ )
553
+ }
554
+ return [ message]
529
555
530
556
case let . iterationEnded( index) :
531
557
guard let iterationStartInstant = context. iterationStartInstant else {
@@ -568,6 +594,31 @@ extension Event.HumanReadableOutputRecorder {
568
594
}
569
595
}
570
596
597
+ extension Test . ID {
598
+ /// The key path in a test data graph representing this test ID.
599
+ fileprivate var keyPath : some Collection < Event . HumanReadableOutputRecorder . Context . TestDataKey > {
600
+ keyPathRepresentation. map { . string( $0) }
601
+ }
602
+ }
603
+
604
+ extension Event . Context {
605
+ /// The key path in a test data graph representing this event this context is
606
+ /// associated with, including its test and/or test case IDs.
607
+ fileprivate var keyPath : some Collection < Event . HumanReadableOutputRecorder . Context . TestDataKey > {
608
+ var keyPath = [ Event . HumanReadableOutputRecorder. Context. TestDataKey] ( )
609
+
610
+ if let test {
611
+ keyPath. append ( contentsOf: test. id. keyPath)
612
+
613
+ if let testCase {
614
+ keyPath. append ( . testCaseID( testCase. id) )
615
+ }
616
+ }
617
+
618
+ return keyPath
619
+ }
620
+ }
621
+
571
622
// MARK: - Codable
572
623
573
624
extension Event . HumanReadableOutputRecorder . Message : Codable { }
0 commit comments