blob: 8802a2eac92292a94117548e28afea2636ee1ce7 [file] [edit]
// Copyright 2019 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 testing
import (
"bytes"
"context"
gotesting "testing"
"time"
"github.com/golang/protobuf/proto"
"github.com/golang/protobuf/ptypes"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"go.chromium.org/chromiumos/config/go/test/api"
"chromiumos/tast/internal/protocol"
"chromiumos/tast/testing/hwdep"
)
// TESTINSTANCETEST is a public test function with a name that's chosen to be appropriate for this file's
// name (test_instance_test.go). The obvious choice, "TestInstanceTest", is unavailable since Go's testing package
// will interpret it as itself being a unit test, so let's just pretend that "instance" and "test" are acronyms.
func TESTINSTANCETEST(context.Context, *State) {}
// InvalidTestName is an arbitrary public test function used by unit tests.
func InvalidTestName(context.Context, *State) {}
// fakePre implements both Precondition for unit tests.
type fakePre struct {
name string // name to return from String
}
func (p *fakePre) Prepare(ctx context.Context, s *PreState) interface{} {
return nil
}
func (p *fakePre) Close(ctx context.Context, s *PreState) {
}
func (p *fakePre) Timeout() time.Duration { return time.Minute }
func (p *fakePre) String() string { return p.name }
func features(availableSWs []string, model string) *protocol.Features {
availableSWSet := make(map[string]struct{})
for _, dep := range availableSWs {
availableSWSet[dep] = struct{}{}
}
var unavailableSWs []string
for _, dep := range []string{"dep0", "dep1", "dep2", "dep3"} {
if _, ok := availableSWSet[dep]; !ok {
unavailableSWs = append(unavailableSWs, dep)
}
}
return &protocol.Features{
CheckDeps: true,
Dut: &protocol.DUTFeatures{
Software: &protocol.SoftwareFeatures{
Available: availableSWs,
Unavailable: unavailableSWs,
},
Hardware: &protocol.HardwareFeatures{
DeprecatedDeviceConfig: &protocol.DeprecatedDeviceConfig{
Id: &protocol.DeprecatedConfigId{
Model: model,
},
},
},
},
}
}
func TestInstantiate(t *gotesting.T) {
got, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Desc: "hello",
Contacts: []string{"a@example.com", "b@example.com"},
Attr: []string{"group:mainline", "informational"},
Data: []string{"data1.txt", "data2.txt"},
Vars: []string{"var1"},
VarDeps: []string{"vardep1"},
SoftwareDeps: []string{"dep1", "dep2"},
HardwareDeps: hwdep.D(hwdep.Model("model1", "model2")),
Timeout: 123 * time.Second,
ServiceDeps: []string{"svc1", "svc2"},
})
if err != nil {
t.Fatal("Failed to instantiate test: ", err)
}
want := []*TestInstance{{
Name: "testing.TESTINSTANCETEST",
Pkg: "chromiumos/tast/internal/testing",
Desc: "hello",
Contacts: []string{"a@example.com", "b@example.com"},
Attr: []string{
"group:mainline",
"informational",
testNameAttrPrefix + "testing.TESTINSTANCETEST",
// The bundle name is the second-to-last component in the package's path.
testBundleAttrPrefix + "internal",
testDepAttrPrefix + "dep1",
testDepAttrPrefix + "dep2",
},
Data: []string{"data1.txt", "data2.txt"},
Vars: []string{"var1"},
VarDeps: []string{"vardep1"},
SoftwareDeps: []string{"dep1", "dep2"},
Timeout: 123 * time.Second,
ServiceDeps: []string{"svc1", "svc2"},
}}
if diff := cmp.Diff(got, want, cmpopts.IgnoreFields(TestInstance{}, "Func", "HardwareDeps")); diff != "" {
t.Errorf("Got unexpected test instances (-got +want):\n%s", diff)
}
if got[0].Func == nil {
t.Error("Got nil Func")
}
}
func TestInstantiateParams(t *gotesting.T) {
got, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Attr: []string{"group:crosbolt"},
Data: []string{"data0.txt"},
SoftwareDeps: []string{"dep0"},
HardwareDeps: hwdep.D(hwdep.Model("model1", "model2")),
Params: []Param{{
Val: 123,
ExtraAttr: []string{"crosbolt_nightly"},
ExtraData: []string{"data1.txt"},
ExtraSoftwareDeps: []string{"dep1"},
ExtraHardwareDeps: hwdep.D(hwdep.SkipOnModel("model2")),
}, {
Name: "foo",
Val: 456,
ExtraAttr: []string{"crosbolt_weekly"},
ExtraData: []string{"data2.txt"},
ExtraSoftwareDeps: []string{"dep2"},
ExtraHardwareDeps: hwdep.D(hwdep.SkipOnModel("model1")),
}},
})
if err != nil {
t.Fatal("Failed to instantiate test: ", err)
}
want := []*TestInstance{
{
Name: "testing.TESTINSTANCETEST",
Pkg: "chromiumos/tast/internal/testing",
Val: 123,
Attr: []string{
"group:crosbolt",
"crosbolt_nightly",
testNameAttrPrefix + "testing.TESTINSTANCETEST",
// The bundle name is the second-to-last component in the package's path.
testBundleAttrPrefix + "internal",
testDepAttrPrefix + "dep0",
testDepAttrPrefix + "dep1",
},
Data: []string{"data0.txt", "data1.txt"},
SoftwareDeps: []string{"dep0", "dep1"},
},
{
Name: "testing.TESTINSTANCETEST.foo",
Pkg: "chromiumos/tast/internal/testing",
Val: 456,
Attr: []string{
"group:crosbolt",
"crosbolt_weekly",
testNameAttrPrefix + "testing.TESTINSTANCETEST.foo",
// The bundle name is the second-to-last component in the package's path.
testBundleAttrPrefix + "internal",
testDepAttrPrefix + "dep0",
testDepAttrPrefix + "dep2",
},
Data: []string{"data0.txt", "data2.txt"},
SoftwareDeps: []string{"dep0", "dep2"},
},
}
if diff := cmp.Diff(got, want, cmpopts.IgnoreFields(TestInstance{}, "Func", "HardwareDeps", "Pre")); diff != "" {
t.Errorf("Got unexpected test instances (-got +want):\n%s", diff)
}
if len(got) == 2 {
if got[0].Func == nil {
t.Error("Got nil Func for the first test instance")
}
reasons, err := got[0].Deps().Check(features([]string{"dep0", "dep1"}, "model1"))
if err != nil {
t.Fatal("Check failed: ", err)
}
if len(reasons) > 0 {
t.Error("Got unexpected HardwareDeps for first test instance: Check returned skip reasons for model1: ", reasons)
}
reasons, err = got[0].Deps().Check(features([]string{"dep0", "dep1"}, "model2"))
if len(reasons) == 0 {
t.Error("Got unexpected HardwareDeps for first test instance: Check returned no skip reason for model2")
}
if got[1].Func == nil {
t.Error("Got nil Func for the second test instance")
}
reasons, err = got[1].Deps().Check(features([]string{"dep0", "dep2"}, "model2"))
if len(reasons) > 0 {
t.Error("Got unexpected HardwareDeps for second test instance: Check returned skip reasons for model2: ", reasons)
}
reasons, err = got[1].Deps().Check(features([]string{"dep0", "dep2"}, "model1"))
if len(reasons) == 0 {
t.Error("Got unexpected HardwareDeps for second test instance: Check returned no skip reason for model1")
}
}
}
func TestInstantiateFixture(t *gotesting.T) {
// Registration without params should succeed.
got, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Fixture: "fixt1",
})
if err != nil {
t.Fatal("Failed to instantiate test: ", err)
}
if len(got) != 1 {
t.Fatalf("Got %d test instances; want 1", len(got))
}
if got[0].Fixture != "fixt1" {
t.Fatalf("TestInstance.Fixture = %q; want %q", got[0].Fixture, "fixt1")
}
// Duplicated fields should be rejected.
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Fixture: "fixt1",
Params: []Param{{
Fixture: "fixt2",
}},
}); err == nil {
t.Error("instantiate succeeded unexpectedly for duplicated Pre")
}
// OK if the field in the base test is unset.
got, err = instantiate(&Test{
Func: TESTINSTANCETEST,
Params: []Param{{
Fixture: "fixt2",
}},
})
if err != nil {
t.Fatal("Failed to instantiate test: ", err)
}
if len(got) != 1 {
t.Fatalf("Got %d test instances; want 1", len(got))
}
if got[0].Fixture != "fixt2" {
t.Fatalf("TestInstance.Fixture = %q; want %q", got[0].Fixture, "fixt2")
}
}
func TestInstantiatePre(t *gotesting.T) {
pre := &fakePre{}
// Registration without params should succeed.
got, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Pre: pre,
})
if err != nil {
t.Fatal("Failed to instantiate test: ", err)
}
if len(got) != 1 {
t.Fatalf("Got %d test instances; want 1", len(got))
}
if got[0].Pre != pre {
t.Fatalf("TestInstance.Pre = %v; want %v", got[0].Pre, pre)
}
// Duplicated fields should be rejected.
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Pre: pre,
Params: []Param{{
Pre: pre,
}},
}); err == nil {
t.Error("instantiate succeeded unexpectedly for duplicated Pre")
}
// OK if the field in the base test is unset.
got, err = instantiate(&Test{
Func: TESTINSTANCETEST,
Params: []Param{{
Pre: pre,
}},
})
if err != nil {
t.Fatal("Failed to instantiate test: ", err)
}
if len(got) != 1 {
t.Fatalf("Got %d test instances; want 1", len(got))
}
if got[0].Pre != pre {
t.Fatalf("TestInstance.Pre = %v; want %v", got[0].Pre, pre)
}
}
func TestInstantiateParamsTimeout(t *gotesting.T) {
const timeout = 123 * time.Second
// Duplicated fields should be rejected.
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Timeout: timeout,
Params: []Param{{
Timeout: timeout,
}},
}); err == nil {
t.Error("instantiate succeeded unexpectedly for duplicated Timeout")
}
// OK if the field in the base test is unset.
got, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Params: []Param{{
Timeout: timeout,
}},
})
if err != nil {
t.Fatal("Failed to instantiate test: ", err)
}
if len(got) != 1 {
t.Fatalf("Got %d test instances; want 1", len(got))
}
if got[0].Timeout != timeout {
t.Fatalf("TestInstance.Timeout = %v; want %v", got[0].Timeout, timeout)
}
}
func TestRelativeDataDir(t *gotesting.T) {
const pkg = "a/b/c"
got := RelativeDataDir(pkg)
want := "a/b/c/data"
if got != want {
t.Errorf("RelativeDataDir(%q) = %q; want %q", pkg, got, want)
}
}
func TestInstantiateNoFunc(t *gotesting.T) {
if _, err := instantiate(&Test{}); err == nil {
t.Error("Didn't get error with missing function")
}
}
// TestValidateName tests name validation of instantiate.
// It is better to call instantiate instead, but it is difficult to define
// Go functions with corresponding names.
func TestValidateName(t *gotesting.T) {
for _, tc := range []struct {
name string
valid bool
}{
{"example.ChromeLogin", true},
{"example.ChromeLogin2", true},
{"example2.ChromeLogin", true},
{"example.ChromeLogin.stress", true},
{"example.ChromeLogin.more_stress", true},
{"example.chromeLogin", false},
{"example.7hromeLogin", false},
{"example.Chrome_Login", false},
{"example.Chrome@Login", false},
{"Example.ChromeLogin", false},
{"3xample.ChromeLogin", false},
{"exam_ple.ChromeLogin", false},
{"exam@ple.ChromeLogin", false},
{"example.ChromeLogin.Stress", false},
{"example.ChromeLogin.more-stress", false},
{"example.ChromeLogin.more@stress", false},
} {
err := validateName(tc.name)
if err != nil && tc.valid {
t.Errorf("validateName(%q) failed: %v", tc.name, err)
} else if err == nil && !tc.valid {
t.Errorf("validateName(%q) didn't return expected error", tc.name)
}
}
}
// TestValidateFileName tests file name validation of instantiate.
// It is better to call instantiate instead, but it is difficult to define
// Go functions with corresponding names.
func TestValidateFileName(t *gotesting.T) {
for _, tc := range []struct {
name, fn string
valid bool
}{
{"Test", "test.go", true}, // single word
{"MyTest", "my_test.go", true}, // two words separated with underscores
{"LoadURL", "load_url.go", true}, // word and acronym
{"PlayMP3", "play_mp3.go", true}, // word contains numbers
{"PlayMP3Song", "play_mp3_song.go", true}, // acronym followed by word
{"ConnectToDBus", "connect_to_dbus.go", true}, // word with multiple leading caps
{"RestartCrosVM", "restart_crosvm.go", true}, // word with ending acronym
{"RestartCrosVM", "restart_cros_vm.go", true}, // word followed by acronym
{"Foo123bar", "foo123bar.go", true}, // word contains digits
{"Foo123Bar", "foo123_bar.go", true}, // word with trailing digits
{"Foo123bar", "foo_123bar.go", true}, // word with leading digits
{"Foo123Bar", "foo_123_bar.go", true}, // word consisting only of digits
{"foo", "foo.go", false}, // lowercase func name
{"Foobar", "foo_bar.go", false}, // lowercase word
{"FirstTest", "first.go", false}, // func name has word not in filename
{"Firstblah", "first.go", false}, // func name has word longer than filename
{"First", "firstabc.go", false}, // filename has word longer than func name
{"First", "first_test.go", false}, // filename has word not in func name
{"FooBar", "foo__bar.go", false}, // empty word in filename
{"Foo", "bar.go", false}, // completely different words
{"Foo", "Foo.go", false}, // non-lowercase filename
{"Foo", "foo.txt", false}, // filename without ".go" extension
} {
err := validateFileName(tc.name, tc.fn)
if err != nil && tc.valid {
t.Errorf("validateFileName(%q, %q) failed: %v", tc.name, tc.fn, err)
} else if err == nil && !tc.valid {
t.Errorf("validateFileName(%q, %q) didn't return expected error", tc.name, tc.fn)
}
}
}
// TestInstantiateFuncName makes sure the validateFileName runs against the name
// derived from the Func's function name and its source file name.
func TestInstantiateFuncName(t *gotesting.T) {
if _, err := instantiate(&Test{Func: TESTINSTANCETEST}); err != nil {
t.Error("instantiate failed: ", err)
}
if _, err := instantiate(&Test{Func: InvalidTestName}); err == nil {
t.Error("instantiate succeeded unexpectedly for wrongly named test func")
}
}
func TestInstantiateDataPath(t *gotesting.T) {
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Data: []string{"foo", "bar/baz"},
}); err != nil {
t.Errorf("Got an unexpected error: %v", err)
}
}
func TestInstantiateUncleanDataPath(t *gotesting.T) {
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Data: []string{"foo", "bar/../bar/baz"},
}); err == nil {
t.Error("Did not get an error with unclean path")
}
}
func TestInstantiateAbsoluteDataPath(t *gotesting.T) {
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Data: []string{"foo", "/etc/passwd"},
}); err == nil {
t.Error("Did not get an error with absolute path")
}
}
func TestInstantiateRelativeDataPath(t *gotesting.T) {
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Data: []string{"foo", "../baz"},
}); err == nil {
t.Error("Did not get an error with relative path")
}
}
func TestInstantiateReservedAttrPrefixes(t *gotesting.T) {
for _, attr := range []string{
testNameAttrPrefix + "foo",
testBundleAttrPrefix + "bar",
testDepAttrPrefix + "dep",
} {
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Attr: []string{attr},
}); err == nil {
t.Errorf("Did not get an error for reserved attribute %q", attr)
}
}
}
func TestValidateVars_OK(t *gotesting.T) {
const (
category = "a"
name = "B"
)
for _, vars := range [][]string{
{"x"},
{"x.c"},
{"a.B.c", "a.c"},
{"a.B.cC1_"},
} {
if err := validateVars(category, name, vars); err != nil {
t.Errorf("validateVars(%v, %v, %v) = %v; want nil", category, name, vars, err)
}
}
}
func TestValidateVars_Error(t *gotesting.T) {
const (
category = "a"
name = "B"
)
for _, vars := range [][]string{
{"a."},
{"a.c", "a."},
{"a.B."},
{"a.X.c"},
{"a.B.c."},
{"a.B._"},
{"a", "a"},
} {
if err := validateVars(category, name, vars); err == nil {
t.Errorf("validateVars(%v, %v, %v) = nil; want error", category, name, vars)
}
}
}
func TestInstantiateNegativeTimeout(t *gotesting.T) {
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Timeout: -1 * time.Second,
}); err == nil {
t.Error("Didn't get error with negative timeout")
}
}
func TestInstantiateDuplicatedParamName(t *gotesting.T) {
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Params: []Param{{
Name: "abc",
}, {
Name: "abc",
}},
}); err == nil {
t.Error("Did not get an error with duplicated param case names")
}
}
func TestInstantiateInconsistentParamValType(t *gotesting.T) {
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Params: []Param{{
Name: "case1",
Val: 1,
}, {
Name: "case2",
Val: "string",
}},
}); err == nil {
t.Error("Did not get an error with param cases containing different value type")
}
}
func TestInstantiateNoManualDisabled(t *gotesting.T) {
for _, attrs := range [][]string{
{"disabled"},
{"group:mainline", "disabled"},
{"group:crosbolt", "disabled"},
} {
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Attr: attrs,
}); err == nil {
t.Errorf("instantiate unexpectedly succeeded with Attr = %q", attrs)
}
}
}
func TestInstantiateCompatAttrs(t *gotesting.T) {
got, err := instantiate(&Test{
Func: TESTINSTANCETEST,
})
if err != nil {
t.Fatal("Failed to instantiate test: ", err)
}
want := []*TestInstance{{
Name: "testing.TESTINSTANCETEST",
Pkg: "chromiumos/tast/internal/testing",
Attr: []string{
testNameAttrPrefix + "testing.TESTINSTANCETEST",
// The bundle name is the second-to-last component in the package's path.
testBundleAttrPrefix + "internal",
// This attribute is added for compatibility.
"disabled",
},
}}
if diff := cmp.Diff(got, want, cmpopts.IgnoreFields(TestInstance{}, "Func", "HardwareDeps", "Pre")); diff != "" {
t.Errorf("Got unexpected test instances (-got +want):\n%s", diff)
}
}
type nonPointerPre struct{}
func (p nonPointerPre) Prepare(ctx context.Context, s *PreState) interface{} { return nil }
func (p nonPointerPre) Close(ctx context.Context, s *PreState) {}
func (p nonPointerPre) Timeout() time.Duration { return time.Minute }
func (p nonPointerPre) String() string { return "nonPointerPre" }
func TestInstantiateNonPointerPrecondition(t *gotesting.T) {
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Pre: &nonPointerPre{},
}); err != nil {
t.Fatal("Instanciate failed for pointer pre: ", err)
}
if _, err := instantiate(&Test{
Func: TESTINSTANCETEST,
Pre: nonPointerPre{},
}); err == nil {
t.Fatal("Instanciate unexpectedly succeeded for non-pointer pre")
}
}
func TestSoftwareDeps(t *gotesting.T) {
fs := &protocol.Features{
CheckDeps: true,
Dut: &protocol.DUTFeatures{
Software: &protocol.SoftwareFeatures{
Available: []string{"dep1", "dep2"},
Unavailable: []string{"dep0", "dep3"},
},
},
}
for _, tc := range []struct {
name string
testDeps []string
wantReasons []string
wantErr string
}{{
name: "error",
testDeps: []string{"dep3", "dep1", "dep2", "depX"},
wantErr: "unknown SoftwareDeps: depX",
}, {
name: "pass",
testDeps: []string{"dep1", "dep2"},
}, {
name: "skip",
testDeps: []string{"dep0", "dep1"},
wantReasons: []string{"missing SoftwareDeps: dep0"},
}} {
t.Run(tc.name, func(t *gotesting.T) {
reasons, err := (&TestInstance{SoftwareDeps: tc.testDeps}).Deps().Check(fs)
if tc.wantErr == "" && err != nil {
t.Errorf("Check() failed: %v", err)
}
if tc.wantErr != "" && (err == nil || err.Error() != tc.wantErr) {
t.Errorf("Check() returned error %v; want %q", err, tc.wantErr)
}
if diff := cmp.Diff(reasons, tc.wantReasons); diff != "" {
t.Errorf("Reasons unmatch (-got +want):\n%v", diff)
}
})
}
}
func TestHardwareDeps(t *gotesting.T) {
fs := &protocol.Features{
CheckDeps: true,
Dut: &protocol.DUTFeatures{
Hardware: &protocol.HardwareFeatures{
DeprecatedDeviceConfig: &protocol.DeprecatedDeviceConfig{
Id: &protocol.DeprecatedConfigId{
Model: "samus",
},
},
},
},
}
for _, tc := range []struct {
modelDep string
wantReasons []string
}{{
modelDep: "eve",
wantReasons: []string{"ModelId did not match"},
}, {
modelDep: "samus",
}} {
t.Run(tc.modelDep, func(t *gotesting.T) {
reasons, err := (&TestInstance{HardwareDeps: hwdep.D(hwdep.Model(tc.modelDep))}).Deps().Check(fs)
if err != nil {
t.Fatal("Check failed: ", err)
}
if diff := cmp.Diff(reasons, tc.wantReasons); diff != "" {
t.Errorf("Reasons unmatch (-got +want):\n%v", diff)
}
})
}
}
func TestVarDeps(t *gotesting.T) {
test := TestInstance{VarDeps: []string{"foo"}}
for _, tc := range []struct {
name string
features *protocol.Features
wantReasons []string
wantErr bool
}{{
name: "pass",
features: &protocol.Features{
CheckDeps: true,
Infra: &protocol.InfraFeatures{
Vars: map[string]string{"foo": "val"},
},
},
wantReasons: nil,
}, {
name: "fail",
features: &protocol.Features{
CheckDeps: true,
Infra: &protocol.InfraFeatures{
Vars: map[string]string{},
},
},
wantErr: true,
}, {
name: "skip",
features: &protocol.Features{
CheckDeps: true,
Infra: &protocol.InfraFeatures{
Vars: map[string]string{},
MaybeMissingVars: `foo`,
},
},
wantReasons: []string{"runtime variable foo is missing and matches with ^foo$"},
}} {
t.Run(tc.name, func(t *gotesting.T) {
reasons, err := test.Deps().Check(tc.features)
if diff := cmp.Diff(reasons, tc.wantReasons); diff != "" {
t.Errorf("Reasons mismatch (-got +want):\n%v", diff)
}
if err != nil && !tc.wantErr {
t.Errorf("Got error unexpectedly: %v", err)
}
if err == nil && tc.wantErr {
t.Errorf("Got no error, want error")
}
})
}
}
func TestTestInstanceEntityProto(t *gotesting.T) {
test := &TestInstance{
Name: "pkg.Test",
Pkg: "chromiumos/foo/bar",
Val: "somevalue",
Func: TESTINSTANCETEST,
Desc: "Description",
Contacts: []string{"me@example.com"},
Attr: []string{"attr1", "attr2"},
Data: []string{"foo.txt"},
Vars: []string{"var1", "var2"},
VarDeps: []string{"vardep1"},
SoftwareDeps: []string{"dep1", "dep2"},
ServiceDeps: []string{"svc1", "svc2"},
Fixture: "fixt",
Timeout: time.Hour,
Bundle: "bundle",
}
got := test.EntityProto()
want := &protocol.Entity{
Name: "pkg.Test",
Package: "chromiumos/foo/bar",
Attributes: []string{"attr1", "attr2"},
Description: "Description",
Fixture: "fixt",
Dependencies: &protocol.EntityDependencies{
DataFiles: []string{"foo.txt"},
Services: []string{"svc1", "svc2"},
},
Contacts: &protocol.EntityContacts{
Emails: []string{"me@example.com"},
},
LegacyData: &protocol.EntityLegacyData{
Variables: []string{"var1", "var2"},
VariableDeps: []string{"vardep1"},
SoftwareDeps: []string{"dep1", "dep2"},
Timeout: ptypes.DurationProto(time.Hour),
Bundle: "bundle",
},
}
if diff := cmp.Diff(got, want); diff != "" {
t.Errorf("Got unexpected Entity (-got +want):\n%s", diff)
}
}
func TestTestClone(t *gotesting.T) {
const (
name = "OldTest"
timeout = time.Minute
)
attr := []string{"a", "b"}
softwareDeps := []string{"sw1", "sw2"}
serviceDeps := []string{"svc1", "svc2"}
f := func(context.Context, *State) {}
// Checks that tst's fields still contain the above values.
checkTest := func(msg string, tst *TestInstance) {
// Go doesn't allow checking functions for equality.
if tst.Func == nil {
t.Errorf("%s set Func to nil; want %p", msg, f)
}
want := &TestInstance{
Name: name,
Attr: attr,
SoftwareDeps: softwareDeps,
ServiceDeps: serviceDeps,
Timeout: timeout,
}
if diff := cmp.Diff(tst, want, cmpopts.IgnoreFields(TestInstance{}, "Func", "HardwareDeps")); diff != "" {
t.Errorf("Unexpected instance after %s; (-got +want):\n%v", msg, diff)
}
}
// First check that a cloned copy gets the correct values.
orig := TestInstance{
Name: name,
Func: f,
Attr: append([]string(nil), attr...),
SoftwareDeps: append([]string(nil), softwareDeps...),
ServiceDeps: append([]string(nil), serviceDeps...),
Timeout: timeout,
}
clone := orig.clone()
checkTest("clone()", clone)
// Now update fields in the copy and check that the original is unaffected.
clone.Name = "NewTest"
clone.Func = nil
clone.Attr[0] = "new"
clone.Timeout = 2 * timeout
clone.SoftwareDeps[0] = "swnew"
clone.ServiceDeps[0] = "svcnew"
checkTest("update after clone()", &orig)
}
func TestWriteTestsAsProto(t *gotesting.T) {
in := []*TestInstance{
{
Name: "test001",
Attr: []string{"attr1", "attr2"},
HardwareDeps: hwdep.D(),
Contacts: []string{
"someone1@chromium.org",
"someone2@chromium.org",
},
},
}
expected := api.TestCaseMetadataList{
Values: []*api.TestCaseMetadata{
{
TestCase: &api.TestCase{
Id: &api.TestCase_Id{
Value: testHarnessPrefix + "." + "test001",
},
Name: "test001",
Tags: []*api.TestCase_Tag{
{Value: "attr1"},
{Value: "attr2"},
},
},
TestCaseExec: &api.TestCaseExec{
TestHarness: &api.TestHarness{
TestHarnessType: &api.TestHarness_Tast_{
Tast: &api.TestHarness_Tast{},
},
},
},
TestCaseInfo: &api.TestCaseInfo{
Owners: []*api.Contact{
{Email: "someone1@chromium.org"},
{Email: "someone2@chromium.org"},
},
},
},
},
}
var b bytes.Buffer
WriteTestsAsProto(&b, in)
var actual api.TestCaseMetadataList
proto.Unmarshal(b.Bytes(), &actual)
if !cmp.Equal(expected, actual, cmp.Comparer(proto.Equal)) {
t.Errorf("WriteTestsAsProto(%v): got %v; want %v", in, actual, expected)
}
}