@@ -387,16 +387,17 @@ func (r *regres) updateTestLists(client *gerrit.Client) error {
387
387
test := r .newTest (headHash )
388
388
defer test .cleanup ()
389
389
390
+ // Always need to checkout the change.
391
+ if err := test .checkout (); err != nil {
392
+ return cause .Wrap (err , "Failed to checkout '%s'" , headHash )
393
+ }
394
+
395
+ // Load the test lists.
390
396
testLists , err := test .loadTestLists (fullTestListRelPath )
391
397
if err != nil {
392
398
return cause .Wrap (err , "Failed to load full test lists for '%s'" , headHash )
393
399
}
394
400
395
- // Couldn't load cached results. Have to build them.
396
- if err := test .checkout (); err != nil {
397
- return cause .Wrap (err , "Failed to checkout '%s'" , headHash )
398
- }
399
-
400
401
// Build the change.
401
402
if err := test .build (); err != nil {
402
403
return cause .Wrap (err , "Failed to build '%s'" , headHash )
@@ -421,23 +422,17 @@ func (r *regres) updateTestLists(client *gerrit.Client) error {
421
422
}
422
423
423
424
log .Println ("Checking for existing test list" )
424
- changes , _ , err := client .Changes .QueryChanges (& gerrit.QueryChangeOptions {
425
- QueryOptions : gerrit.QueryOptions {
426
- Query : []string {fmt .Sprintf (`status:open+owner:"%v"` , r .gerritEmail )},
427
- Limit : 1 ,
428
- },
429
- })
425
+ existingChange , err := r .findTestListChange (client )
430
426
if err != nil {
431
- return cause . Wrap ( err , "Failed to checking for existing test list" )
427
+ return err
432
428
}
433
429
434
430
commitMsg := strings.Builder {}
435
431
commitMsg .WriteString (consts .TestListUpdateCommitSubjectPrefix + headHash .String ()[:8 ])
436
- if results != nil && len ( * changes ) > 0 {
432
+ if existingChange != nil {
437
433
// Reuse gerrit change ID if there's already a change up for review.
438
- id := (* changes )[0 ].ChangeID
439
434
commitMsg .WriteString ("\n \n " )
440
- commitMsg .WriteString ("Change-Id: " + id )
435
+ commitMsg .WriteString ("Change-Id: " + existingChange . ChangeID + " \n " )
441
436
}
442
437
443
438
if err := git .Commit (test .srcDir , commitMsg .String (), git.CommitFlags {
@@ -460,9 +455,86 @@ func (r *regres) updateTestLists(client *gerrit.Client) error {
460
455
log .Println ("Test results posted for review" )
461
456
}
462
457
458
+ change , err := r .findTestListChange (client )
459
+ if err != nil {
460
+ return err
461
+ }
462
+
463
+ if err := r .postMostCommonFailures (client , change , results ); err != nil {
464
+ return err
465
+ }
466
+
467
+ return nil
468
+ }
469
+
470
+ // postMostCommonFailures posts the most common failure cases as a review
471
+ // comment on the given change.
472
+ func (r * regres ) postMostCommonFailures (client * gerrit.Client , change * gerrit.ChangeInfo , results * CommitTestResults ) error {
473
+ const limit = 25
474
+
475
+ failures := results .commonFailures ()
476
+ if len (failures ) > limit {
477
+ failures = failures [:limit ]
478
+ }
479
+ sb := strings.Builder {}
480
+ sb .WriteString (fmt .Sprintf ("Top %v most common failures:\n " , len (failures )))
481
+ for _ , f := range failures {
482
+ lines := strings .Split (f .error , "\n " )
483
+ if len (lines ) == 1 {
484
+ line := lines [0 ]
485
+ if line != "" {
486
+ sb .WriteString (fmt .Sprintf (" %d occurrences: %v: %v\n " , f .count , f .status , line ))
487
+ } else {
488
+ sb .WriteString (fmt .Sprintf (" %d occurrences: %v\n " , f .count , f .status ))
489
+ }
490
+ } else {
491
+ sb .WriteString (fmt .Sprintf (" %d occurrences: %v:\n " , f .count , f .status ))
492
+ for _ , l := range lines {
493
+ sb .WriteString (" > " )
494
+ sb .WriteString (l )
495
+ sb .WriteString ("\n " )
496
+ }
497
+ }
498
+ }
499
+ msg := sb .String ()
500
+
501
+ if r .dryRun {
502
+ log .Printf ("DRY RUN: add most common failures to '%v':\n %v\n " , change .ChangeID , msg )
503
+ } else {
504
+ log .Printf ("Posting most common failures to '%s'\n " , change .ChangeID )
505
+ _ , _ , err := client .Changes .SetReview (change .ChangeID , change .CurrentRevision , & gerrit.ReviewInput {
506
+ Message : msg ,
507
+ Tag : "autogenerated:regress" ,
508
+ })
509
+ if err != nil {
510
+ return cause .Wrap (err , "Failed to post comments on change '%s'" , change .ChangeID )
511
+ }
512
+ }
463
513
return nil
464
514
}
465
515
516
+ func (r * regres ) findTestListChange (client * gerrit.Client ) (* gerrit.ChangeInfo , error ) {
517
+ log .Println ("Checking for existing test list change" )
518
+ changes , _ , err := client .Changes .QueryChanges (& gerrit.QueryChangeOptions {
519
+ QueryOptions : gerrit.QueryOptions {
520
+ Query : []string {fmt .Sprintf (`status:open+owner:"%v"` , r .gerritEmail )},
521
+ Limit : 1 ,
522
+ },
523
+ ChangeOptions : gerrit.ChangeOptions {
524
+ AdditionalFields : []string {"CURRENT_REVISION" },
525
+ },
526
+ })
527
+ if err != nil {
528
+ return nil , cause .Wrap (err , "Failed to checking for existing test list" )
529
+ }
530
+ if len (* changes ) > 0 {
531
+ // TODO: This currently assumes that only change changes from
532
+ // gerritEmail are test lists updates. This may not always be true.
533
+ return & (* changes )[0 ], nil
534
+ }
535
+ return nil , nil
536
+ }
537
+
466
538
// changeInfo holds the important information about a single, open change in
467
539
// gerrit.
468
540
type changeInfo struct {
@@ -827,6 +899,33 @@ func (r *CommitTestResults) save(path string) error {
827
899
return nil
828
900
}
829
901
902
+ type testStatusAndError struct {
903
+ status testlist.Status
904
+ error string
905
+ }
906
+
907
+ type commonFailure struct {
908
+ count int
909
+ testStatusAndError
910
+ }
911
+
912
+ func (r * CommitTestResults ) commonFailures () []commonFailure {
913
+ failures := map [testStatusAndError ]int {}
914
+ for _ , test := range r .Tests {
915
+ if ! test .Status .Failing () {
916
+ continue
917
+ }
918
+ key := testStatusAndError {test .Status , test .Err }
919
+ failures [key ] = failures [key ] + 1
920
+ }
921
+ out := make ([]commonFailure , 0 , len (failures ))
922
+ for failure , count := range failures {
923
+ out = append (out , commonFailure {count , failure })
924
+ }
925
+ sort .Slice (out , func (i , j int ) bool { return out [i ].count > out [j ].count })
926
+ return out
927
+ }
928
+
830
929
// compare returns a string describing all differences between two
831
930
// CommitTestResults. This string is used as the report message posted to the
832
931
// gerrit code review.
@@ -903,6 +1002,10 @@ func compare(old, new *CommitTestResults) string {
903
1002
{" Pass" , testlist .Pass },
904
1003
{" Fail" , testlist .Fail },
905
1004
{" Timeout" , testlist .Timeout },
1005
+ {" UNIMPLEMENTED()" , testlist .Unimplemented },
1006
+ {" UNREACHABLE()" , testlist .Unreachable },
1007
+ {" ASSERT()" , testlist .Assert },
1008
+ {" ABORT()" , testlist .Abort },
906
1009
{" Crash" , testlist .Crash },
907
1010
{" Not Supported" , testlist .NotSupported },
908
1011
{"Compatibility Warning" , testlist .CompatibilityWarning },
@@ -978,15 +1081,26 @@ func (r TestResult) String() string {
978
1081
return fmt .Sprintf ("%s: %s" , r .Test , r .Status )
979
1082
}
980
1083
981
- // Regular expression to parse the output of a dEQP test.
982
- var parseRE = regexp .MustCompile (`(Fail|Pass|NotSupported|CompatibilityWarning|QualityWarning) \(([\s\S]*)\)` )
1084
+ var (
1085
+ // Regular expression to parse the output of a dEQP test.
1086
+ deqpRE = regexp .MustCompile (`(Fail|Pass|NotSupported|CompatibilityWarning|QualityWarning) \(([^\)]*)\)` )
1087
+ // Regular expression to parse a test that failed due to UNIMPLEMENTED()
1088
+ unimplementedRE = regexp .MustCompile (`[^\n]*UNIMPLEMENTED:[^\n]*` )
1089
+ // Regular expression to parse a test that failed due to UNREACHABLE()
1090
+ unreachableRE = regexp .MustCompile (`[^\n]*UNREACHABLE:[^\n]*` )
1091
+ // Regular expression to parse a test that failed due to ASSERT()
1092
+ assertRE = regexp .MustCompile (`[^\n]*ASSERT\([^\)]*\)[^\n]*` )
1093
+ // Regular expression to parse a test that failed due to ABORT()
1094
+ abortRE = regexp .MustCompile (`[^\n]*ABORT:[^\n]*` )
1095
+ )
983
1096
984
1097
// deqpTestRoutine repeatedly runs the dEQP test executable exe with the tests
985
1098
// taken from tests. The output of the dEQP test is parsed, and the test result
986
1099
// is written to results.
987
1100
// deqpTestRoutine only returns once the tests chan has been closed.
988
1101
// deqpTestRoutine does not close the results chan.
989
1102
func (t * test ) deqpTestRoutine (exe string , tests <- chan string , results chan <- TestResult ) {
1103
+ nextTest:
990
1104
for name := range tests {
991
1105
// log.Printf("Running test '%s'\n", name)
992
1106
env := []string {
@@ -996,24 +1110,43 @@ func (t *test) deqpTestRoutine(exe string, tests <-chan string, results chan<- T
996
1110
"LIBC_FATAL_STDERR_=1" , // Put libc explosions into logs.
997
1111
}
998
1112
999
- out , err := shell .Exec (testTimeout , exe , filepath .Dir (exe ), env , "--deqp-surface-type=pbuffer" , "-n=" + name )
1113
+ outRaw , err := shell .Exec (testTimeout , exe , filepath .Dir (exe ), env , "--deqp-surface-type=pbuffer" , "-n=" + name )
1114
+ out := string (outRaw )
1115
+ out = strings .ReplaceAll (out , t .srcDir , "<SwiftShader>" )
1116
+ out = strings .ReplaceAll (out , exe , "<dEQP>" )
1000
1117
switch err .(type ) {
1001
1118
default :
1119
+ for _ , test := range []struct {
1120
+ re * regexp.Regexp
1121
+ s testlist.Status
1122
+ }{
1123
+ {unimplementedRE , testlist .Unimplemented },
1124
+ {unreachableRE , testlist .Unreachable },
1125
+ {assertRE , testlist .Assert },
1126
+ {abortRE , testlist .Abort },
1127
+ } {
1128
+ if s := test .re .FindString (out ); s != "" {
1129
+ results <- TestResult {
1130
+ Test : name ,
1131
+ Status : test .s ,
1132
+ Err : s ,
1133
+ }
1134
+ continue nextTest
1135
+ }
1136
+ }
1002
1137
results <- TestResult {
1003
1138
Test : name ,
1004
1139
Status : testlist .Crash ,
1005
- Err : cause .Wrap (err , string (out )).Error (),
1006
1140
}
1007
1141
case shell.ErrTimeout :
1008
1142
results <- TestResult {
1009
1143
Test : name ,
1010
1144
Status : testlist .Timeout ,
1011
- Err : cause .Wrap (err , string (out )).Error (),
1012
1145
}
1013
1146
case nil :
1014
- toks := parseRE .FindStringSubmatch (string ( out ) )
1147
+ toks := deqpRE .FindStringSubmatch (out )
1015
1148
if len (toks ) < 3 {
1016
- err := fmt .Sprintf ("Couldn't parse test '%v' output:\n %s" , name , string ( out ) )
1149
+ err := fmt .Sprintf ("Couldn't parse test '%v' output:\n %s" , name , out )
1017
1150
log .Println ("Warning: " , err )
1018
1151
results <- TestResult {Test : name , Status : testlist .Fail , Err : err }
1019
1152
continue
@@ -1034,7 +1167,7 @@ func (t *test) deqpTestRoutine(exe string, tests <-chan string, results chan<- T
1034
1167
}
1035
1168
results <- TestResult {Test : name , Status : testlist .Fail , Err : err }
1036
1169
default :
1037
- err := fmt .Sprintf ("Couldn't parse test output:\n %s" , string ( out ) )
1170
+ err := fmt .Sprintf ("Couldn't parse test output:\n %s" , out )
1038
1171
log .Println ("Warning: " , err )
1039
1172
results <- TestResult {Test : name , Status : testlist .Fail , Err : err }
1040
1173
}
0 commit comments