test-runner: Add option to limit number of threads

Fixed: tint:830
Change-Id: I46696d0f72760549743f95a35ea21fef96269146
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/51924
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
Ben Clayton 2021-05-21 18:02:33 +00:00 committed by Tint LUCI CQ
parent 6cf7f2eca5
commit a53a3b8c19
1 changed files with 97 additions and 67 deletions

View File

@ -22,6 +22,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"runtime"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -68,10 +69,12 @@ optional flags:`)
func run() error { func run() error {
var formatList, filter string var formatList, filter string
numCPU := runtime.NumCPU()
generateExpected := false generateExpected := false
flag.StringVar(&formatList, "format", "all", "comma separated list of formats to emit. Possible values are: all, wgsl, spvasm, msl, hlsl") flag.StringVar(&formatList, "format", "all", "comma separated list of formats to emit. Possible values are: all, wgsl, spvasm, msl, hlsl")
flag.StringVar(&filter, "filter", "**.wgsl, **.spvasm, **.spv", "comma separated list of glob patterns for test files") flag.StringVar(&filter, "filter", "**.wgsl, **.spvasm, **.spv", "comma separated list of glob patterns for test files")
flag.BoolVar(&generateExpected, "generate-expected", false, "create or update all expected outputs") flag.BoolVar(&generateExpected, "generate-expected", false, "create or update all expected outputs")
flag.IntVar(&numCPU, "j", numCPU, "maximum number of concurrent threads to run tests")
flag.Usage = showUsage flag.Usage = showUsage
flag.Parse() flag.Parse()
@ -144,82 +147,41 @@ func run() error {
} }
} }
// Structures to hold the results of the tests results := make([]map[outputFormat]*status, len(files))
type statusCode string jobs := make(chan job, 256)
const (
fail statusCode = "FAIL"
pass statusCode = "PASS"
skip statusCode = "SKIP"
)
type status struct {
code statusCode
err error
}
type result map[outputFormat]status
results := make([]result, len(files))
// In parallel... // Spawn numCPU job runners...
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(len(files)) wg.Add(numCPU)
for i, file := range files { // For each test file... for cpu := 0; cpu < numCPU; cpu++ {
i, file := i, filepath.Join(dir, file)
go func() { go func() {
defer wg.Done() defer wg.Done()
r := result{} for job := range jobs {
for _, format := range formats { // For each output format... job.run(exe, generateExpected)
// Is there an expected output?
expected := loadExpectedFile(file, format)
if strings.HasPrefix(expected, "SKIP") { // Special SKIP token
r[format] = status{code: skip}
continue
}
// Invoke the compiler...
var err error
if ok, out := invoke(exe, file, "--format", string(format), "--dawn-validation"); ok {
if generateExpected {
// If --generate-expected was passed, write out the output
err = saveExpectedFile(file, format, out)
} else if expected != "" && expected != out {
// Expected output did not match
dmp := diffmatchpatch.New()
diff := dmp.DiffPrettyText(dmp.DiffMain(expected, out, true))
err = fmt.Errorf(`Output was not as expected
--------------------------------------------------------------------------------
-- Expected: --
--------------------------------------------------------------------------------
%s
--------------------------------------------------------------------------------
-- Got: --
--------------------------------------------------------------------------------
%s
--------------------------------------------------------------------------------
-- Diff: --
--------------------------------------------------------------------------------
%s`,
expected, out, diff)
}
} else {
// Compiler returned a non-zero exit code
err = fmt.Errorf("%s", out)
}
if err != nil {
r[format] = status{code: fail, err: err}
} else {
r[format] = status{code: pass}
}
} }
results[i] = r
}() }()
} }
// Issue the jobs...
for i, file := range files { // For each test file...
file := filepath.Join(dir, file)
fileResults := map[outputFormat]*status{}
for _, format := range formats { // For each output format...
result := &status{}
jobs <- job{
file: file,
format: format,
result: result,
}
fileResults[format] = result
}
results[i] = fileResults
}
// Wait for the jobs to all finish...
close(jobs)
wg.Wait() wg.Wait()
// At this point all the tests have been run
// Time to print the outputs // Time to print the outputs
// Start by printing the error message for any file x format combinations // Start by printing the error message for any file x format combinations
@ -331,6 +293,74 @@ func run() error {
return nil return nil
} }
// Structures to hold the results of the tests
type statusCode string
const (
fail statusCode = "FAIL"
pass statusCode = "PASS"
skip statusCode = "SKIP"
)
type status struct {
code statusCode
err error
}
type job struct {
file string
format outputFormat
result *status
}
func (j job) run(exe string, generateExpected bool) {
// Is there an expected output?
expected := loadExpectedFile(j.file, j.format)
if strings.HasPrefix(expected, "SKIP") { // Special SKIP token
*j.result = status{code: skip}
return
}
// Invoke the compiler...
var err error
if ok, out := invoke(exe, j.file, "--format", string(j.format), "--dawn-validation"); ok {
if generateExpected {
// If --generate-expected was passed, write out the output
err = saveExpectedFile(j.file, j.format, out)
} else if expected != "" && expected != out {
// Expected output did not match
dmp := diffmatchpatch.New()
diff := dmp.DiffPrettyText(dmp.DiffMain(expected, out, true))
err = fmt.Errorf(`Output was not as expected
--------------------------------------------------------------------------------
-- Expected: --
--------------------------------------------------------------------------------
%s
--------------------------------------------------------------------------------
-- Got: --
--------------------------------------------------------------------------------
%s
--------------------------------------------------------------------------------
-- Diff: --
--------------------------------------------------------------------------------
%s`,
expected, out, diff)
}
} else {
// Compiler returned a non-zero exit code
err = fmt.Errorf("%s", out)
}
if err != nil {
*j.result = status{code: fail, err: err}
} else {
*j.result = status{code: pass}
}
}
// loadExpectedFile loads the expected output file for the test file at 'path' // loadExpectedFile loads the expected output file for the test file at 'path'
// and the output format 'format'. If the file does not exist, or cannot be // and the output format 'format'. If the file does not exist, or cannot be
// read, then an empty string is returned. // read, then an empty string is returned.