blob: 63799870fc9e6f526ec12318a01c16ed44d99d1c [file] [log] [blame]
// Copyright 2020 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package testing
import (
"context"
"fmt"
"reflect"
"time"
"go.chromium.org/tast/core/internal/protocol"
"go.chromium.org/tast/core/testing/hwdep"
)
const (
// ExternalLinkSuffix is a file name suffix for external data link files.
// These are JSON files that can be unmarshaled into the externalLink struct.
ExternalLinkSuffix = ".external"
// ExternalErrorSuffix is a file name suffix for external data download error files.
// An error message is written to the file when we encounter an error downloading
// the corresponding external data file. This mechanism is used to pass errors from
// the test runner (which downloads the files) to the test bundle so the bundle
// can include them in the test's output.
ExternalErrorSuffix = ".external-error"
// ExternalURLSuffix is a file name suffix to store external data file's source url.
// It is used to perform staleness check for external files. If this file is present
// tast first refer this file to see if it has previously downloaded file from the
// url. If so then file is not downloaded again. This is useful in case when
// the artifact name in External data files does not change, however the
// buildartifactsurl cli flag changed, resulting in different source url.
ExternalURLSuffix = ".external-url"
)
// TestFunc is the code associated with a test.
type TestFunc func(context.Context, *State)
// OnErrorHandler is the interface of the custom error handler which will
// be used when a test calls s.Error.
type OnErrorHandler func(errMsg string)
// OnFatalHandler is the interface of the custom error handler which will
// be used when a test calls s.Fatal.
type OnFatalHandler func(errMsg string)
// Test describes a registration of one or more test instances.
//
// Test can be passed to testing.AddTest to actually register test instances
// to the framework.
//
// In the most basic form where Params field is empty, Test describes exactly
// one test instance. If Params is not empty, multiple test instances are
// generated on registration by merging each testing.Param to the base Test.
type Test struct {
// Func is the function to be executed to perform the test.
Func TestFunc
// Desc is a short one-line description of the test.
Desc string
// Contacts is a list of email addresses of persons and groups who are familiar with the test.
// At least one personal email address of an active committer should be specified so that we can
// file bugs or ask for code reviews.
//
// The first address must be the team alias for whichever team/product area ultimately owns the test.
// Partner tests should have a Googler contact, but addresses outside of google or chromium are OK
// for the second+ entries. Please make sure your group alias can be emailed by Googlers outside of the group.
//
// Additional email aliases should all be people who want to be added to
// bugs/emails/communication about the test.
Contacts []string
// Attr contains freeform text attributes describing the test.
// See https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/docs/test_attributes.md
// for commonly-used attributes.
Attr []string
// PrivateAttr contains freeform text private attributes describing the test.
// This should not be used other than Tast and tests.
// Note: this info is not retrievable in test results.
PrivateAttr []string
// SearchFlags contains key-value pairs describing the test.
// This information will be available in the test results, and can be used
// for custom test results filtering or any other mapping.
SearchFlags []*protocol.StringPair
// Data contains paths of data files needed by the test, relative to a "data" subdirectory within the
// directory in which Func is located.
Data []string
// Vars contains the names of runtime variables used to pass out-of-band data to tests.
// Values are supplied using "tast run -var=name=value", and tests can access values via State.Var.
Vars []string
// VarDeps serves similar purpose as Vars but lists runtime variables that
// are required to run the test.
// Whether test fails or skipped when runtime variables in VarDeps is
// missing is controlled by the flag -maybemissingvars for the Tast CLI.
//
// Tests should access runtime variables in VarDeps via State.RequiredVar.
VarDeps []string
// SoftwareDeps lists software features that are required to run the test.
// If any dependencies are not satisfied by the DUT, the test will be skipped.
// See https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/docs/test_dependencies.md
// for more information about dependencies.
SoftwareDeps []string
// HardwareDeps describes hardware features and setup that are required to run the test.
HardwareDeps hwdep.Deps
// Pre contains a precondition that must be met before the test is run.
Pre Precondition
// Fixture is the name of the fixture the test depends on.
Fixture string
// Timeout contains the maximum duration for which Func may run before the test is aborted.
// This should almost always be set. If not specified, a reasonable default will be used,
// but tests should not depend on it.
// This field is serialized as an integer nanosecond count.
Timeout time.Duration
// Params lists the Param structs for parameterized tests.
Params []Param
// ServiceDeps contains a list of RPC service names in local test bundles that this remote test
// will access. This field is valid only for remote tests.
ServiceDeps []string
// LacrosStatus indicates whether lacros variants have been considered for this test or not.
LacrosStatus LacrosMetadata
// SoftwareDepsForAll lists software features of all DUTs that
// are required to run the test.
// It is a map of companion roles and software features.
// The role for primary DUT should be "".
// The primary DUT software dependency will be the union of
// SoftwareDeps and SoftwareDepsForAll[""].
// If any dependencies are not satisfied, the test will be skipped.
SoftwareDepsForAll map[string][]string
// HardwareDepsForAll describes hardware features and setup of all
// DUTs that are required to run the test.
// It is a map of companion roles and hardware features.
// The role for primary DUT should be "".
// The primary DUT hardware dependency will be the union of
// HardwareDeps and HardwareDepsForAll[""].
// If any dependencies are not satisfied, the test will be skipped.
HardwareDepsForAll map[string]hwdep.Deps
// Fields after this line are used for automation purposes only, tests should not use
// these fields.
// TestBedDeps are used for defining test bed dependencies only, i.e., 'carrier:verizon'.
// These are not used by tests themselves, but added to test metadata definitions used by
// infra services.
//
// For details about what dependencies are supported and how they are parsed,
// see the converter and reverter functions in
// https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/libs/skylab/inventory/autotest/labels/.
TestBedDeps []string
// Requirements are used for linking test cases to requirements. These are not used by
// tests themselves, but added to test metadata definitions used by infra services.
Requirements []string
// Bug component id for filing bugs against this test, i.e. 'b:1234'. This field is not
// to be used by tests themselves, but added to test metadata definitions used by infra services.
BugComponent string
// Life cycle metadata to indicate the usage of this test. This field is not
// to be used by tests themselves, but added to test metadata definitions used by infra services.
LifeCycleStage LifeCycle
// VariantCategory defines hardware and software capabilities of the device or test rigging it
// needs, which can influence the behavior of the test and its outcome.
// Not required for the legacy pipeline.
VariantCategory string
}
// LacrosMetadata indicates whether lacros variants have been considered for this test or not.
type LacrosMetadata int
const (
// LacrosVariantUnknown indicates that this test has not yet been checked as to whether it requires a lacros variant.
// New tests should not use this value, i.e. new tests should always consider lacros.
LacrosVariantUnknown = iota
// LacrosVariantNeeded indicates that a lacros variant for this is needed but hasn't been created yet.
LacrosVariantNeeded
// LacrosVariantExists indicates that all required lacros variants for this test have been created.
LacrosVariantExists
// LacrosVariantUnneeded indicates that lacros variants for this test are not needed.
LacrosVariantUnneeded
)
func (m LacrosMetadata) String() string {
switch m {
case LacrosVariantUnknown:
return "unknown"
case LacrosVariantNeeded:
return "needed"
case LacrosVariantExists:
return "exists"
case LacrosVariantUnneeded:
return "unneeded"
default:
return fmt.Sprintf("unexpected value (%d)", int(m))
}
}
// LifeCycle aligns with the TestCaseMetadata proto value of LifeCycle.
type LifeCycle int
const (
// LifeCycleDefault should not be added to a test directly and indicates that this test does not have an
// explicitly defined value. ProductionReady will be used instead, unless a parent test or
// parameterized sub-test defines a different value.
LifeCycleDefault LifeCycle = iota
// LifeCycleProductionReady is the indicates the test can be run in the lab and is expected to pass.
// Most tests will be in this stage, and this value will be assumed if no other value is provided.
LifeCycleProductionReady
// LifeCycleDisabled indicates that the test should not run in the lab and code will be deleted if not cleaned
// up in a timely manner.
LifeCycleDisabled
// LifeCycleInDevelopment indicates that the test is either new or broken. It can still run in the lab
// (ideally at a reduced frequency) but should not be included in flakiness reports or used to make
// decisions like release qualification.
LifeCycleInDevelopment
// LifeCycleManualOnly indicates that the test is not meant to be scheduled in the lab - it will only be
// triggered manually or outside of a lab environment. The code should not be deleted unless test owners
// do not maintain the test.
LifeCycleManualOnly
// LifeCycleOwnerMonitored indicates that the test has inherently ambiguous usage and should not be run by
// or interpreted by anyone other than the test owner. These tests can run in the lab but should not
// be run in release-blocking suites or any other situation where a non-owner will need to use the results.
LifeCycleOwnerMonitored
)
func (s LifeCycle) String() string {
switch s {
case LifeCycleProductionReady:
return "LIFE_CYCLE_PRODUCTION_READY"
case LifeCycleDisabled:
return "LIFE_CYCLE_DISABLED"
case LifeCycleInDevelopment:
return "LIFE_CYCLE_IN_DEVELOPMENT"
case LifeCycleManualOnly:
return "LIFE_CYCLE_MANUAL_ONLY"
case LifeCycleOwnerMonitored:
return "LIFE_CYCLE_OWNER_MONITORED"
default:
return fmt.Sprintf("Unknown")
}
}
// Param defines parameters for a parameterized test case.
// See also https://chromium.googlesource.com/chromiumos/platform/tast/+/HEAD/docs/writing_tests.md#Parameterized-tests
type Param struct {
// Name is the name of this parameterized test.
// Full name of the test case will be category.TestFuncName.param_name,
// or category.TestFuncName if Name is empty.
// Name should match with [a-z0-9_]*.
Name string
// ExtraAttr contains freeform text attributes describing the test,
// in addition to Attr declared in the enclosing Test.
ExtraAttr []string
// ExtraPrivateAttr contains freeform text private attributes describing the test,
// in addition to PrivateAttr declared in the enclosing Test.
ExtraPrivateAttr []string
// ExtraSearchFlags contains name-value pairs describing the test,
// in addition to SearchFlags declared in the enclosing Test.
ExtraSearchFlags []*protocol.StringPair
// ExtraData contains paths of data files needed by the test case of this
// param in addition to Data declared in the enclosing Test.
ExtraData []string
// ExtraSoftwareDeps lists software features that are required to run the test case for this param,
// in addition to SoftwareDeps in the enclosing Test.
ExtraSoftwareDeps []string
// ExtraHardwareDeps describes hardware features and setup that are required to run the test for this
// param, in addition to HardwareDeps in the enclosing Test.
ExtraHardwareDeps hwdep.Deps
// ExtraRequirements are used for linking test cases to requirements. These are not used by
// tests themselves, but added to test metadata definitions used by infra services. This slice is
// appended to an Requirements declared by the test.
ExtraRequirements []string
// ExtraTestBedDeps are used for defining test bed dependencies only, i.e., 'carrier:verizon'.
// These are not used by tests themselves, but added to test metadata definitions used by
// infra services. This slice is appended to the TestBedDeps in the enclosing Test.
//
// For details about what dependencies are supported and how they are parsed,
// see the converter and reverter functions in
// https://source.chromium.org/chromium/infra/infra/+/main:go/src/infra/libs/skylab/inventory/autotest/labels/.
ExtraTestBedDeps []string
// Pre contains a precondition that must be met before the test is run.
// Can only be set if the enclosing test doesn't have one already set.
Pre Precondition
// Fixture is the name of the fixture the test depends on.
// Can only be set if the enclosing test doesn't have one already set.
Fixture string
// Timeout contains the maximum duration for which Func may run before the test is aborted.
// Can only be set if the enclosing test doesn't have one already set.
Timeout time.Duration
// Val is the value which can be retrieved from testing.State.Param() method.
Val interface{}
// ExtraSoftwareDepsForAll lists software features of all DUTs
// that are required to run the test case for this param,
// in addition to SoftwareDepsForAll in the enclosing Test.
// The primary DUT software dependency will be the union of
// SoftwareDeps, SoftwareDepsForAll[""], ExtraSoftwareDeps and
// ExtraSoftwareDepsForAll[""].
// It is a map of companion roles and software features.
ExtraSoftwareDepsForAll map[string][]string
// ExtraHardwareDepsForAll describes hardware features and setup
// companion DUTs that are required to run the test case for this param,
// in addition to HardwareDepsForAll in the enclosing Test.
// It is a map of companion roles and hardware features.
// The role for primary DUT should be ""
// The primary DUT hardware dependency will be the union of
// HardwareDeps, HardwareDepsForAll[""], ExtraHardwareDeps and
// ExtraHardwareDep and ExtraHardwareDepsForAll[""].
ExtraHardwareDepsForAll map[string]hwdep.Deps
// BugComponent overrides BugComponent defined in the test.
// This field is for infra/external use only and should not be used
// or referenced within the test code.
BugComponent string
// LifeCycleStage overrides the LifeCycleStage defined in the test.
// This field is not to be used or referenced by test code.
LifeCycleStage LifeCycle
// VariantCategory defines hardware and software capabilities of the device or test rigging it
// needs, which can influence the behavior of the test and its outcome.
// Not required for the legacy pipeline.
VariantCategory string
}
// validate performs initial validations of Test.
// Most validations are done while constructing TestInstance from a combination
// of Test and Param in newTestInstance, not in this method, so that we can
// validate fields of the final products. However some validations can be done
// only in this method, e.g. checking consistencies among multiple parameters.
func (t *Test) validate() error {
if err := validateParams(t.Params); err != nil {
return err
}
return nil
}
func validateParams(params []Param) error {
if len(params) == 0 {
return nil
}
// Ensure unique param name.
seen := make(map[string]struct{})
for _, p := range params {
name := p.Name
if _, ok := seen[name]; ok {
return fmt.Errorf("duplicate param name is found: %s", name)
}
seen[name] = struct{}{}
}
// Ensure all value assigned to Val should have the same type.
typ0 := reflect.TypeOf(params[0].Val)
for _, p := range params {
typ := reflect.TypeOf(p.Val)
if typ != typ0 {
return fmt.Errorf("unmatched Val type: got %v; want %v", typ, typ0)
}
}
return nil
}