blob: 781538d1f636efaed46be8001845fd8fe16f204c [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 bundle_test
import (
"context"
"fmt"
"io"
"io/ioutil"
"path/filepath"
"sync"
gotesting "testing"
"time"
"github.com/google/go-cmp/cmp"
"go.chromium.org/tast/core/internal/bundle/bundletest"
"go.chromium.org/tast/core/internal/fakesshserver"
"go.chromium.org/tast/core/internal/logging"
"go.chromium.org/tast/core/internal/protocol"
"go.chromium.org/tast/core/internal/protocol/protocoltest"
"go.chromium.org/tast/core/internal/testcontext"
"go.chromium.org/tast/core/internal/testing"
"go.chromium.org/tast/core/internal/testing/testfixture"
"go.chromium.org/tast/core/testutil"
)
func setUpWithDefaultDUT(t *gotesting.T, os ...bundletest.Option) *bundletest.Env {
defaultPrimaryDUT := bundletest.WithPrimaryDUT(&bundletest.DUTConfig{
ExtraSSHHandlers: []fakesshserver.Handler{
fakesshserver.ExactMatchHandler("exec cat /proc/sys/kernel/random/boot_id", func(_ io.Reader, stdout, stderr io.Writer) int {
io.WriteString(stdout, "fake-boot-id")
return 0
}),
},
})
opts := append(append([]bundletest.Option(nil), os...), defaultPrimaryDUT)
return bundletest.SetUp(
t,
opts...,
)
}
func TestTestServiceRunTests(t *gotesting.T) {
localTest1 := &testing.TestInstance{
Name: "foo.Local1",
Pkg: "foo",
Data: []string{"data.txt"},
Func: func(ctx context.Context, s *testing.State) {
s.Log("Local test 1")
ioutil.WriteFile(filepath.Join(s.OutDir(), "out.txt"), []byte("local1"), 0644)
b, err := ioutil.ReadFile(s.DataPath("data.txt"))
if err != nil {
t.Fatal(err)
}
if got, want := string(b), "local data"; got != want {
t.Errorf("Got %v, want %v", got, want)
}
},
Fixture: "remoteFixture",
Timeout: time.Minute,
}
localTest2 := &testing.TestInstance{
Name: "foo.Local2",
Func: func(ctx context.Context, s *testing.State) {
s.Log("Local test 2")
ioutil.WriteFile(filepath.Join(s.OutDir(), "out.txt"), []byte("local2"), 0644)
},
Fixture: "remoteFixture",
Timeout: time.Minute,
}
remoteTest := &testing.TestInstance{
Name: "foo.Remote",
Func: func(ctx context.Context, s *testing.State) {
s.Log("Remote testing")
ioutil.WriteFile(filepath.Join(s.OutDir(), "out.txt"), []byte("remote"), 0644)
},
Timeout: time.Minute,
}
parentRemoteFixture := &testing.FixtureInstance{
Name: "parentRemoteFixture",
Pkg: "fixt",
Impl: testfixture.New(
testfixture.WithSetUp(func(ctx context.Context, s *testing.FixtState) interface{} {
s.Log("Parent fixture SetUp")
return nil
}),
),
}
remoteFixture := &testing.FixtureInstance{
Name: "remoteFixture",
Parent: "parentRemoteFixture",
Pkg: "fixt",
Data: []string{"data.txt"},
Impl: testfixture.New(
testfixture.WithSetUp(func(ctx context.Context, s *testing.FixtState) interface{} {
s.Log("Remote fixture SetUp")
b, err := ioutil.ReadFile(s.DataPath("data.txt"))
if err != nil {
t.Fatal(err)
}
if got, want := string(b), "fixture data"; got != want {
t.Errorf("Got %v, want %v", got, want)
}
if err := ioutil.WriteFile(filepath.Join(s.OutDir(), "set_up.txt"), []byte("set up"), 0644); err != nil {
t.Error(err)
}
return nil
}),
testfixture.WithTearDown(func(ctx context.Context, s *testing.FixtState) {
s.Log("Remote fixture TearDown")
if err := ioutil.WriteFile(filepath.Join(s.OutDir(), "tear_down.txt"), []byte("tear down"), 0644); err != nil {
t.Error(err)
}
}),
testfixture.WithPreTest(func(ctx context.Context, s *testing.FixtTestState) {
s.Log("Remote fixture PreTest")
if err := ioutil.WriteFile(filepath.Join(s.OutDir(), "pre_test.txt"), []byte("pre test"), 0644); err != nil {
t.Error(err)
}
}),
testfixture.WithPostTest(func(ctx context.Context, s *testing.FixtTestState) {
s.Log("Remote fixture PostTest")
if err := ioutil.WriteFile(filepath.Join(s.OutDir(), "post_test.txt"), []byte("post test"), 0644); err != nil {
t.Error(err)
}
}),
testfixture.WithReset(func(ctx context.Context) error {
logging.Info(ctx, "Remote fixture Reset")
dir, ok := testcontext.OutDir(ctx)
if !ok {
t.Error("No OutDir access in Reset")
}
if err := ioutil.WriteFile(filepath.Join(dir, "reset.txt"), []byte("reset"), 0644); err != nil {
t.Error(err)
}
return nil
}),
),
}
localReg := testing.NewRegistry("bundle")
localReg.AddTestInstance(localTest1)
localReg.AddTestInstance(localTest2)
remoteReg := testing.NewRegistry("bundle")
remoteReg.AddTestInstance(remoteTest)
remoteReg.AddFixtureInstance(remoteFixture)
remoteReg.AddFixtureInstance(parentRemoteFixture)
env := setUpWithDefaultDUT(
t,
bundletest.WithLocalBundles(localReg),
bundletest.WithRemoteBundles(remoteReg),
bundletest.WithLocalData(map[string]string{
"foo/data/data.txt": "local data",
}),
bundletest.WithRemoteData(map[string]string{
"fixt/data/data.txt": "fixture data",
}),
)
cl := protocol.NewTestServiceClient(env.DialRemoteBundle(context.Background(), t, remoteReg.Name()))
events, err := protocoltest.RunTestsRecursiveForEvents(context.Background(),
cl, env.RunConfig(), protocoltest.WithEntityLogs(),
)
if err != nil {
t.Fatalf("RunTests failed: %v", err)
}
wantEvents := []protocol.Event{
// Fixture set up
&protocol.EntityStartEvent{Entity: parentRemoteFixture.EntityProto()},
&protocol.EntityLogEvent{EntityName: parentRemoteFixture.Name, Text: "Parent fixture SetUp"},
&protocol.EntityStartEvent{Entity: remoteFixture.EntityProto()},
&protocol.EntityLogEvent{EntityName: remoteFixture.Name, Text: "Remote fixture SetUp"},
// First test
&protocol.EntityStartEvent{Entity: localTest1.EntityProto()},
&protocol.EntityLogEvent{EntityName: localTest1.Name, Text: "Remote fixture PreTest"},
&protocol.EntityLogEvent{EntityName: localTest1.Name, Text: "Local test 1"},
&protocol.EntityLogEvent{EntityName: localTest1.Name, Text: "Remote fixture PostTest"},
&protocol.EntityEndEvent{EntityName: localTest1.Name},
&protocol.EntityLogEvent{EntityName: remoteFixture.Name, Text: "Remote fixture Reset"},
// Second test
&protocol.EntityStartEvent{Entity: localTest2.EntityProto()},
&protocol.EntityLogEvent{EntityName: localTest2.Name, Text: "Remote fixture PreTest"},
&protocol.EntityLogEvent{EntityName: localTest2.Name, Text: "Local test 2"},
&protocol.EntityLogEvent{EntityName: localTest2.Name, Text: "Remote fixture PostTest"},
&protocol.EntityEndEvent{EntityName: localTest2.Name},
// Fixture tear down
&protocol.EntityLogEvent{EntityName: remoteFixture.Name, Text: "Remote fixture TearDown"},
&protocol.EntityEndEvent{EntityName: remoteFixture.Name},
&protocol.EntityEndEvent{EntityName: parentRemoteFixture.Name},
// Remote test
&protocol.EntityStartEvent{Entity: remoteTest.EntityProto()},
&protocol.EntityLogEvent{EntityName: remoteTest.Name, Text: "Remote testing"},
&protocol.EntityEndEvent{EntityName: remoteTest.Name},
}
if diff := cmp.Diff(events, wantEvents, protocoltest.EventCmpOpts...); diff != "" {
t.Errorf("Events mismatch (-got +want):\n%s", diff)
}
m, err := testutil.ReadFiles(env.RemoteOutDir())
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(m, map[string]string{
"tests/foo.Local1/out.txt": "local1",
"tests/foo.Local2/out.txt": "local2",
"foo.Local1/pre_test.txt": "pre test",
"foo.Local1/post_test.txt": "post test",
"foo.Local2/pre_test.txt": "pre test",
"foo.Local2/post_test.txt": "post test",
"foo.Remote/out.txt": "remote",
"remoteFixture/set_up.txt": "set up",
"remoteFixture/tear_down.txt": "tear down",
"remoteFixture/reset.txt": "reset",
}); diff != "" {
t.Errorf("OutDir mismatch (-got +want):\n%s", diff)
}
m, err = testutil.ReadFiles(env.LocalOutDir())
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(m, map[string]string{}); diff != "" {
t.Errorf("Local OutDir is non-empty unexpectedly (-got +want):\n%s", diff)
}
}
func TestTestServiceRunTests_SimpleRemoteLocal(t *gotesting.T) {
localTest := &testing.TestInstance{
Name: "foo.Local",
Func: func(ctx context.Context, s *testing.State) {},
Fixture: "remoteFixture",
Timeout: time.Minute,
}
remoteFixture := &testing.FixtureInstance{
Name: "remoteFixture",
Pkg: "fixt",
Impl: testfixture.New(),
}
localReg := testing.NewRegistry("bundle")
localReg.AddTestInstance(localTest)
remoteReg := testing.NewRegistry("bundle")
remoteReg.AddFixtureInstance(remoteFixture)
env := setUpWithDefaultDUT(
t,
bundletest.WithLocalBundles(localReg),
bundletest.WithRemoteBundles(remoteReg),
)
cl := protocol.NewTestServiceClient(env.DialRemoteBundle(context.Background(), t, remoteReg.Name()))
events, err := protocoltest.RunTestsRecursiveForEvents(context.Background(),
cl, env.RunConfig(),
)
if err != nil {
t.Fatalf("RunTests failed: %v", err)
}
wantEvents := []protocol.Event{
&protocol.EntityStartEvent{Entity: remoteFixture.EntityProto()},
&protocol.EntityStartEvent{Entity: localTest.EntityProto()},
&protocol.EntityEndEvent{EntityName: localTest.Name},
&protocol.EntityEndEvent{EntityName: remoteFixture.Name},
}
if diff := cmp.Diff(events, wantEvents, protocoltest.EventCmpOpts...); diff != "" {
t.Errorf("Events mismatch (-got +want):\n%s", diff)
}
}
func TestTestServiceRunTests_NoTests(t *gotesting.T) {
env := setUpWithDefaultDUT(t,
bundletest.WithRemoteBundles(testing.NewRegistry("bundle")),
bundletest.WithLocalBundles(testing.NewRegistry("bundle")),
)
cl := protocol.NewTestServiceClient(env.DialRemoteBundle(context.Background(), t, "bundle"))
if _, err := protocoltest.RunTestsRecursiveForEvents(context.Background(), cl, env.RunConfig()); err != nil {
t.Fatalf("RunTests failed for empty tests: %v", err)
}
}
func TestTestServiceRunTests_RemoteOnly(t *gotesting.T) {
remoteTest := &testing.TestInstance{
Name: "foo.Remote",
Fixture: "remoteFixture",
Func: func(ctx context.Context, s *testing.State) {
s.Log("Remote testing")
},
Timeout: time.Minute,
}
remoteFixture := &testing.FixtureInstance{
Name: "remoteFixture",
Pkg: "fixt",
Impl: testfixture.New(
testfixture.WithSetUp(func(ctx context.Context, s *testing.FixtState) interface{} {
s.Log("Remote fixture SetUp")
return nil
}),
testfixture.WithTearDown(func(ctx context.Context, s *testing.FixtState) {
s.Log("Remote fixture TearDown")
}),
),
}
remoteReg := testing.NewRegistry("bundle")
remoteReg.AddTestInstance(remoteTest)
remoteReg.AddFixtureInstance(remoteFixture)
env := setUpWithDefaultDUT(
t,
bundletest.WithLocalBundles(testing.NewRegistry("bundle")),
bundletest.WithRemoteBundles(remoteReg),
)
cl := protocol.NewTestServiceClient(env.DialRemoteBundle(context.Background(), t, remoteReg.Name()))
events, err := protocoltest.RunTestsRecursiveForEvents(context.Background(),
cl, env.RunConfig(), protocoltest.WithEntityLogs(),
)
if err != nil {
t.Fatalf("RunTests failed: %v", err)
}
wantEvents := []protocol.Event{
&protocol.EntityStartEvent{Entity: remoteFixture.EntityProto()},
&protocol.EntityLogEvent{EntityName: remoteFixture.Name, Text: "Remote fixture SetUp"},
&protocol.EntityStartEvent{Entity: remoteTest.EntityProto()},
&protocol.EntityLogEvent{EntityName: remoteTest.Name, Text: "Remote testing"},
&protocol.EntityEndEvent{EntityName: remoteTest.Name},
&protocol.EntityLogEvent{EntityName: remoteFixture.Name, Text: "Remote fixture TearDown"},
&protocol.EntityEndEvent{EntityName: remoteFixture.Name},
}
if diff := cmp.Diff(events, wantEvents, protocoltest.EventCmpOpts...); diff != "" {
t.Errorf("Events mismatch (-got +want):\n%s", diff)
}
}
func TestTestServiceRunTests_LocalOnly(t *gotesting.T) {
localTest := &testing.TestInstance{
Name: "foo.Local",
Fixture: "localFixture",
Func: func(ctx context.Context, s *testing.State) {
s.Log("Local testing")
},
Timeout: time.Minute,
}
localFixture := &testing.FixtureInstance{
Name: "localFixture",
Pkg: "fixt",
Impl: testfixture.New(
testfixture.WithSetUp(func(ctx context.Context, s *testing.FixtState) interface{} {
s.Log("Local fixture SetUp")
return nil
}),
testfixture.WithTearDown(func(ctx context.Context, s *testing.FixtState) {
s.Log("Local fixture TearDown")
}),
),
}
localReg := testing.NewRegistry("bundle")
localReg.AddTestInstance(localTest)
localReg.AddFixtureInstance(localFixture)
env := setUpWithDefaultDUT(
t,
bundletest.WithLocalBundles(localReg),
bundletest.WithRemoteBundles(testing.NewRegistry("bundle")),
)
cl := protocol.NewTestServiceClient(env.DialRemoteBundle(context.Background(), t, localReg.Name()))
events, err := protocoltest.RunTestsRecursiveForEvents(context.Background(),
cl, env.RunConfig(), protocoltest.WithEntityLogs(),
)
if err != nil {
t.Fatalf("RunTests failed: %v", err)
}
wantEvents := []protocol.Event{
&protocol.EntityStartEvent{Entity: localFixture.EntityProto()},
&protocol.EntityLogEvent{EntityName: localFixture.Name, Text: "Local fixture SetUp"},
&protocol.EntityStartEvent{Entity: localTest.EntityProto()},
&protocol.EntityLogEvent{EntityName: localTest.Name, Text: "Local testing"},
&protocol.EntityEndEvent{EntityName: localTest.Name},
&protocol.EntityLogEvent{EntityName: localFixture.Name, Text: "Local fixture TearDown"},
&protocol.EntityEndEvent{EntityName: localFixture.Name},
}
if diff := cmp.Diff(events, wantEvents, protocoltest.EventCmpOpts...); diff != "" {
t.Errorf("Events mismatch (-got +want):\n%s", diff)
}
}
func TestTestServiceRunTests_Failures(t *gotesting.T) {
localPass := &testing.TestInstance{
Name: "local.Pass",
Timeout: time.Minute,
Func: func(ctx context.Context, s *testing.State) {},
}
localFail := &testing.TestInstance{
Name: "local.Fail",
Timeout: time.Minute,
Func: func(ctx context.Context, s *testing.State) {
s.Error("Failed")
},
}
localSkip := &testing.TestInstance{
Name: "local.Skip",
Timeout: time.Minute,
SoftwareDeps: map[string][]string{"": []string{"missing"}},
Func: func(ctx context.Context, s *testing.State) {},
}
localReg := testing.NewRegistry("bundle")
localReg.AddTestInstance(localPass)
localReg.AddTestInstance(localFail)
localReg.AddTestInstance(localSkip)
remotePass := &testing.TestInstance{
Name: "remote.Pass",
Timeout: time.Minute,
Func: func(ctx context.Context, s *testing.State) {},
}
remoteFail := &testing.TestInstance{
Name: "remote.Fail",
Timeout: time.Minute,
Func: func(ctx context.Context, s *testing.State) {
s.Error("Failed")
},
}
remoteSkip := &testing.TestInstance{
Name: "remote.Skip",
Timeout: time.Minute,
SoftwareDeps: map[string][]string{"": []string{"missing"}},
Func: func(ctx context.Context, s *testing.State) {},
}
remoteReg := testing.NewRegistry("bundle")
remoteReg.AddTestInstance(remotePass)
remoteReg.AddTestInstance(remoteFail)
remoteReg.AddTestInstance(remoteSkip)
env := setUpWithDefaultDUT(t,
bundletest.WithLocalBundles(localReg),
bundletest.WithRemoteBundles(remoteReg),
)
cl := protocol.NewTestServiceClient(env.DialRemoteBundle(context.Background(), t, "bundle"))
events, err := protocoltest.RunTestsRecursiveForEvents(context.Background(), cl, env.RunConfig())
if err != nil {
t.Fatal(err)
}
wantEvents := []protocol.Event{
// Report skipped tests first.
&protocol.EntityStartEvent{Entity: localSkip.EntityProto()},
&protocol.EntityErrorEvent{EntityName: localSkip.Name, Error: &protocol.Error{Reason: "unknown SoftwareDeps: missing"}},
&protocol.EntityEndEvent{EntityName: localSkip.Name},
&protocol.EntityStartEvent{Entity: localFail.EntityProto()},
&protocol.EntityErrorEvent{EntityName: localFail.Name, Error: &protocol.Error{Reason: "Failed"}},
&protocol.EntityEndEvent{EntityName: localFail.Name},
&protocol.EntityStartEvent{Entity: localPass.EntityProto()},
&protocol.EntityEndEvent{EntityName: localPass.Name},
&protocol.EntityStartEvent{Entity: remoteSkip.EntityProto()},
&protocol.EntityErrorEvent{EntityName: remoteSkip.Name, Error: &protocol.Error{Reason: "unknown SoftwareDeps: missing"}},
&protocol.EntityEndEvent{EntityName: remoteSkip.Name},
&protocol.EntityStartEvent{Entity: remoteFail.EntityProto()},
&protocol.EntityErrorEvent{EntityName: remoteFail.Name, Error: &protocol.Error{Reason: "Failed"}},
&protocol.EntityEndEvent{EntityName: remoteFail.Name},
&protocol.EntityStartEvent{Entity: remotePass.EntityProto()},
&protocol.EntityEndEvent{EntityName: remotePass.Name},
}
if diff := cmp.Diff(events, wantEvents, protocoltest.EventCmpOpts...); diff != "" {
t.Errorf("Events mismatch (-got +want):\n%s", diff)
}
}
func TestTestServiceRunTests_LocalTestRemoteFixtureFailures(t *gotesting.T) {
localNoSuchFixture := &testing.TestInstance{
Name: "local.NoSuchFixture",
Fixture: "noSuchFixture",
Timeout: time.Minute,
Func: func(ctx context.Context, s *testing.State) {
s.Log("Unexpected log")
},
}
localFailSetUpRemoteFixture := &testing.TestInstance{
Name: "local.FailSetUpRemoteFixture",
Fixture: "failSetUpRemoteFixture",
Timeout: time.Minute,
Func: func(ctx context.Context, s *testing.State) {
s.Log("Unexpected log")
},
}
localFailTearDownRemoteFixture := &testing.TestInstance{
Name: "local.FailTearDownRemoteFixture",
Fixture: "failTearDownRemoteFixture",
Timeout: time.Minute,
Func: func(ctx context.Context, s *testing.State) {
s.Log("Test run")
},
}
localReg := testing.NewRegistry("bundle")
localReg.AddTestInstance(localNoSuchFixture)
localReg.AddTestInstance(localFailSetUpRemoteFixture)
localReg.AddTestInstance(localFailTearDownRemoteFixture)
failSetUpRemoteFixture := &testing.FixtureInstance{
Name: "failSetUpRemoteFixture",
Impl: testfixture.New(testfixture.WithSetUp(func(ctx context.Context, s *testing.FixtState) interface{} {
s.Error("SetUp fail")
return nil
})),
}
failTearDownRemoteFixture := &testing.FixtureInstance{
Name: "failTearDownRemoteFixture",
Impl: testfixture.New(testfixture.WithTearDown(func(ctx context.Context, s *testing.FixtState) {
s.Error("TearDown fail")
})),
}
remoteReg := testing.NewRegistry("bundle")
remoteReg.AddFixtureInstance(failSetUpRemoteFixture)
remoteReg.AddFixtureInstance(failTearDownRemoteFixture)
env := setUpWithDefaultDUT(t,
bundletest.WithLocalBundles(localReg),
bundletest.WithRemoteBundles(remoteReg),
)
cl := protocol.NewTestServiceClient(env.DialRemoteBundle(context.Background(), t, remoteReg.Name()))
events, err := protocoltest.RunTestsRecursiveForEvents(context.Background(), cl, env.RunConfig(), protocoltest.WithEntityLogs())
if err != nil {
t.Fatal(err)
}
wantEvents := []protocol.Event{
// Orphan tests are reported first.
&protocol.EntityStartEvent{Entity: localNoSuchFixture.EntityProto()},
&protocol.EntityErrorEvent{EntityName: localNoSuchFixture.Name, Error: &protocol.Error{Reason: `Fixture "noSuchFixture" not found`}},
&protocol.EntityEndEvent{EntityName: localNoSuchFixture.Name},
&protocol.EntityStartEvent{Entity: failSetUpRemoteFixture.EntityProto()},
&protocol.EntityErrorEvent{EntityName: failSetUpRemoteFixture.Name, Error: &protocol.Error{Reason: "SetUp fail"}},
&protocol.EntityEndEvent{EntityName: failSetUpRemoteFixture.Name},
&protocol.EntityStartEvent{Entity: localFailSetUpRemoteFixture.EntityProto()},
&protocol.EntityErrorEvent{EntityName: localFailSetUpRemoteFixture.Name, Error: &protocol.Error{Reason: "[Fixture failure] failSetUpRemoteFixture: SetUp fail"}},
&protocol.EntityEndEvent{EntityName: localFailSetUpRemoteFixture.Name},
&protocol.EntityStartEvent{Entity: failTearDownRemoteFixture.EntityProto()},
&protocol.EntityStartEvent{Entity: localFailTearDownRemoteFixture.EntityProto()},
&protocol.EntityLogEvent{EntityName: localFailTearDownRemoteFixture.Name, Text: "Test run"},
&protocol.EntityEndEvent{EntityName: localFailTearDownRemoteFixture.Name},
&protocol.EntityErrorEvent{EntityName: failTearDownRemoteFixture.Name, Error: &protocol.Error{Reason: "TearDown fail"}},
&protocol.EntityEndEvent{EntityName: failTearDownRemoteFixture.Name},
}
if diff := cmp.Diff(events, wantEvents, protocoltest.EventCmpOpts...); diff != "" {
t.Errorf("Events mismatch (-got +want):\n%s", diff)
}
}
func TestTestServiceRunTests_ExternalPreTest(t *gotesting.T) {
localTest := &testing.TestInstance{
Name: "local.Foo",
Fixture: "fixture",
Timeout: time.Hour,
Func: func(ctx context.Context, s *testing.State) {
s.Error("Test error")
},
}
localReg := testing.NewRegistry("bundle")
localReg.AddTestInstance(localTest)
var wg sync.WaitGroup
wg.Add(2)
var env *bundletest.Env
fixture := &testing.FixtureInstance{
Name: "fixture",
PreTestTimeout: time.Hour,
PostTestTimeout: time.Hour,
TearDownTimeout: time.Hour,
Impl: testfixture.New(
testfixture.WithPreTest(func(ctx context.Context, s *testing.FixtTestState) {
if s.HasError() {
t.Error("t.HasError() = true, want false")
}
if got, want := s.DUT().HostName(), env.PrimaryServer(); got != want {
t.Errorf("DUT host name = %v, want %v", got, want)
}
if s.CloudStorage() == nil {
t.Error("s.CloudStorage() = nil, want non nil")
}
if s.RPCHint() == nil {
t.Error("s.RPCHint() = nil, want non nil")
}
if err := s.TestContext().Err(); err != nil {
t.Errorf("s.TestContext() is canceled: %v", err)
}
go func() {
<-s.TestContext().Done()
wg.Done()
}()
}),
testfixture.WithPostTest(func(ctx context.Context, s *testing.FixtTestState) {
if !s.HasError() {
t.Error("t.HasError() = false, want true")
}
if got, want := s.DUT().HostName(), env.PrimaryServer(); got != want {
t.Errorf("DUT host name = %v, want %v", got, want)
}
if s.CloudStorage() == nil {
t.Error("s.CloudStorage() = nil, want non nil")
}
if s.RPCHint() == nil {
t.Error("s.RPCHint() = nil, want non nil")
}
if err := s.TestContext().Err(); err != nil {
t.Errorf("s.TestContext() is canceled: %v", err)
}
go func() {
<-s.TestContext().Done()
wg.Done()
}()
}),
testfixture.WithTearDown(func(ctx context.Context, s *testing.FixtState) {
// TestContext must be cancelled as soon as PostTest finishes.
wg.Wait()
}),
),
}
remoteReg := testing.NewRegistry("bundle")
remoteReg.AddFixtureInstance(fixture)
env = setUpWithDefaultDUT(t, bundletest.WithLocalBundles(localReg), bundletest.WithRemoteBundles(remoteReg))
cl := protocol.NewTestServiceClient(env.DialRemoteBundle(context.Background(), t, remoteReg.Name()))
events, err := protocoltest.RunTestsRecursiveForEvents(context.Background(), cl, env.RunConfig(), protocoltest.WithEntityLogs())
if err != nil {
t.Fatal(err)
}
wg.Wait()
wantEvents := []protocol.Event{
&protocol.EntityStartEvent{Entity: fixture.EntityProto()},
&protocol.EntityStartEvent{Entity: localTest.EntityProto()},
&protocol.EntityErrorEvent{EntityName: localTest.Name, Error: &protocol.Error{Reason: "Test error"}},
&protocol.EntityEndEvent{EntityName: localTest.Name},
&protocol.EntityEndEvent{EntityName: fixture.Name},
}
if diff := cmp.Diff(events, wantEvents, protocoltest.EventCmpOpts...); diff != "" {
t.Errorf("Events mismatch (-got +want):\n%s", diff)
}
}
func TestTestServiceRunTests_ExternalResetFailure(t *gotesting.T) {
ctx := context.Background()
localTest := &testing.TestInstance{
Name: "local.Foo",
Fixture: "fixture",
Timeout: time.Hour,
Func: func(ctx context.Context, s *testing.State) {},
}
localTest2 := &testing.TestInstance{
Name: "local.Foo2",
Fixture: "fixture",
Timeout: time.Hour,
Func: func(ctx context.Context, s *testing.State) {},
}
localReg := testing.NewRegistry("bundle")
localReg.AddTestInstance(localTest)
localReg.AddTestInstance(localTest2)
var env *bundletest.Env
fixture := &testing.FixtureInstance{
Name: "fixture",
Impl: testfixture.New(
testfixture.WithReset(func(ctx context.Context) error {
return fmt.Errorf("Reset failed")
}),
),
}
remoteReg := testing.NewRegistry("bundle")
remoteReg.AddFixtureInstance(fixture)
env = setUpWithDefaultDUT(t, bundletest.WithLocalBundles(localReg), bundletest.WithRemoteBundles(remoteReg))
cl := protocol.NewTestServiceClient(env.DialRemoteBundle(ctx, t, remoteReg.Name()))
events, err := protocoltest.RunTestsRecursiveForEvents(ctx, cl, env.RunConfig(), protocoltest.WithEntityLogs())
if err != nil {
t.Error(err)
}
wantEvents := []protocol.Event{
&protocol.EntityStartEvent{Entity: fixture.EntityProto()},
&protocol.EntityStartEvent{Entity: localTest.EntityProto()},
&protocol.EntityEndEvent{EntityName: localTest.Name},
&protocol.EntityLogEvent{EntityName: fixture.Name, Text: "Fixture failed to reset: Reset failed; recovering"},
&protocol.EntityEndEvent{EntityName: fixture.Name},
&protocol.EntityStartEvent{Entity: fixture.EntityProto()},
&protocol.EntityStartEvent{Entity: localTest2.EntityProto()},
&protocol.EntityEndEvent{EntityName: localTest2.Name},
&protocol.EntityEndEvent{EntityName: fixture.Name},
}
if len(events) != len(wantEvents) {
t.Errorf("Got len %v, want %v", len(events), len(wantEvents))
}
if diff := cmp.Diff(events, wantEvents, protocoltest.EventCmpOpts...); diff != "" {
t.Errorf("Events mismatch (-got +want):\n%s", diff)
}
}
func TestTestServiceRunTests_ExternalResetSetUpFailure(t *gotesting.T) {
ctx := context.Background()
localTest := &testing.TestInstance{
Name: "local.Foo",
Fixture: "fixture",
Timeout: time.Hour,
Func: func(ctx context.Context, s *testing.State) {},
}
localTest2 := &testing.TestInstance{
Name: "local.Foo2",
Fixture: "fixture",
Timeout: time.Hour,
Func: func(ctx context.Context, s *testing.State) {},
}
localTest3 := &testing.TestInstance{
Name: "local.Foo3",
Fixture: "fixture",
Timeout: time.Hour,
Func: func(ctx context.Context, s *testing.State) {},
}
localReg := testing.NewRegistry("bundle")
localReg.AddTestInstance(localTest)
localReg.AddTestInstance(localTest2)
localReg.AddTestInstance(localTest3)
var setUpCount = 0
var env *bundletest.Env
fixture := &testing.FixtureInstance{
Name: "fixture",
Impl: testfixture.New(
testfixture.WithSetUp(func(ctx context.Context, s *testing.FixtState) interface{} {
setUpCount++
s.Log("SetUp called")
if setUpCount > 1 {
s.Error("SetUp failed")
}
return nil
}),
testfixture.WithReset(func(ctx context.Context) error {
return fmt.Errorf("Reset failed")
}),
),
}
remoteReg := testing.NewRegistry("bundle")
remoteReg.AddFixtureInstance(fixture)
env = setUpWithDefaultDUT(t, bundletest.WithLocalBundles(localReg), bundletest.WithRemoteBundles(remoteReg))
cl := protocol.NewTestServiceClient(env.DialRemoteBundle(ctx, t, remoteReg.Name()))
events, err := protocoltest.RunTestsRecursiveForEvents(ctx, cl, env.RunConfig(), protocoltest.WithEntityLogs())
if err != nil {
t.Error(err)
}
wantEvents := []protocol.Event{
&protocol.EntityStartEvent{Entity: fixture.EntityProto()},
&protocol.EntityLogEvent{EntityName: fixture.Name, Text: "SetUp called"},
&protocol.EntityStartEvent{Entity: localTest.EntityProto()},
&protocol.EntityEndEvent{EntityName: localTest.Name},
&protocol.EntityLogEvent{EntityName: fixture.Name, Text: "Fixture failed to reset: Reset failed; recovering"},
&protocol.EntityEndEvent{EntityName: fixture.Name},
&protocol.EntityStartEvent{Entity: fixture.EntityProto()},
&protocol.EntityLogEvent{EntityName: fixture.Name, Text: "SetUp called"},
&protocol.EntityErrorEvent{EntityName: fixture.Name, Error: &protocol.Error{Reason: "SetUp failed"}},
&protocol.EntityEndEvent{EntityName: fixture.Name},
&protocol.EntityStartEvent{Entity: localTest2.EntityProto()},
&protocol.EntityErrorEvent{EntityName: localTest2.Name, Error: &protocol.Error{Reason: "[Fixture failure] fixture: SetUp failed"}},
&protocol.EntityEndEvent{EntityName: localTest2.Name},
&protocol.EntityStartEvent{Entity: localTest3.EntityProto()},
&protocol.EntityErrorEvent{EntityName: localTest3.Name, Error: &protocol.Error{Reason: "[Fixture failure] fixture: SetUp failed"}},
&protocol.EntityEndEvent{EntityName: localTest3.Name},
}
if diff := cmp.Diff(events, wantEvents, protocoltest.EventCmpOpts...); diff != "" {
t.Errorf("Events mismatch (-got +want):\n%s", diff)
}
}