blob: 5a60f9db39a783055ee5cd40d4f5ded2c1868890 [file] [log] [blame]
// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Package meta contains support code for Tast meta tests.
package meta
import (
"context"
"fmt"
"os"
"path/filepath"
"reflect"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"google.golang.org/protobuf/encoding/protojson"
"go.chromium.org/tast-tests/cros/common/meta"
"go.chromium.org/tast/core/fsutil"
"go.chromium.org/tast/core/testing"
"go.chromium.org/tast/core/framework/protocol"
)
func init() {
testing.AddFixture(&testing.Fixture{
Name: "metaLocalDataFilesFixture",
Desc: "Demonstrate how to use data files in fixtures",
Contacts: []string{"tast-core@google.com", "seewaifu@google.com"},
BugComponent: "b:1034522", // ChromeOS > Test > Harness > Tast > Examples
Data: []string{
"fixture_data_internal.txt",
"fixture_data_external.txt",
},
Impl: dataFileFixture{},
})
testing.AddFixture(&testing.Fixture{
Name: "metaLocalFixtureDUTFeature",
Desc: "Demonstrate how to access DUT Features in fixtures",
Contacts: []string{"tast-core@google.com", "seewaifu@google.com"},
BugComponent: "b:1034522", // ChromeOS > Test > Harness > Tast > Examples
Data: []string{},
Impl: &dutFeatureFixture{},
})
testing.AddFixture(&testing.Fixture{
Name: "metaLocalFixtureWithStringVal",
Parent: "metaRemoteFixtureWithStringVal",
Desc: "Used for verification of tests accessing string value from local fixture",
Contacts: []string{"tast-core@google.com", "seewaifu@google.com", "yichiyan@google.com"},
BugComponent: "b:1034522", // ChromeOS > Test > Harness > Tast > Examples
Data: []string{},
Impl: &fixtSerializedStringFixture{},
})
testing.AddFixture(&testing.Fixture{
Name: "metaLocalFixtureWithStructVal",
Parent: "metaRemoteFixtureWithStructVal",
Desc: "Used for verification of tests accessing string value from local fixture",
Contacts: []string{"tast-core@google.com", "seewaifu@google.com", "yichiyan@google.com"},
BugComponent: "b:1034522", // ChromeOS > Test > Harness > Tast > Examples
Data: []string{},
Impl: &fixtSerializedStructFixture{},
})
testing.AddFixture(&testing.Fixture{
Name: "metaLocalFixtureWithSameBundle",
Parent: "metaLocalFixtureWithStringVal",
Desc: "Used for verification of fixtures accessing string value from parent in the same bundle",
Contacts: []string{"tast-core@google.com", "seewaifu@google.com", "yichiyan@google.com"},
BugComponent: "b:1034522", // ChromeOS > Test > Harness > Tast > Examples
Data: []string{},
Impl: &fixtSerializedSameBundleFixture{},
})
testing.AddFixture(&testing.Fixture{
Name: "metaLocalSetupFailureFixture",
Desc: "Test tast fixture failures",
Contacts: []string{"tast-core@google.com", "seewaifu@google.com"},
BugComponent: "b:1034522", // ChromeOS > Test > Harness > Tast > Examples
Impl: localSetupFailureFixture{},
})
testing.AddFixture(&testing.Fixture{
Name: "metaLocalParamFixture",
Desc: "Test tast parameterized fixture",
Contacts: []string{"tast-core@google.com", "seewaifu@google.com"},
BugComponent: "b:1034522", // ChromeOS > Test > Harness > Tast > Examples
Impl: localParamFixture{},
Params: genParams(),
})
testing.AddFixture(&testing.Fixture{
Name: "metaLocalDUTLabConfigFixture",
Desc: "Test tast parameterized fixture",
Contacts: []string{"tast-core@google.com", "seewaifu@google.com"},
BugComponent: "b:1034522", // ChromeOS > Test > Harness > Tast > Examples
Impl: localDUTLabConfigFixture{},
})
}
type fixtSerializedStringFixture struct{}
func (fixtSerializedStringFixture) SetUp(ctx context.Context, s *testing.FixtState) interface{} {
s.AttachErrorHandlers(
func(errMsg string) {
testing.ContextLog(ctx, "Got error: ", errMsg)
},
func(errMsg string) {
testing.ContextLog(ctx, "Got fatal error: ", errMsg)
})
v := ""
if err := s.ParentFillValue(&v); err != nil {
s.Fatal("Failed to get remote parent string value in Setup: ", err)
}
if v != meta.RemoteFixtureExpectedStringVal {
s.Errorf("Failed to get expected fixture value with FixtFillValue; got %q, want %q", v, meta.RemoteFixtureExpectedStringVal)
}
return v
}
func (fixtSerializedStringFixture) Reset(ctx context.Context) error {
return nil
}
func (fixtSerializedStringFixture) PreTest(ctx context.Context, s *testing.FixtTestState) {
}
func (fixtSerializedStringFixture) PostTest(ctx context.Context, s *testing.FixtTestState) {}
func (fixtSerializedStringFixture) TearDown(ctx context.Context, s *testing.FixtState) {}
type fixtSerializedSameBundleFixture struct{}
func (fixtSerializedSameBundleFixture) SetUp(ctx context.Context, s *testing.FixtState) interface{} {
v := ""
if err := s.ParentFillValue(&v); err != nil {
s.Fatal("Failed to get remote parent string value in Setup: ", err)
}
parentValue, ok := s.ParentValue().(string)
if !ok {
s.Fatal("Parent is not avaiable")
}
if v != parentValue {
s.Errorf("Failed to get expected fixture value with FixtFillValue; got %q, want %q", v, parentValue)
}
return v
}
func (fixtSerializedSameBundleFixture) Reset(ctx context.Context) error {
return nil
}
func (fixtSerializedSameBundleFixture) PreTest(ctx context.Context, s *testing.FixtTestState) {
}
func (fixtSerializedSameBundleFixture) PostTest(ctx context.Context, s *testing.FixtTestState) {}
func (fixtSerializedSameBundleFixture) TearDown(ctx context.Context, s *testing.FixtState) {}
type fixtSerializedStructFixture struct{}
func (fixtSerializedStructFixture) SetUp(ctx context.Context, s *testing.FixtState) interface{} {
structValue := meta.TestStruct{}
if err := s.ParentFillValue(&structValue); err != nil {
s.Fatal("Failed to deserialize struct data with FixtFillValue: ", err)
}
if diff := cmp.Diff(&structValue, meta.RemoteFixtureExpectedStructVal); diff != "" {
s.Errorf("Failed to get expected fixture value with FixtFillValue; (-got +want): %s", diff)
}
return structValue
}
func (fixtSerializedStructFixture) Reset(ctx context.Context) error {
return nil
}
func (fixtSerializedStructFixture) PreTest(ctx context.Context, s *testing.FixtTestState) {}
func (fixtSerializedStructFixture) PostTest(ctx context.Context, s *testing.FixtTestState) {}
func (fixtSerializedStructFixture) TearDown(ctx context.Context, s *testing.FixtState) {}
type dataFileFixture struct{}
// DataFiles contains fixture data files information.
type DataFiles struct {
Files []string // Files contains location for all fixture data files.
}
func (dataFileFixture) SetUp(ctx context.Context, s *testing.FixtState) interface{} {
var files []string
for _, fn := range []string{
"fixture_data_internal.txt",
"fixture_data_external.txt",
} {
s.Log("Copying ", fn)
dst := filepath.Join(s.OutDir(), fn)
if err := fsutil.CopyFile(s.DataPath(fn), dst); err != nil {
s.Errorf("Failed copying %s: %s", fn, err)
}
files = append(files, dst)
}
return &DataFiles{Files: files}
}
func (dataFileFixture) Reset(ctx context.Context) error {
return nil
}
func (dataFileFixture) PreTest(ctx context.Context, s *testing.FixtTestState) {}
func (dataFileFixture) PostTest(ctx context.Context, s *testing.FixtTestState) {}
func (dataFileFixture) TearDown(ctx context.Context, s *testing.FixtState) {}
type dutFeatureFixture struct {
feature *protocol.DUTFeatures
}
func (dff *dutFeatureFixture) SetUp(ctx context.Context, s *testing.FixtState) interface{} {
dff.feature = s.Features("")
return dff.feature
}
func (dff *dutFeatureFixture) Reset(ctx context.Context) error {
return nil
}
func (dff *dutFeatureFixture) PreTest(ctx context.Context, s *testing.FixtTestState) {
feature := s.Features("")
allowUnexported := func(reflect.Type) bool { return true }
if diff := cmp.Diff(feature, dff.feature, cmpopts.EquateEmpty(), cmp.Exporter(allowUnexported)); diff != "" {
s.Logf("Got unexpected feature in PreTest (-got +want): %s", diff)
s.Fatal("Got unexpected feature in PreTest")
}
}
func (dff *dutFeatureFixture) PostTest(ctx context.Context, s *testing.FixtTestState) {
feature := s.Features("")
allowUnexported := func(reflect.Type) bool { return true }
if diff := cmp.Diff(feature, dff.feature, cmpopts.EquateEmpty(), cmp.Exporter(allowUnexported)); diff != "" {
s.Logf("Got unexpected feature in PostTest (-got +want): %s", diff)
s.Fatal("Got unexpected feature in PostTest")
}
}
func (dff *dutFeatureFixture) TearDown(ctx context.Context, s *testing.FixtState) {
feature := s.Features("")
allowUnexported := func(reflect.Type) bool { return true }
if diff := cmp.Diff(feature, dff.feature, cmpopts.EquateEmpty(), cmp.Exporter(allowUnexported)); diff != "" {
s.Logf("Got unexpected feature in TearDown (-got +want): %s", diff)
s.Fatal("Got unexpected feature in TearDown")
}
}
type localSetupFailureFixture struct{}
func (localSetupFailureFixture) SetUp(ctx context.Context, s *testing.FixtState) interface{} {
s.Error("Failed intentionally ")
return nil
}
func (localSetupFailureFixture) Reset(ctx context.Context) error {
return nil
}
func (localSetupFailureFixture) PreTest(ctx context.Context, s *testing.FixtTestState) {
}
func (localSetupFailureFixture) PostTest(ctx context.Context, s *testing.FixtTestState) {}
func (localSetupFailureFixture) TearDown(ctx context.Context, s *testing.FixtState) {}
type localParamVal struct {
featureA bool
featureB bool
}
type remoteParamFixture struct{}
type localParamFixture struct{}
func (localParamFixture) SetUp(ctx context.Context, s *testing.FixtState) interface{} {
var v []string
// Since the parent fixture is from a different bundle, ParentFillValue is used.
// If the fixture is from the same bundle, ParentValue can be used, too.
if err := s.ParentFillValue(&v); err != nil {
s.Fatal("Failed to get remote parent string value in Setup: ", err)
}
val := s.Param().(localParamVal)
features := []string{meta.LocalFeature}
if val.featureA {
features = append(features, meta.LocalFeatureA)
}
if val.featureB {
features = append(features, meta.LocalFeatureB)
}
features = append(features, v...)
return features
}
func (localParamFixture) Reset(ctx context.Context) error {
return nil
}
func (localParamFixture) PreTest(ctx context.Context, s *testing.FixtTestState) {}
func (localParamFixture) PostTest(ctx context.Context, s *testing.FixtTestState) {}
func (localParamFixture) TearDown(ctx context.Context, s *testing.FixtState) {}
const (
// BaseFeature is a feature mask for a parameterized factory fixture example.
BaseFeature = iota
// FeatureA is a feature mask for a parameterized factory fixture example.
FeatureA = 1 << iota
// FeatureB is a feature mask for a parameterized factory fixture example.
FeatureB = 1 << iota
)
// supportedFeatures provide a mapping between the supported feature combinations
// and the parent fixtures.
var supportedFeatures = map[uint32]string{
BaseFeature: "metaRemoteParamFixture",
FeatureA: "metaRemoteParamFixture.a",
FeatureB: "metaRemoteParamFixture.b",
FeatureA | FeatureB: "metaRemoteParamFixture.ab",
}
// genParams generates all supported parameters of localParamFixture.
func genParams() (params []testing.FixtureParam) {
for features := range supportedFeatures {
params = append(params, paramFixtureFactory(features))
}
return params
}
// paramFixtureFactory returns a parameterized fixture of localParamFixture.
func paramFixtureFactory(features uint32) testing.FixtureParam {
val := localParamVal{
featureA: (features & FeatureA) != 0,
featureB: (features & FeatureB) != 0,
}
parent, ok := supportedFeatures[features]
if !ok {
panic(fmt.Sprintf("unsupported feature mask: %x", features))
}
return testing.FixtureParam{
Name: paramName(features),
Val: val,
Parent: parent,
}
}
// ParamFixtureName returns a parameterized fixture of localParamFixture.
func ParamFixtureName(features uint32) string {
_, ok := supportedFeatures[features]
if !ok {
panic(fmt.Sprintf("unsupported feature mask: %x", features))
}
return fmt.Sprintf("metaLocalParamFixture.%s", paramName(features))
}
func paramName(features uint32) string {
return fmt.Sprintf("f_%x", features)
}
type localDUTLabConfigFixture struct{}
func (localDUTLabConfigFixture) SetUp(ctx context.Context, s *testing.FixtState) interface{} {
dutLabConfig, err := s.ChromeOSDUTLabConfig("")
if err != nil {
s.Fatal("Failed to get the lab configuration of the DUT: ", err)
}
jsonOut := protojson.Format(dutLabConfig)
const filename = "dut_lab_config_for_fixture.jsonpb"
if err := os.WriteFile(filepath.Join(s.OutDir(), filename), []byte(jsonOut), 0644); err != nil {
s.Fatalf("Failed to create %q: %v", filename, err)
}
return nil
}
func (localDUTLabConfigFixture) Reset(ctx context.Context) error {
return nil
}
func (localDUTLabConfigFixture) PreTest(ctx context.Context, s *testing.FixtTestState) {}
func (localDUTLabConfigFixture) PostTest(ctx context.Context, s *testing.FixtTestState) {}
func (localDUTLabConfigFixture) TearDown(ctx context.Context, s *testing.FixtState) {}