tools/run-cts: Fix code coverage in `--isolate` mode

Use the new `cmdline.ts --coverage` flag in https://github.com/gpuweb/cts/pull/2206 to write out the per-test coverage to a unique file.

Change-Id: Iea273217eede2fa615b78cd6f69f036976dcfd35
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/117883
Kokoro: Ben Clayton <bclayton@google.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
This commit is contained in:
Ben Clayton 2023-03-09 23:08:02 +00:00 committed by Dawn LUCI CQ
parent cc2e9eca1a
commit a92a8078c5
2 changed files with 31 additions and 36 deletions

View File

@ -850,16 +850,10 @@ func (r *runner) runParallelIsolated(ctx context.Context) error {
for i := 0; i < r.numRunners; i++ { for i := 0; i < r.numRunners; i++ {
wg.Add(1) wg.Add(1)
profraw := ""
if r.covEnv != nil {
profraw = filepath.Join(r.tmpDir, fmt.Sprintf("cts-%v.profraw", i))
defer os.Remove(profraw)
}
go func() { go func() {
defer wg.Done() defer wg.Done()
for idx := range caseIndices { for idx := range caseIndices {
res := r.runTestcase(ctx, r.testcases[idx], profraw) res := r.runTestcase(ctx, r.testcases[idx])
res.index = idx res.index = idx
results <- res results <- res
@ -1053,17 +1047,11 @@ func (r *runner) streamResults(ctx context.Context, wg *sync.WaitGroup, results
return nil return nil
} }
// runSerially() calls the CTS test runner to run the test query in a single // runSerially() calls the CTS test runner to run the test query in a single process.
// process.
// TODO(bclayton): Support comparing against r.expectations // TODO(bclayton): Support comparing against r.expectations
func (r *runner) runSerially(ctx context.Context, query string) error { func (r *runner) runSerially(ctx context.Context, query string) error {
profraw := ""
if r.covEnv != nil {
profraw = filepath.Join(r.tmpDir, "cts.profraw")
}
start := time.Now() start := time.Now()
result := r.runTestcase(ctx, query, profraw) result := r.runTestcase(ctx, query)
timeTaken := time.Since(start) timeTaken := time.Since(start)
if r.verbose { if r.verbose {
@ -1115,7 +1103,7 @@ type result struct {
// runTestcase() runs the CTS testcase with the given query, returning the test // runTestcase() runs the CTS testcase with the given query, returning the test
// result. // result.
func (r *runner) runTestcase(ctx context.Context, query string, profraw string) result { func (r *runner) runTestcase(ctx context.Context, query string) result {
ctx, cancel := context.WithTimeout(ctx, testTimeout) ctx, cancel := context.WithTimeout(ctx, testTimeout)
defer cancel() defer cancel()
@ -1134,6 +1122,9 @@ func (r *runner) runTestcase(ctx context.Context, query string, profraw string)
if r.verbose { if r.verbose {
args = append(args, "--gpu-provider-flag", "verbose=1") args = append(args, "--gpu-provider-flag", "verbose=1")
} }
if r.covEnv != nil {
args = append(args, "--coverage")
}
if r.colors { if r.colors {
args = append(args, "--colors") args = append(args, "--colors")
} }
@ -1148,11 +1139,6 @@ func (r *runner) runTestcase(ctx context.Context, query string, profraw string)
cmd := exec.CommandContext(ctx, r.node, args...) cmd := exec.CommandContext(ctx, r.node, args...)
cmd.Dir = r.cts cmd.Dir = r.cts
if profraw != "" {
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, cov.RuntimeEnv(cmd.Env, profraw))
}
var buf bytes.Buffer var buf bytes.Buffer
cmd.Stdout = &buf cmd.Stdout = &buf
cmd.Stderr = &buf cmd.Stderr = &buf
@ -1160,18 +1146,33 @@ func (r *runner) runTestcase(ctx context.Context, query string, profraw string)
err := cmd.Run() err := cmd.Run()
msg := buf.String() msg := buf.String()
res := result{testcase: query, res := result{
status: pass, testcase: query,
message: msg, status: pass,
error: err, message: msg,
error: err,
} }
if r.covEnv != nil { if err == nil && r.covEnv != nil {
coverage, covErr := r.covEnv.Import(profraw) const header = "Code-coverage: [["
if covErr != nil { const footer = "]]"
err = fmt.Errorf("could not import coverage data: %v", err) if headerStart := strings.Index(msg, header); headerStart >= 0 {
if footerStart := strings.Index(msg[headerStart:], footer); footerStart >= 0 {
footerStart += headerStart
path := msg[headerStart+len(header) : footerStart]
res.message = msg[:headerStart] + msg[footerStart+len(footer):] // Strip out the coverage from the message
coverage, covErr := r.covEnv.Import(path)
os.Remove(path)
if covErr == nil {
res.coverage = coverage
} else {
err = fmt.Errorf("could not import coverage data from '%v': %v", path, covErr)
}
}
}
if err == nil && res.coverage == nil {
err = fmt.Errorf("failed to parse code coverage from output")
} }
res.coverage = coverage
} }
switch { switch {

View File

@ -48,12 +48,6 @@ type Env struct {
TurboCov string // path to the turbo-cov tool (one of Cov or TurboCov must be supplied) TurboCov string // path to the turbo-cov tool (one of Cov or TurboCov must be supplied)
} }
// RuntimeEnv returns the environment variable key=value pair for setting
// LLVM_PROFILE_FILE to coverageFile
func RuntimeEnv(env []string, coverageFile string) string {
return "LLVM_PROFILE_FILE=" + coverageFile
}
// AllSourceFiles returns a *Coverage containing all the source files without // AllSourceFiles returns a *Coverage containing all the source files without
// coverage data. This populates the coverage view with files even if they // coverage data. This populates the coverage view with files even if they
// didn't get compiled. // didn't get compiled.