blob: 59d52e3a8af9f6f3b8907a480c5af3aaccf2aeb0 [file] [log] [blame] [edit]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package batchtester
import (
"context"
"errors"
"fmt"
"os/exec"
"time"
"go.fuchsia.dev/fuchsia/tools/lib/clock"
"go.fuchsia.dev/fuchsia/tools/lib/streams"
"go.fuchsia.dev/fuchsia/tools/lib/subprocess"
)
// TODO(olivernewman): Make this configurable on a per-test basis.
const perTestTimeout = 10 * time.Minute
func Run(ctx context.Context, config *Config) error {
var runner subprocess.Runner
var failures int
for _, test := range config.Tests {
// TODO(olivernewman): Stream results to ResultDB.
r, err := runTest(ctx, &runner, test)
if err != nil {
return err
}
if r.Status != Pass {
failures++
}
}
if failures > 0 {
return fmt.Errorf("%d of %d test(s) failed", failures, len(config.Tests))
}
return nil
}
func runTest(ctx context.Context, runner *subprocess.Runner, test Test) (TestResult, error) {
testCtx, cancel := context.WithTimeout(ctx, perTestTimeout)
defer cancel()
options := subprocess.RunOptions{
Stdout: streams.Stdout(ctx),
Stderr: streams.Stderr(ctx),
Dir: test.Execroot,
}
startTime := clock.Now(ctx)
// TODO(olivernewman): Sandbox using nsjail.
err := runner.Run(testCtx, []string{test.Executable}, options)
endTime := clock.Now(ctx)
result := TestResult{
Name: test.Name,
Duration: endTime.Sub(startTime),
}
if err == nil {
result.Status = Pass
} else if errExit := (*exec.ExitError)(nil); errors.As(err, &errExit) {
result.Status = Fail
} else if errors.Is(err, context.DeadlineExceeded) && ctx.Err() == nil {
// Only consider a test to have timed out if the test's context timed
// out without the parent context timing out.
result.Status = Abort
} else {
// Unrecognized error.
return TestResult{}, err
}
return result, nil
}