blob: 2c6b15657f6151508e3bc3ad8419e26b22edf8b1 [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 runner
import (
"context"
"flag"
"fmt"
"io"
"os"
"path/filepath"
frameworkprotocol "chromiumos/tast/framework/protocol"
"chromiumos/tast/internal/command"
"chromiumos/tast/internal/protocol"
)
// RunnerType describes the type of test runner that is using this package.
type RunnerType int // NOLINT
const (
// LocalRunner indicates that this package is being used by local_test_runner.
LocalRunner RunnerType = iota
// RemoteRunner indicates that this package is being used by remote_test_runner.
RemoteRunner
)
// StaticConfig contains fixed parameters for the runner that are passed in from
// local_test_runner or remote_test_runner.
type StaticConfig struct {
// Type describes the type of runner being executed.
Type RunnerType
// KillStaleRunners dictates whether SIGTERM should be sent to any existing test runner processes
// when using RunnerRunTestsMode. This can help prevent confusing failures if multiple test jobs are
// incorrectly scheduled on the same DUT: https://crbug.com/941829
KillStaleRunners bool
// EnableSyslog specifies whether to copy logs to syslog. It should be
// always enabled on production, but can be disabled in unit tests to
// avoid spamming syslog.
EnableSyslog bool
// GetDUTInfo is a function to respond to GetDUTInfo RPC.
// If it is nil, an empty GetDUTInfoResponse is always returned.
GetDUTInfo func(ctx context.Context, req *protocol.GetDUTInfoRequest) (*protocol.GetDUTInfoResponse, error)
// GetSysInfoState is a function to respond to GetSysInfoState RPC.
// If it is nil, an empty GetSysInfoStateResponse is always returned.
GetSysInfoState func(ctx context.Context, req *protocol.GetSysInfoStateRequest) (*protocol.GetSysInfoStateResponse, error)
// CollectSysInfo is a function to respond to CollectSysInfo RPC.
// If it is nil, an empty CollectSysInfoResponse is always returned.
CollectSysInfo func(ctx context.Context, req *protocol.CollectSysInfoRequest) (*protocol.CollectSysInfoResponse, error)
// PrivateBundlesStampPath contains the path to a stamp file indicating private test bundles have been
// successfully downloaded and installed before. This prevents downloading private test bundles for
// every runner invocation.
PrivateBundlesStampPath string
// DeprecatedDirectRunDefaults is default configuration values used when
// the user executes a test runner directly to run tests.
//
// DEPRECATED: Direct test execution is deprecated. Tast tests should be
// always initiated with Tast CLI, in which case default values here are
// ignored.
DeprecatedDirectRunDefaults DeprecatedDirectRunConfig
}
// mode denotes the execution mode of the test runner.
type mode int
const (
// modeRPC is the standard execution mode of the test runner to start
// a gRPC server on stdin/stdout.
modeRPC mode = iota
// modeDeprecatedDirectRun is the deprecated execution mode of the test
// runner to allow users to run local tests directly on the DUT without
// Tast CLI.
modeDeprecatedDirectRun
)
// parsedArgs holds the results of command line parsing.
type parsedArgs struct {
Mode mode
// DeprecatedDirectRunConfig contains configuration values used when
// the user executes a test runner directly to run tests.
//
// DEPRECATED: Direct test execution is deprecated. Tast tests should be
// always initiated with Tast CLI.
DeprecatedDirectRunConfig DeprecatedDirectRunConfig
}
// DeprecatedDirectRunConfig contains configuration values used when the user
// executes a test runner directly to run tests.
//
// DEPRECATED: Direct test execution is deprecated. Tast tests should be always
// initiated with Tast CLI.
type DeprecatedDirectRunConfig struct {
// BundleGlob is a glob-style path matching test bundles to execute.
BundleGlob string
// DataDir is the path to the directory containing test data files.
DataDir string
// TempDir is the path to the directory under which temporary files for
// tests are written.
TempDir string
// Patterns contains patterns (either empty to run all tests, exactly one attribute expression,
// or one or more globs) describing which tests to run.
Patterns []string
// OutDir is the path to the base directory under which tests should write output files.
OutDir string
// Devservers contains URLs of devservers that can be used to download files.
Devservers []string
// WaitUntilReady indicates that the test bundle's "ready" function (see ReadyFunc) should
// be executed before any tests are executed.
WaitUntilReady bool
// ConnectionSpec is the DUT connection spec as [<user>@]host[:<port>].
// It is only relevant for remote tests.
ConnectionSpec string
// KeyFile is the path to the SSH private key to use to connect to the DUT.
// It is only relevant for remote tests.
KeyFile string
// KeyDir is the directory containing SSH private keys (typically $HOME/.ssh).
// It is only relevant for remote tests.
KeyDir string
// CheckDeps indicates whether test runners should skip tests whose
// dependencies are not satisfied by available features.
CheckDeps bool
// AvailableSoftwareFeatures contains a list of software features supported by the DUT.
AvailableSoftwareFeatures []string
// UnavailableSoftwareFeatures contains a list of software features supported by the DUT.
UnavailableSoftwareFeatures []string
}
// RunConfig generates protocol.RunConfig.
// Tests should be a resolved list of test names according to a.Patterns.
// Todo: Use primary dut for now, will add companion features in the future
func (c *DeprecatedDirectRunConfig) RunConfig(tests []string) *protocol.RunConfig {
return &protocol.RunConfig{
Tests: tests,
Dirs: &protocol.RunDirectories{
DataDir: c.DataDir,
OutDir: c.OutDir,
TempDir: c.TempDir,
},
Features: &protocol.Features{
CheckDeps: c.CheckDeps,
Dut: &frameworkprotocol.DUTFeatures{
Software: &frameworkprotocol.SoftwareFeatures{
Available: c.AvailableSoftwareFeatures,
Unavailable: c.UnavailableSoftwareFeatures,
},
},
},
ServiceConfig: &protocol.ServiceConfig{
Devservers: c.Devservers,
},
DataFileConfig: &protocol.DataFileConfig{},
WaitUntilReady: c.WaitUntilReady,
}
}
// parseArgs parses runtime arguments.
// clArgs contains command-line arguments and is typically os.Args[1:].
func parseArgs(clArgs []string, stderr io.Writer, scfg *StaticConfig) (*parsedArgs, error) {
args := &parsedArgs{
Mode: modeDeprecatedDirectRun,
DeprecatedDirectRunConfig: scfg.DeprecatedDirectRunDefaults,
}
// Expose a limited amount of configurability via command-line flags to support running test runners manually.
var extraUSEFlags []string
flags := flag.NewFlagSet("", flag.ContinueOnError)
flags.SetOutput(stderr)
const usage = `Usage: %s [flag]... [pattern]...
Run Tast tests matched by zero or more patterns.
This executes test bundles to run Tast tests and is typically executed by the
"tast" command. It can be executed manually to e.g. perform stress testing.
Exits with 0 if all tests passed and with a non-zero exit code for all other
errors, including the failure of an individual test.
`
flags.Usage = func() {
fmt.Fprintf(stderr, usage, filepath.Base(os.Args[0]))
flags.PrintDefaults()
}
rpc := flags.Bool("rpc", false, "run gRPC server")
flags.StringVar(&args.DeprecatedDirectRunConfig.BundleGlob, "bundles",
args.DeprecatedDirectRunConfig.BundleGlob, "glob matching test bundles")
flags.StringVar(&args.DeprecatedDirectRunConfig.DataDir, "datadir",
args.DeprecatedDirectRunConfig.DataDir, "directory containing data files")
flags.StringVar(&args.DeprecatedDirectRunConfig.OutDir, "outdir",
args.DeprecatedDirectRunConfig.OutDir, "base directory to write output files to")
flags.Var(command.NewListFlag(",", func(v []string) { args.DeprecatedDirectRunConfig.Devservers = v }, nil),
"devservers", "comma-separated list of devserver URLs")
flags.Var(command.NewListFlag(",", func(v []string) { extraUSEFlags = v }, nil),
"extrauseflags", "comma-separated list of additional USE flags to inject when checking test dependencies")
flags.BoolVar(&args.DeprecatedDirectRunConfig.WaitUntilReady, "waituntilready",
true, "wait until DUT is ready before running tests")
if scfg.Type == RemoteRunner {
flags.StringVar(&args.DeprecatedDirectRunConfig.ConnectionSpec, "target",
"", "DUT connection spec as \"[<user>@]host[:<port>]\"")
flags.StringVar(&args.DeprecatedDirectRunConfig.KeyFile, "keyfile",
"", "path to SSH private key to use for connecting to DUT")
flags.StringVar(&args.DeprecatedDirectRunConfig.KeyDir, "keydir",
"", "directory containing SSH private keys (typically $HOME/.ssh)")
}
if err := flags.Parse(clArgs); err != nil {
return nil, command.NewStatusErrorf(statusBadArgs, "%v", err)
}
if *rpc {
args.Mode = modeRPC
return args, nil
}
args.DeprecatedDirectRunConfig.Patterns = flags.Args()
// When the runner is executed by the "tast run" command, the list of software features (used to skip
// unsupported tests) is passed in after having been gathered by an earlier call to local_test_runner
// with RunnerGetDUTInfoMode. When the runner is executed directly, gather the list here instead.
if scfg.GetDUTInfo != nil {
req := &protocol.GetDUTInfoRequest{ExtraUseFlags: extraUSEFlags}
res, err := scfg.GetDUTInfo(context.Background(), req)
if err != nil {
return nil, err
}
fs := res.GetDutInfo().GetFeatures().GetSoftware()
args.DeprecatedDirectRunConfig.CheckDeps = true
args.DeprecatedDirectRunConfig.AvailableSoftwareFeatures = fs.GetAvailable()
args.DeprecatedDirectRunConfig.UnavailableSoftwareFeatures = fs.GetUnavailable()
// Historically we set software features only. Do not bother to
// improve hardware feature support in direct mode.
}
return args, nil
}