@@ -36,7 +36,7 @@ extension Event {
3636
3737 /// A type that contains mutable context for
3838 /// ``Event/ConsoleOutputRecorder``.
39- private struct _Context {
39+ fileprivate struct Context {
4040 /// The instant at which the run started.
4141 var runStartInstant : Test . Clock . Instant ?
4242
@@ -51,6 +51,17 @@ extension Event {
5151 /// The number of test suites started or skipped during the run.
5252 var suiteCount = 0
5353
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+
5465 /// A type describing data tracked on a per-test basis.
5566 struct TestData {
5667 /// The instant at which the test started.
@@ -62,18 +73,15 @@ extension Event {
6273
6374 /// The number of known issues recorded for the test.
6475 var knownIssueCount = 0
65-
66- /// The number of test cases for the test.
67- var testCasesCount = 0
6876 }
6977
7078 /// Data tracked on a per-test basis.
71- var testData = Graph < String , TestData ? > ( )
79+ var testData = Graph < TestDataKey , TestData ? > ( )
7280 }
7381
7482 /// This event recorder's mutable context about events it has received,
7583 /// 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 ( ) )
7785
7886 /// Initialize a new human-readable event recorder.
7987 ///
@@ -128,7 +136,9 @@ extension Event.HumanReadableOutputRecorder {
128136 /// - graph: The graph to walk while counting issues.
129137 ///
130138 /// - 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 ) {
132142 guard let graph else {
133143 return ( 0 , 0 , 0 , 0 , " " )
134144 }
@@ -241,6 +251,7 @@ extension Event.HumanReadableOutputRecorder {
241251 0
242252 }
243253 let test = eventContext. test
254+ let keyPath = eventContext. keyPath
244255 let testName = if let test {
245256 if let displayName = test. displayName {
246257 if verbosity > 0 {
@@ -271,7 +282,7 @@ extension Event.HumanReadableOutputRecorder {
271282
272283 case . testStarted:
273284 let test = test!
274- context. testData [ test . id . keyPathRepresentation ] = . init( startInstant: instant)
285+ context. testData [ keyPath ] = . init( startInstant: instant)
275286 if test. isSuite {
276287 context. suiteCount += 1
277288 } else {
@@ -287,23 +298,17 @@ extension Event.HumanReadableOutputRecorder {
287298 }
288299
289300 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)
296302 if issue. isKnown {
297303 testData. knownIssueCount += 1
298304 } else {
299305 let issueCount = testData. issueCount [ issue. severity] ?? 0
300306 testData. issueCount [ issue. severity] = issueCount + 1
301307 }
302- context. testData [ id ] = testData
308+ context. testData [ keyPath ] = testData
303309
304310 case . testCaseStarted:
305- let test = test!
306- context. testData [ test. id. keyPathRepresentation] ? . testCasesCount += 1
311+ context. testData [ keyPath] = . init( startInstant: instant)
307312
308313 default :
309314 // These events do not manipulate the context structure.
@@ -384,13 +389,12 @@ extension Event.HumanReadableOutputRecorder {
384389
385390 case . testEnded:
386391 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)
389393 let testData = testDataGraph? . value ?? . init( startInstant: instant)
390394 let issues = _issueCounts ( in: testDataGraph)
391395 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 " ) ) "
394398 } else {
395399 " "
396400 }
@@ -517,15 +521,37 @@ extension Event.HumanReadableOutputRecorder {
517521 break
518522 }
519523
524+ let status = verbosity > 0 ? " started " : " "
525+
520526 return [
521527 Message (
522528 symbol: . default,
523- stringValue: " Passing \( arguments. count. counting ( " argument " ) ) \( testCase. labeledArguments ( includingQualifiedTypeNames: verbosity > 0 ) ) to \( testName) "
529+ stringValue: " Test case passing \( arguments. count. counting ( " argument " ) ) \( testCase. labeledArguments ( includingQualifiedTypeNames: verbosity > 0 ) ) to \( testName) \( status ) started. "
524530 )
525531 ]
526532
527533 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: " Test case 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: " Test case passing \( arguments. count. counting ( " argument " ) ) \( testCase. labeledArguments ( includingQualifiedTypeNames: verbosity > 0 ) ) to \( testName) passed after \( duration) \( issues. description) . "
552+ )
553+ }
554+ return [ message]
529555
530556 case let . iterationEnded( index) :
531557 guard let iterationStartInstant = context. iterationStartInstant else {
@@ -542,6 +568,7 @@ extension Event.HumanReadableOutputRecorder {
542568
543569 case . runEnded:
544570 let testCount = context. testCount
571+ let suiteCount = context. suiteCount
545572 let issues = _issueCounts ( in: context. testData)
546573 let runStartInstant = context. runStartInstant ?? instant
547574 let duration = runStartInstant. descriptionOfDuration ( to: instant)
@@ -550,14 +577,14 @@ extension Event.HumanReadableOutputRecorder {
550577 [
551578 Message (
552579 symbol: . fail,
553- stringValue: " Test run with \( testCount. counting ( " test " ) ) failed after \( duration) \( issues. description) . "
580+ stringValue: " Test run with \( testCount. counting ( " test " ) ) in \( suiteCount . counting ( " suite " ) ) failed after \( duration) \( issues. description) . "
554581 )
555582 ]
556583 } else {
557584 [
558585 Message (
559586 symbol: . pass( knownIssueCount: issues. knownIssueCount) ,
560- stringValue: " Test run with \( testCount. counting ( " test " ) ) passed after \( duration) \( issues. description) . "
587+ stringValue: " Test run with \( testCount. counting ( " test " ) ) in \( suiteCount . counting ( " suite " ) ) passed after \( duration) \( issues. description) . "
561588 )
562589 ]
563590 }
@@ -567,6 +594,31 @@ extension Event.HumanReadableOutputRecorder {
567594 }
568595}
569596
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+
570622// MARK: - Codable
571623
572624extension Event . HumanReadableOutputRecorder . Message : Codable { }
0 commit comments