@@ -23,6 +23,16 @@ type TestEvent struct {
2323 Output string
2424}
2525
26+ const (
27+ ActionBuildOutput = "build-output"
28+ ActionBuildFail = "build-fail"
29+ ActionOutput = "output"
30+ ActionRun = "run"
31+ ActionPass = "pass"
32+ ActionFail = "fail"
33+ ActionSkip = "skip"
34+ )
35+
2636var buildOutput []string
2737
2838func ParseTestResults (r io.Reader , verbose bool , env * ctrf.Environment ) (* ctrf.Report , error ) {
@@ -33,6 +43,11 @@ func ParseTestResults(r io.Reader, verbose bool, env *ctrf.Environment) (*ctrf.R
3343 report .Results .Summary .Start = time .Now ().UnixNano () / int64 (time .Millisecond )
3444
3545 testStartTimes := make (map [string ]int64 )
46+ extraMap := make (map [string ]any )
47+ buildOutputEvents := make ([]TestEvent , 0 )
48+ buildFailEvents := make ([]TestEvent , 0 )
49+
50+ report .Results .Extra = extraMap
3651
3752 for {
3853 var event TestEvent
@@ -44,56 +59,45 @@ func ParseTestResults(r io.Reader, verbose bool, env *ctrf.Environment) (*ctrf.R
4459 testEvents = append (testEvents , event )
4560
4661 if verbose {
47- if event .Action == "build-output" || event .Action == "output" {
62+ if event .Action == ActionBuildOutput || event .Action == ActionOutput {
4863 fmt .Print (event .Output )
4964 }
5065 }
5166 }
5267
5368 for i , event := range testEvents {
69+ // If we see any test failures, mark an overall failure in the Extra fields
70+ if event .Action == ActionFail {
71+ extraMap ["FailedBuild" ] = true
72+ }
5473
55- if event .Action == "build-output" || event .Action == "build-fail" || event .Action == "fail" {
56- if report .Results .Extra == nil {
57- report .Results .Extra = make (map [string ]any )
58- }
59- extraMap , ok := report .Results .Extra .(map [string ]any )
60- if ! ok {
61- return nil , fmt .Errorf ("expected a map, but got %T instead" , report .Results .Extra )
62- }
63-
64- if event .Action == "fail" {
65- if _ , ok := extraMap ["FailedBuild" ]; ! ok {
66- extraMap ["FailedBuild" ] = true
67- }
68- }
74+ if event .Action == ActionBuildOutput {
75+ // Capture the full events to the extras
76+ buildOutputEvents = append (buildOutputEvents , event )
77+ extraMap ["buildOutput" ] = buildOutputEvents
6978
70- if event .Action == "build-output" {
71- if _ , ok := extraMap ["buildOutput" ]; ! ok {
72- extraMap ["buildOutput" ] = []TestEvent {}
73- }
74- buildOutputEvents := extraMap ["buildOutput" ].([]TestEvent )
75- extraMap ["buildOutput" ] = append (buildOutputEvents , event )
76- buildOutput = append (buildOutput , event .Output )
77- continue
78- }
79+ // Capture the actual build output as well
80+ buildOutput = append (buildOutput , event .Output )
81+ continue
82+ }
7983
80- if event .Action == "build-fail" {
81- if _ , ok := extraMap ["buildFail" ]; ! ok {
82- extraMap ["buildFail" ] = []TestEvent {}
83- }
84- buildFailEvents := extraMap ["buildFail" ].([]TestEvent )
85- extraMap ["buildFail" ] = append (buildFailEvents , event )
86- break
87- }
84+ // Mark if we see a build failure in the extras field
85+ if event .Action == ActionBuildFail {
86+ buildFailEvents = append (buildFailEvents , event )
87+ extraMap ["buildFail" ] = buildFailEvents
88+ break
8889 }
8990
90- if event .Action == "output" {
91+ if event .Action == ActionOutput {
9192 buildOutput = append (buildOutput , event .Output )
9293 }
9394
95+ // From this point, we only care about events associated with an actual test
9496 if event .Test == "" {
9597 continue
9698 }
99+
100+ // Parse timestamp data from the event
97101 eventTime , err := parseTimeString (event .Time )
98102 if err != nil {
99103 fmt .Fprintf (os .Stderr , "error parsing test event start time '%s' : %v\n " , event .Time , err )
@@ -107,14 +111,14 @@ func ParseTestResults(r io.Reader, verbose bool, env *ctrf.Environment) (*ctrf.R
107111
108112 // If this is a "run" event, record the start time of the test. We'll look this up later when
109113 // we process the "pass"/"fail"/"skip" event for the test to create the TestResult
110- if event .Action == "run" {
114+ if event .Action == ActionRun {
111115 testStartTimes [testNameKey (event .Package , event .Test )] = eventTime
112116 }
113117 }
114118
115119 // From this point on, we only deal with pass, fail, and skip events, which indicate that the
116120 // test has completed, and we can create/update a TestResult for it.
117- if event .Action == "pass" || event .Action == "fail" || event .Action == "skip" {
121+ if event .Action == ActionPass || event .Action == ActionFail || event .Action == ActionSkip {
118122 // Look up the start time, and use this event's time as the endTime, to mark the start/stop times
119123 // for the test result. Duration we get from the event.Elapsed field, which better takes into
120124 // account parallel tests, setup/teardown time, etc...
@@ -127,7 +131,7 @@ func ParseTestResults(r io.Reader, verbose bool, env *ctrf.Environment) (*ctrf.R
127131 // Determine the message for this test result. We only include messages on failures though,
128132 // per the CTRF spec, so if this is not a failure, we pass an empty string for the message.
129133 message := ""
130- if event .Action == "fail" {
134+ if event .Action == ActionFail {
131135 message = getMessagesForTest (testEvents , i , event .Package , event .Test , startTime )
132136 }
133137
@@ -158,7 +162,7 @@ func ParseTestResults(r io.Reader, verbose bool, env *ctrf.Environment) (*ctrf.R
158162 return report , nil
159163}
160164
161- // addResult adds a new test result to the report, filling out all the relevant details
165+ // addResult adds a new test result to the report, filling out all the relevant details.
162166func addResult (report * ctrf.Report , result * ctrf.TestResult ) {
163167 // Update the overall test count in the Summary
164168 report .Results .Summary .Tests ++
@@ -171,59 +175,63 @@ func addResult(report *ctrf.Report, result *ctrf.TestResult) {
171175 report .Results .Summary .Failed ++
172176 case ctrf .TestSkipped :
173177 report .Results .Summary .Skipped ++
178+ case ctrf .TestPending :
179+ report .Results .Summary .Pending ++
180+ default :
181+ report .Results .Summary .Other ++
174182 }
175183
176184 // Append the result to the report's results
177185 report .Results .Tests = append (report .Results .Tests , result )
178186}
179187
180- func updateResult (report * ctrf.Report , existing , new * ctrf.TestResult ) {
188+ func updateResult (report * ctrf.Report , oldResult , newResult * ctrf.TestResult ) {
181189 // If the existing result does not have a retries field, initialize it, and move the
182190 // results to the first RetryAttempts object
183- if existing .RetryAttempts == nil {
184- existing .Retries = 1
185- existing .RetryAttempts = append (existing .RetryAttempts , ctrf.RetryAttempt {
191+ if oldResult .RetryAttempts == nil {
192+ oldResult .Retries = 1
193+ oldResult .RetryAttempts = append (oldResult .RetryAttempts , ctrf.RetryAttempt {
186194 Attempt : 1 ,
187- Status : existing .Status ,
188- Message : existing .Message ,
189- Duration : existing .Duration ,
190- Start : existing .Start ,
191- Stop : existing .Stop ,
195+ Status : oldResult .Status ,
196+ Message : oldResult .Message ,
197+ Duration : oldResult .Duration ,
198+ Start : oldResult .Start ,
199+ Stop : oldResult .Stop ,
192200 })
193201 }
194202
195203 // If this is a pass after a failure, mark the test as flaky, not failed,
196204 // and update the summary counts accordingly
197- if existing .Status == ctrf .TestFailed && new .Status == ctrf .TestPassed {
198- existing .Flaky = true
205+ if oldResult .Status == ctrf .TestFailed && newResult .Status == ctrf .TestPassed {
206+ oldResult .Flaky = true
199207 report .Results .Summary .Flaky ++
200208 report .Results .Summary .Failed --
201209 }
202210
203211 // Update the overall test status to match that of the new result
204- existing .Status = new .Status
212+ oldResult .Status = newResult .Status
205213
206214 // Clear out the top-level message on the overall result, since the messages are in the retries
207- existing .Message = ""
215+ oldResult .Message = ""
208216
209217 // Update the times of the overall test result
210- existing .Duration += new .Duration
211- if new .Stop > existing .Stop {
212- existing .Stop = new .Stop
218+ oldResult .Duration += newResult .Duration
219+ if newResult .Stop > oldResult .Stop {
220+ oldResult .Stop = newResult .Stop
213221 }
214- if new .Start < existing .Start {
215- existing .Start = new .Start
222+ if newResult .Start < oldResult .Start {
223+ oldResult .Start = newResult .Start
216224 }
217225
218226 // Now add the new attempt to the retries
219- existing .Retries ++
220- existing .RetryAttempts = append (existing .RetryAttempts , ctrf.RetryAttempt {
221- Attempt : existing .Retries ,
222- Status : new .Status ,
223- Message : new .Message ,
224- Duration : new .Duration ,
225- Start : new .Start ,
226- Stop : new .Stop ,
227+ oldResult .Retries ++
228+ oldResult .RetryAttempts = append (oldResult .RetryAttempts , ctrf.RetryAttempt {
229+ Attempt : oldResult .Retries ,
230+ Status : newResult .Status ,
231+ Message : newResult .Message ,
232+ Duration : newResult .Duration ,
233+ Start : newResult .Start ,
234+ Stop : newResult .Stop ,
227235 })
228236}
229237
@@ -234,11 +242,11 @@ func testNameKey(suite, name string) string {
234242
235243func actionToTestResult (action string ) ctrf.TestStatus {
236244 switch action {
237- case "pass" :
245+ case ActionPass :
238246 return ctrf .TestPassed
239- case "fail" :
247+ case ActionFail :
240248 return ctrf .TestFailed
241- case "skip" :
249+ case ActionSkip :
242250 return ctrf .TestSkipped
243251 default :
244252 return ctrf .TestOther
@@ -310,7 +318,7 @@ func getMessagesForTest(testEvents []TestEvent, index int, packageName, testName
310318 }
311319 }
312320
313- if testEvents [i ].Action == "output" {
321+ if testEvents [i ].Action == ActionOutput {
314322 messages = append (messages , testEvents [i ].Output )
315323 }
316324 }
0 commit comments