@ -22,13 +22,7 @@ import (
"sort"
"sort"
"strings"
"strings"
"time"
"time"
"unicode"
"github.com/dave/courtney/scanner"
"github.com/dave/courtney/shared"
"github.com/dave/courtney/tester"
"github.com/dave/patsy"
"github.com/dave/patsy/vos"
"tailscale.com/cmd/testwrapper/flakytest"
"tailscale.com/cmd/testwrapper/flakytest"
"tailscale.com/util/slicesx"
"tailscale.com/util/slicesx"
)
)
@ -238,30 +232,6 @@ func main() {
fmt . Printf ( "%s\t%s\t%.3fs\n" , outcome , pkg , runtime . Seconds ( ) )
fmt . Printf ( "%s\t%s\t%.3fs\n" , outcome , pkg , runtime . Seconds ( ) )
}
}
// Check for -coverprofile argument and filter it out
combinedCoverageFilename := ""
filteredGoTestArgs := make ( [ ] string , 0 , len ( goTestArgs ) )
preceededByCoverProfile := false
for _ , arg := range goTestArgs {
if arg == "-coverprofile" {
preceededByCoverProfile = true
} else if preceededByCoverProfile {
combinedCoverageFilename = strings . TrimSpace ( arg )
preceededByCoverProfile = false
} else {
filteredGoTestArgs = append ( filteredGoTestArgs , arg )
}
}
goTestArgs = filteredGoTestArgs
runningWithCoverage := combinedCoverageFilename != ""
if runningWithCoverage {
fmt . Printf ( "Will log coverage to %v\n" , combinedCoverageFilename )
}
// Keep track of all test coverage files. With each retry, we'll end up
// with additional coverage files that will be combined when we finish.
coverageFiles := make ( [ ] string , 0 )
for len ( toRun ) > 0 {
for len ( toRun ) > 0 {
var thisRun * nextRun
var thisRun * nextRun
thisRun , toRun = toRun [ 0 ] , toRun [ 1 : ]
thisRun , toRun = toRun [ 0 ] , toRun [ 1 : ]
@ -275,27 +245,13 @@ func main() {
fmt . Printf ( "\n\nAttempt #%d: Retrying flaky tests:\n\nflakytest failures JSON: %s\n\n" , thisRun . attempt , j )
fmt . Printf ( "\n\nAttempt #%d: Retrying flaky tests:\n\nflakytest failures JSON: %s\n\n" , thisRun . attempt , j )
}
}
goTestArgsWithCoverage := testArgs
if runningWithCoverage {
coverageFile := fmt . Sprintf ( "/tmp/coverage_%d.out" , thisRun . attempt )
coverageFiles = append ( coverageFiles , coverageFile )
goTestArgsWithCoverage = make ( [ ] string , len ( goTestArgs ) , len ( goTestArgs ) + 2 )
copy ( goTestArgsWithCoverage , goTestArgs )
goTestArgsWithCoverage = append (
goTestArgsWithCoverage ,
fmt . Sprintf ( "-coverprofile=%v" , coverageFile ) ,
"-covermode=set" ,
"-coverpkg=./..." ,
)
}
toRetry := make ( map [ string ] [ ] * testAttempt ) // pkg -> tests to retry
toRetry := make ( map [ string ] [ ] * testAttempt ) // pkg -> tests to retry
for _ , pt := range thisRun . tests {
for _ , pt := range thisRun . tests {
ch := make ( chan * testAttempt )
ch := make ( chan * testAttempt )
runErr := make ( chan error , 1 )
runErr := make ( chan error , 1 )
go func ( ) {
go func ( ) {
defer close ( runErr )
defer close ( runErr )
runErr <- runTests ( ctx , thisRun . attempt , pt , goTestArgs WithCoverage , testArgs , ch )
runErr <- runTests ( ctx , thisRun . attempt , pt , goTestArgs , testArgs , ch )
} ( )
} ( )
var failed bool
var failed bool
@ -372,107 +328,4 @@ func main() {
}
}
toRun = append ( toRun , nextRun )
toRun = append ( toRun , nextRun )
}
}
if runningWithCoverage {
intermediateCoverageFilename := "/tmp/coverage.out_intermediate"
if err := combineCoverageFiles ( intermediateCoverageFilename , coverageFiles ) ; err != nil {
fmt . Printf ( "error combining coverage files: %v\n" , err )
os . Exit ( 2 )
}
if err := processCoverageWithCourtney ( intermediateCoverageFilename , combinedCoverageFilename , testArgs ) ; err != nil {
fmt . Printf ( "error processing coverage with courtney: %v\n" , err )
os . Exit ( 3 )
}
fmt . Printf ( "Wrote combined coverage to %v\n" , combinedCoverageFilename )
}
}
func combineCoverageFiles ( intermediateCoverageFilename string , coverageFiles [ ] string ) error {
combinedCoverageFile , err := os . OpenFile ( intermediateCoverageFilename , os . O_CREATE | os . O_WRONLY | os . O_TRUNC , 0644 )
if err != nil {
return fmt . Errorf ( "create /tmp/coverage.out: %w" , err )
}
defer combinedCoverageFile . Close ( )
w := bufio . NewWriter ( combinedCoverageFile )
defer w . Flush ( )
for fileNumber , coverageFile := range coverageFiles {
f , err := os . Open ( coverageFile )
if err != nil {
return fmt . Errorf ( "open %v: %w" , coverageFile , err )
}
defer f . Close ( )
in := bufio . NewReader ( f )
line := 0
for {
r , _ , err := in . ReadRune ( )
if err != nil {
if err != io . EOF {
return fmt . Errorf ( "read %v: %w" , coverageFile , err )
}
break
}
// On all but the first coverage file, skip the coverage file header
if fileNumber > 0 && line == 0 {
continue
}
if r == '\n' {
line ++
}
// filter for only printable characters because coverage file sometimes includes junk on 2nd line
if unicode . IsPrint ( r ) || r == '\n' {
if _ , err := w . WriteRune ( r ) ; err != nil {
return fmt . Errorf ( "write %v: %w" , combinedCoverageFile . Name ( ) , err )
}
}
}
}
return nil
}
// processCoverageWithCourtney post-processes code coverage to exclude less
// meaningful sections like 'if err != nil { return err}', as well as
// anything marked with a '// notest' comment.
//
// instead of running the courtney as a separate program, this embeds
// courtney for easier integration.
func processCoverageWithCourtney ( intermediateCoverageFilename , combinedCoverageFilename string , testArgs [ ] string ) error {
env := vos . Os ( )
setup := & shared . Setup {
Env : vos . Os ( ) ,
Paths : patsy . NewCache ( env ) ,
TestArgs : testArgs ,
Load : intermediateCoverageFilename ,
Output : combinedCoverageFilename ,
}
if err := setup . Parse ( testArgs ) ; err != nil {
return fmt . Errorf ( "parse args: %w" , err )
}
s := scanner . New ( setup )
if err := s . LoadProgram ( ) ; err != nil {
return fmt . Errorf ( "load program: %w" , err )
}
if err := s . ScanPackages ( ) ; err != nil {
return fmt . Errorf ( "scan packages: %w" , err )
}
t := tester . New ( setup )
if err := t . Load ( ) ; err != nil {
return fmt . Errorf ( "load: %w" , err )
}
if err := t . ProcessExcludes ( s . Excludes ) ; err != nil {
return fmt . Errorf ( "process excludes: %w" , err )
}
if err := t . Save ( ) ; err != nil {
return fmt . Errorf ( "save: %w" , err )
}
return nil
}
}