blob: d47a9559186c1388488a8b0e280c4ffaf036b1f2 [file] [edit]
// Copyright 2021 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 jsonprotocol
import (
"encoding/json"
"time"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"go.chromium.org/chromiumos/config/go/api"
"chromiumos/tast/internal/planner"
"chromiumos/tast/internal/protocol"
)
// BundleRunMode describes the bundle's behavior.
type BundleRunMode int
const (
// BundleRunTestsMode indicates that the bundle should run all matched tests and write the results to stdout as
// a sequence of JSON-marshaled control.Entity* control messages.
BundleRunTestsMode BundleRunMode = 0
// BundleListTestsMode indicates that the bundle should write information about matched tests to stdout as a
// JSON array of testing.Test structs and exit.
BundleListTestsMode BundleRunMode = 1
// BundleRPCMode indicates that the bundle should run a gRPC server on the stdin/stdout.
BundleRPCMode BundleRunMode = 2
// BundleExportMetadataMode indicates that the bundle should compose and dump test metadata.
BundleExportMetadataMode BundleRunMode = 3
// BundleListFixturesMode indicates that the bundle should write information about all the fixtures
// to stdout as a JSON array of testing.EntityInfo structs and exit.
BundleListFixturesMode BundleRunMode = 4
)
// BundleArgs is used to pass arguments from test runners to test bundles.
// The runner executable writes the struct's JSON-marshaled representation to the bundle's stdin.
type BundleArgs struct {
// Mode describes the mode that should be used by the bundle.
Mode BundleRunMode `json:"mode"`
// RunTests contains arguments used by BundleRunTestsMode.
RunTests *BundleRunTestsArgs `json:"runTests,omitempty"`
// ListTests contains arguments used by BundleListTestsMode.
ListTests *BundleListTestsArgs `json:"listTests,omitempty"`
}
// FillDeprecated backfills deprecated fields from the corresponding non-deprecated fields.
// This method is called by test runners to ensure that args will be interpreted
// correctly by older test bundles.
func (a *BundleArgs) FillDeprecated() {
// If there were any deprecated fields, we would fill them from the corresponding
// non-deprecated fields here using command.CopyFieldIfNonZero for basic types or
// manual copies for structs.
}
// PromoteDeprecated copies all non-zero-valued deprecated fields to the corresponding non-deprecated fields.
// Missing sub-structs (e.g. BundleRunTestsArgs and BundleListTestsArgs) are initialized.
// This method is called by test bundles to normalize args that were marshaled by an older test runner.
//
// If both an old and new field are set, the old field takes precedence. This is counter-intuitive but
// necessary: a default value for the new field may have been passed to run by Local or Remote. If the
// corresponding old field is non-zero, it was passed by an old runner (or by a new runner that called
// FillDeprecated), so we use the old field to make sure that it overrides the default.
func (a *BundleArgs) PromoteDeprecated() {
// We don't have any deprecated fields right now.
}
// DeviceConfigJSON is a wrapper class for protocol.DeprecatedDeviceConfig to facilitate marshalling/unmarshalling.
type DeviceConfigJSON struct {
// Proto contains the device configuration information about DUT.
// Marshaling and unmarshaling of this field is handled in MarshalJSON/UnmarshalJSON
// respectively.
Proto *protocol.DeprecatedDeviceConfig `json:"-"`
}
// MarshalJSON marshals the protocol.DeprecatedDeviceConfig struct into JSON.
func (a *DeviceConfigJSON) MarshalJSON() ([]byte, error) {
if a.Proto == nil {
return []byte(`null`), nil
}
bin, err := proto.Marshal(a.Proto)
if err != nil {
return nil, err
}
return json.Marshal(bin)
}
// UnmarshalJSON unmarshals JSON blob for protocol.DeprecatedDeviceConfig.
func (a *DeviceConfigJSON) UnmarshalJSON(b []byte) error {
var aux []byte
if err := json.Unmarshal(b, &aux); err != nil {
return err
}
if len(aux) == 0 {
return nil
}
var dc protocol.DeprecatedDeviceConfig
if err := proto.Unmarshal(aux, &dc); err != nil {
return err
}
a.Proto = &dc
return nil
}
// HardwareFeaturesJSON is a wrapper class for api.HardwareFeatures to facilitate marshalling/unmarshalling.
type HardwareFeaturesJSON struct {
// Proto contains the hardware info about DUT.
// Marshaling and unmarshaling of this field is handled in MarshalJSON/UnmarshalJSON
// respectively.
Proto *api.HardwareFeatures `json:"-"`
}
// MarshalJSON marshals the api.HardwareFeatures struct into JSON.
func (a *HardwareFeaturesJSON) MarshalJSON() ([]byte, error) {
if a.Proto == nil {
return []byte(`null`), nil
}
bin, err := proto.Marshal(a.Proto)
if err != nil {
return nil, err
}
return json.Marshal(bin)
}
// UnmarshalJSON unmarshals JSON blob for api.HardwareFeatures.
func (a *HardwareFeaturesJSON) UnmarshalJSON(b []byte) error {
var aux []byte
if err := json.Unmarshal(b, &aux); err != nil || len(aux) == 0 {
return err
}
var hw api.HardwareFeatures
if err := proto.Unmarshal(aux, &hw); err != nil {
return err
}
a.Proto = &hw
return nil
}
// FeatureArgs includes all the feature related arguments.
type FeatureArgs struct {
// TestVars contains names and values of runtime variables used to pass out-of-band data to tests.
// Names correspond to testing.Test.Vars and values are accessed using testing.State.Var.
TestVars map[string]string `json:"testVars,omitempty"`
// MaybeMissingVars contains a regex compiled with regexp.Compile().
// If every missing variable in testing.Test.VarDeps (exactly) matches the
// regex, the test is skipped instead of failing.
// If empty, no tests are skipped due to missing vars.
MaybeMissingVars string `json:"maybeMissingVars,omitempty"`
// CheckDeps indicates whether test runners should skip tests whose
// dependencies are not satisfied by available features.
CheckDeps bool `json:"checkSoftwareDeps,omitempty"`
// AvailableSoftwareFeatures contains a list of software features supported by the DUT.
AvailableSoftwareFeatures []string `json:"availableSoftwareFeatures,omitempty"`
// UnavailableSoftwareFeatures contains a list of software features supported by the DUT.
UnavailableSoftwareFeatures []string `json:"unavailableSoftwareFeatures,omitempty"`
// DeviceConfig contains the hardware info about the DUT.
// Marshaling and unmarshaling of this field is handled in MarshalJSON/UnmarshalJSON
// respectively.
DeviceConfig DeviceConfigJSON
// HardwareFeatures contains the hardware info about DUT.
// Marshaling and unmarshaling of this field is handled in MarshalJSON/UnmarshalJSON
// respectively.
HardwareFeatures HardwareFeaturesJSON
}
// Features returns protocol.Features to be used to check test dependencies.
func (a *FeatureArgs) Features() *protocol.Features {
vars := make(map[string]string)
for k, v := range a.TestVars {
vars[k] = v
}
return &protocol.Features{
CheckDeps: a.CheckDeps,
Dut: &protocol.DUTFeatures{
Software: &protocol.SoftwareFeatures{
Available: a.AvailableSoftwareFeatures,
Unavailable: a.UnavailableSoftwareFeatures,
},
Hardware: &protocol.HardwareFeatures{
DeprecatedDeviceConfig: a.DeviceConfig.Proto,
HardwareFeatures: a.HardwareFeatures.Proto,
},
},
Infra: &protocol.InfraFeatures{
Vars: vars,
MaybeMissingVars: a.MaybeMissingVars,
},
}
}
// BundleRunTestsArgs is nested within BundleArgs and contains arguments used by BundleRunTestsMode.
type BundleRunTestsArgs struct {
// FeatureArgs includes all the feature related arguments.
FeatureArgs
// 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 `json:"patterns,omitempty"`
// DataDir is the path to the directory containing test data files.
DataDir string `json:"dataDir,omitempty"`
// OutDir is the path to the base directory under which tests should write output files.
OutDir string `json:"outDir,omitempty"`
// TempDir is the path to the directory under which temporary files for tests are written.
TempDir string `json:"tempDir,omitempty"`
// Target is the DUT connection spec as [<user>@]host[:<port>].
// It is only relevant for remote tests.
Target string `json:"target,omitempty"`
// 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 `json:"keyFile,omitempty"`
// KeyDir is the directory containing SSH private keys (typically $HOME/.ssh).
// It is only relevant for remote tests.
KeyDir string `json:"keyDir,omitempty"`
// TastPath contains the path to the tast binary that was executed to initiate testing.
// It is only relevant for remote tests.
TastPath string `json:"tastPath,omitempty"`
// RunFlags contains a subset of the flags that were passed to the "tast run" command.
// The included flags are ones that are necessary for core functionality,
// e.g. paths to binaries used by the tast process and credentials for reconnecting to the DUT.
// It is only relevant for remote tests.
RunFlags []string `json:"runFlags,omitempty"`
// LocalBundleDir is the directory on the DUT where local test bundle executables are located.
// This path is used by remote tests to invoke gRPC services in local test bundles.
// It is only relevant for remote tests.
LocalBundleDir string `json:"localBundleDir,omitempty"`
// Devservers contains URLs of devservers that can be used to download files.
Devservers []string `json:"devservers,omitempty"`
// TLWServer contains address of Test Lab Service Wiring APIs.
TLWServer string `json:"tlwServer,omitempty"`
// DUTName contains given DUT identifier to be passed to TLS Wiring API.
DUTName string `json:"dutName,omitempty"`
// companionDuts contains mapping between roles and addresses of companion DUTs.
CompanionDUTs map[string]string `json:"companionDUTs,omitempty"`
// BuildArtifactsURL is the URL of Google Cloud Storage directory, ending with a slash,
// containing build artifacts for the current Chrome OS image.
// If it is empty, DefaultBuildArtifactsURL in runner.Config is used.
BuildArtifactsURL string `json:"buildArtifactsUrl,omitempty"`
// DownloadMode specifies a strategy to download external data files.
DownloadMode planner.DownloadMode `json:"downloadMode,omitempty"`
// WaitUntilReady indicates that the test bundle's "ready" function (see ReadyFunc) should
// be executed before any tests are executed.
WaitUntilReady bool `json:"waitUntilReady,omitempty"`
// HeartbeatInterval is the interval in seconds at which heartbeat messages are sent back
// periodically from runners (before running bundles) and bundles. If this value is not
// positive, heartbeat messages are not sent.
HeartbeatInterval time.Duration `json:"heartbeatInterval,omitempty"`
// SetUpErrors contains error messages happened on test setup (e.g. fixture SetUp). If its
// length is non-zero, tests shouldn't run.
SetUpErrors []string `json:"setUpErrors,omitempty"`
// StartFixtureName is the remote fixture name that ran for the test.
StartFixtureName string `json:"startFixtureName,omitempty"`
}
// Proto generates protocol.RunConfig.
func (a *BundleRunTestsArgs) Proto() *protocol.RunConfig {
var remoteCfg *protocol.RemoteTestConfig
var tlwSelfName string
// We consider that BundleRunTestsArgs is for remote tests if Target is
// non-empty.
if a.Target == "" {
tlwSelfName = a.DUTName
} else {
companionDUTs := make(map[string]*protocol.DUTConfig)
for role, addr := range a.CompanionDUTs {
companionDUTs[role] = &protocol.DUTConfig{
SshConfig: &protocol.SSHConfig{
Target: addr,
KeyFile: a.KeyFile,
KeyDir: a.KeyDir,
},
}
}
remoteCfg = &protocol.RemoteTestConfig{
PrimaryDut: &protocol.DUTConfig{
SshConfig: &protocol.SSHConfig{
Target: a.Target,
KeyFile: a.KeyFile,
KeyDir: a.KeyDir,
},
TlwName: a.DUTName,
},
CompanionDuts: companionDUTs,
LocalBundleDir: a.LocalBundleDir,
MetaTestConfig: &protocol.MetaTestConfig{
TastPath: a.TastPath,
RunFlags: a.RunFlags,
},
}
}
var startFixtErrors []*protocol.Error
for _, r := range a.SetUpErrors {
startFixtErrors = append(startFixtErrors, &protocol.Error{Reason: r})
}
return &protocol.RunConfig{
Tests: a.Patterns,
Dirs: &protocol.RunDirectories{
DataDir: a.DataDir,
OutDir: a.OutDir,
TempDir: a.TempDir,
},
Features: a.Features(),
ServiceConfig: &protocol.ServiceConfig{
Devservers: a.Devservers,
TlwServer: a.TLWServer,
TlwSelfName: tlwSelfName,
},
DataFileConfig: &protocol.DataFileConfig{
DownloadMode: a.DownloadMode.Proto(),
BuildArtifactsUrl: a.BuildArtifactsURL,
},
RemoteTestConfig: remoteCfg,
StartFixtureState: &protocol.StartFixtureState{
Name: a.StartFixtureName,
Errors: startFixtErrors,
},
HeartbeatInterval: ptypes.DurationProto(a.HeartbeatInterval),
WaitUntilReady: a.WaitUntilReady,
}
}
// BundleListTestsArgs is nested within BundleArgs and contains arguments used by BundleListTestsMode.
type BundleListTestsArgs struct {
// FeatureArgs includes all the feature related arguments.
FeatureArgs
// Patterns contains patterns (either empty to list all tests, exactly one attribute expression,
// or one or more globs) describing which tests to list.
Patterns []string `json:"patterns,omitempty"`
}