@@ -6,6 +6,7 @@ package report
66import (
77 "bufio"
88 "bytes"
9+ "encoding/json"
910 "flag"
1011 "fmt"
1112 "os"
@@ -41,9 +42,14 @@ type ParseTest struct {
4142 EndLine string
4243 Corrupted bool
4344 Suppressed bool
44- HasReport bool
45- Report []byte
45+
46+ // HasReport is in charge of both Report and TailReports.
47+ HasReport bool
48+ Report []byte
49+ TailReports [][]byte
50+
4651 Executor string
52+ ContextIDs []string
4753 // Only used in report parsing:
4854 corruptedReason string
4955}
@@ -55,6 +61,9 @@ func (test *ParseTest) Equal(other *ParseTest) bool {
5561 test .Type != other .Type {
5662 return false
5763 }
64+ if test .ContextIDs != nil && ! reflect .DeepEqual (test .ContextIDs , other .ContextIDs ) {
65+ return false
66+ }
5867 if ! reflect .DeepEqual (test .AltTitles , other .AltTitles ) {
5968 return false
6069 }
@@ -64,6 +73,9 @@ func (test *ParseTest) Equal(other *ParseTest) bool {
6473 if test .HasReport && ! bytes .Equal (test .Report , other .Report ) {
6574 return false
6675 }
76+ if test .HasReport && ! reflect .DeepEqual (test .TailReports , other .TailReports ) {
77+ return false
78+ }
6779 return test .Executor == other .Executor
6880}
6981
@@ -90,6 +102,10 @@ func (test *ParseTest) Headers() []byte {
90102 if test .Executor != "" {
91103 fmt .Fprintf (buf , "EXECUTOR: %s\n " , test .Executor )
92104 }
105+ if strings .Join (test .ContextIDs , "" ) != "" {
106+ jsonData , _ := json .Marshal (test .ContextIDs )
107+ fmt .Fprintf (buf , "CONTEXTS: %s\n " , jsonData )
108+ }
93109 return buf .Bytes ()
94110}
95111
@@ -98,8 +114,8 @@ func testParseFile(t *testing.T, reporter *Reporter, fn string) {
98114 testParseImpl (t , reporter , test )
99115}
100116
101- func parseReport (t * testing.T , reporter * Reporter , fn string ) * ParseTest {
102- data , err := os .ReadFile (fn )
117+ func parseReport (t * testing.T , reporter * Reporter , testFileName string ) * ParseTest {
118+ data , err := os .ReadFile (testFileName )
103119 if err != nil {
104120 t .Fatal (err )
105121 }
@@ -109,10 +125,11 @@ func parseReport(t *testing.T, reporter *Reporter, fn string) *ParseTest {
109125 phaseHeaders = iota
110126 phaseLog
111127 phaseReport
128+ phaseTailReports
112129 )
113130 phase := phaseHeaders
114131 test := & ParseTest {
115- FileName : fn ,
132+ FileName : testFileName ,
116133 }
117134 prevEmptyLine := false
118135 s := bufio .NewScanner (bytes .NewReader (data ))
@@ -134,8 +151,20 @@ func parseReport(t *testing.T, reporter *Reporter, fn string) *ParseTest {
134151 test .Log = append (test .Log , '\n' )
135152 }
136153 case phaseReport :
137- test .Report = append (test .Report , s .Bytes ()... )
138- test .Report = append (test .Report , '\n' )
154+ if string (s .Bytes ()) == "TAIL REPORTS:" {
155+ test .TailReports = [][]byte {{}}
156+ phase = phaseTailReports
157+ } else {
158+ test .Report = append (test .Report , s .Bytes ()... )
159+ test .Report = append (test .Report , '\n' )
160+ }
161+ case phaseTailReports :
162+ if string (s .Bytes ()) == reportSeparator {
163+ test .TailReports = append (test .TailReports , []byte {})
164+ continue
165+ }
166+ test .TailReports [len (test .TailReports )- 1 ] = append (test .TailReports [len (test .TailReports )- 1 ], s .Bytes ()... )
167+ test .TailReports [len (test .TailReports )- 1 ] = append (test .TailReports [len (test .TailReports )- 1 ], []byte {'\n' }... )
139168 }
140169 prevEmptyLine = len (s .Bytes ()) == 0
141170 }
@@ -160,6 +189,7 @@ func parseHeaderLine(t *testing.T, test *ParseTest, ln string) {
160189 corruptedPrefix = "CORRUPTED: "
161190 suppressedPrefix = "SUPPRESSED: "
162191 executorPrefix = "EXECUTOR: "
192+ contextidPrefix = "CONTEXTS: "
163193 )
164194 switch {
165195 case strings .HasPrefix (ln , "#" ):
@@ -195,60 +225,75 @@ func parseHeaderLine(t *testing.T, test *ParseTest, ln string) {
195225 }
196226 case strings .HasPrefix (ln , executorPrefix ):
197227 test .Executor = ln [len (executorPrefix ):]
228+ case strings .HasPrefix (ln , contextidPrefix ):
229+ err := json .Unmarshal ([]byte (ln [len (contextidPrefix ):]), & test .ContextIDs )
230+ if err != nil {
231+ t .Fatalf ("contextIDs unmarshaling error: %q" , err )
232+ }
198233 default :
199234 t .Fatalf ("unknown header field %q" , ln )
200235 }
201236}
202237
203- func testFromReport ( rep * Report ) * ParseTest {
204- if rep == nil {
238+ func testFromReports ( reps ... * Report ) * ParseTest {
239+ if reps == nil || len ( reps ) > 0 && reps [ 0 ] == nil {
205240 return & ParseTest {}
206241 }
207242 ret := & ParseTest {
208- Title : rep .Title ,
209- AltTitles : rep .AltTitles ,
210- Corrupted : rep .Corrupted ,
211- corruptedReason : rep .CorruptedReason ,
212- Suppressed : rep .Suppressed ,
213- Type : crash .TitleToType (rep .Title ),
214- Frame : rep .Frame ,
215- Report : rep .Report ,
216- }
217- if rep .Executor != nil {
218- ret .Executor = fmt .Sprintf ("proc=%d, id=%d" , rep .Executor .ProcID , rep .Executor .ExecID )
243+ Title : reps [ 0 ] .Title ,
244+ AltTitles : reps [ 0 ] .AltTitles ,
245+ Corrupted : reps [ 0 ] .Corrupted ,
246+ corruptedReason : reps [ 0 ] .CorruptedReason ,
247+ Suppressed : reps [ 0 ] .Suppressed ,
248+ Type : crash .TitleToType (reps [ 0 ] .Title ),
249+ Frame : reps [ 0 ] .Frame ,
250+ Report : reps [ 0 ] .Report ,
251+ }
252+ if reps [ 0 ] .Executor != nil {
253+ ret .Executor = fmt .Sprintf ("proc=%d, id=%d" , reps [ 0 ] .Executor .ProcID , reps [ 0 ] .Executor .ExecID )
219254 }
220255 sort .Strings (ret .AltTitles )
256+ ret .ContextIDs = append (ret .ContextIDs , reps [0 ].ContextID )
257+ for i := 1 ; i < len (reps ); i ++ {
258+ ret .TailReports = append (ret .TailReports , reps [i ].Report )
259+ ret .ContextIDs = append (ret .ContextIDs , reps [i ].ContextID )
260+ }
221261 return ret
222262}
223263
224264func testParseImpl (t * testing.T , reporter * Reporter , test * ParseTest ) {
225- rep := reporter .Parse (test .Log )
265+ gotReports := ParseAll (reporter , test .Log , 0 )
266+
267+ var firstReport * Report
268+ if len (gotReports ) > 0 {
269+ firstReport = gotReports [0 ]
270+ }
226271 containsCrash := reporter .ContainsCrash (test .Log )
227272 expectCrash := (test .Title != "" )
228273 if expectCrash && ! containsCrash {
229274 t .Fatalf ("did not find crash" )
230275 }
231276 if ! expectCrash && containsCrash {
232- t .Fatalf ("found unexpected crash" )
277+ t .Fatalf ("found unexpected crash: %s" , firstReport . Title )
233278 }
234- if rep != nil && rep .Title == "" {
279+ if firstReport != nil && firstReport .Title == "" {
235280 t .Fatalf ("found crash, but title is empty" )
236281 }
237- parsed := testFromReport ( rep )
282+ parsed := testFromReports ( gotReports ... )
238283 if ! test .Equal (parsed ) {
239284 if * flagUpdate && test .StartLine + test .EndLine == "" {
240285 updateReportTest (t , test , parsed )
241286 }
242287 t .Fatalf ("want:\n %s\n got:\n %sCorrupted reason: %q" ,
243288 test .Headers (), parsed .Headers (), parsed .corruptedReason )
244289 }
245- if parsed .Title != "" && len (rep .Report ) == 0 {
290+ if parsed .Title != "" && len (firstReport .Report ) == 0 {
246291 t .Fatalf ("found crash message but report is empty" )
247292 }
248- if rep == nil {
293+ if firstReport == nil {
249294 return
250295 }
251- checkReport (t , reporter , rep , test )
296+ checkReport (t , reporter , firstReport , test )
252297}
253298
254299func checkReport (t * testing.T , reporter * Reporter , rep * Report , test * ParseTest ) {
@@ -285,11 +330,6 @@ func checkReport(t *testing.T, reporter *Reporter, rep *Report, test *ParseTest)
285330 if rep1 == nil || rep1 .Title != rep .Title || rep1 .StartPos != rep .StartPos {
286331 t .Fatalf ("did not find the same report from rep.StartPos=%v" , rep .StartPos )
287332 }
288- // If we parse from EndPos, we must not find the same report.
289- rep2 := reporter .ParseFrom (test .Log , rep .EndPos )
290- if rep2 != nil && rep2 .Title == rep .Title {
291- t .Fatalf ("found the same report after rep.EndPos=%v" , rep .EndPos )
292- }
293333 }
294334}
295335
@@ -303,6 +343,10 @@ func updateReportTest(t *testing.T, test, parsed *ParseTest) {
303343 fmt .Fprintf (buf , "\n %s" , test .Log )
304344 if test .HasReport {
305345 fmt .Fprintf (buf , "REPORT:\n %s" , parsed .Report )
346+ if len (parsed .TailReports ) > 0 {
347+ fmt .Fprintf (buf , "TAIL REPORTS:\n " )
348+ buf .Write (bytes .Join (parsed .TailReports , []byte (reportSeparator + "\n " )))
349+ }
306350 }
307351 if err := os .WriteFile (test .FileName , buf .Bytes (), 0640 ); err != nil {
308352 t .Logf ("failed to update test file: %v" , err )
@@ -395,7 +439,7 @@ func testSymbolizeFile(t *testing.T, reporter *Reporter, fn string) {
395439 if err != nil {
396440 t .Fatalf ("failed to symbolize: %v" , err )
397441 }
398- parsed := testFromReport (rep )
442+ parsed := testFromReports (rep )
399443 if ! test .Equal (parsed ) {
400444 if * flagUpdate {
401445 updateReportTest (t , test , parsed )
0 commit comments