blob: 104cf13f5453f71ef399b403f372eadf790af69c [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package commands_test
import (
"context"
"infra/cros/cmd/common_lib/common"
"infra/cros/cmd/common_lib/containers"
"infra/cros/cmd/common_lib/tools/crostoolrunner"
"infra/cros/cmd/cros_test_runner/data"
"infra/cros/cmd/cros_test_runner/internal/commands"
"infra/cros/cmd/cros_test_runner/internal/executors"
"testing"
"time"
"github.com/golang/protobuf/ptypes/duration"
. "github.com/smartystreets/goconvey/convey"
_go "go.chromium.org/chromiumos/config/go"
configpb "go.chromium.org/chromiumos/config/go"
buildapi "go.chromium.org/chromiumos/config/go/build/api"
"go.chromium.org/chromiumos/config/go/test/api"
testapi "go.chromium.org/chromiumos/config/go/test/api"
"go.chromium.org/chromiumos/config/go/test/api/metadata"
"go.chromium.org/chromiumos/config/go/test/artifact"
artifactpb "go.chromium.org/chromiumos/config/go/test/artifact"
labapi "go.chromium.org/chromiumos/config/go/test/lab/api"
"go.chromium.org/chromiumos/infra/proto/go/test_platform/skylab_test_runner"
bbpb "go.chromium.org/luci/buildbucket/proto"
buildbucketpb "go.chromium.org/luci/buildbucket/proto"
. "go.chromium.org/luci/common/testing/assertions"
"go.chromium.org/luci/luciexe/build"
"google.golang.org/protobuf/types/known/timestamppb"
)
func parseTime(s string) time.Time {
t, _ := time.Parse("2006-01-02T15:04:05.99Z", s)
return t
}
func TestRdbPublishPublishCmd_UnsupportedSK(t *testing.T) {
t.Parallel()
Convey("Unsupported state keeper", t, func() {
ctx := context.Background()
sk := &UnsupportedStateKeeper{}
ctrCipd := crostoolrunner.CtrCipdInfo{Version: "prod"}
ctr := &crostoolrunner.CrosToolRunner{CtrCipdInfo: ctrCipd}
cont := containers.NewCrosPublishTemplatedContainer(
containers.CrosRdbPublishTemplatedContainerType,
"container/image/path",
ctr)
exec := executors.NewCrosPublishExecutor(
cont,
executors.CrosRdbPublishExecutorType)
cmd := commands.NewRdbPublishUploadCmd(exec)
err := cmd.ExtractDependencies(ctx, sk)
So(err, ShouldNotBeNil)
})
}
func TestRdbPublishPublishCmd_MissingDeps(t *testing.T) {
t.Parallel()
Convey("Cmd missing deps", t, func() {
ctx := context.Background()
sk := &data.HwTestStateKeeper{}
ctrCipd := crostoolrunner.CtrCipdInfo{Version: "prod"}
ctr := &crostoolrunner.CrosToolRunner{CtrCipdInfo: ctrCipd}
cont := containers.NewCrosPublishTemplatedContainer(
containers.CrosRdbPublishTemplatedContainerType,
"container/image/path",
ctr)
exec := executors.NewCrosPublishExecutor(
cont,
executors.CrosRdbPublishExecutorType)
cmd := commands.NewRdbPublishUploadCmd(exec)
err := cmd.ExtractDependencies(ctx, sk)
So(err, ShouldNotBeNil)
})
}
func TestRdbPublishPublishCmd_UpdateSK(t *testing.T) {
t.Parallel()
Convey("Cmd with no updates", t, func() {
ctx := context.Background()
sk := &data.HwTestStateKeeper{CftTestRequest: nil}
ctrCipd := crostoolrunner.CtrCipdInfo{Version: "prod"}
ctr := &crostoolrunner.CrosToolRunner{CtrCipdInfo: ctrCipd}
cont := containers.NewCrosPublishTemplatedContainer(
containers.CrosRdbPublishTemplatedContainerType,
"container/image/path",
ctr)
exec := executors.NewCrosPublishExecutor(
cont,
executors.CrosRdbPublishExecutorType)
cmd := commands.NewRdbPublishUploadCmd(exec)
err := cmd.UpdateStateKeeper(ctx, sk)
So(err, ShouldBeNil)
})
}
func TestRdbPublishPublishCmd_ExtractSources(t *testing.T) {
t.Parallel()
Convey("With CFT Test Request", t, func() {
request := &skylab_test_runner.CFTTestRequest{
PrimaryDut: &skylab_test_runner.CFTTestRequest_Device{
ProvisionState: &api.ProvisionState{
SystemImage: &api.ProvisionState_SystemImage{
SystemImagePath: &_go.StoragePath{
HostType: _go.StoragePath_GS,
Path: "gs://some-bucket/builder/build-12345",
},
},
},
},
}
expectedSources := &metadata.PublishRdbMetadata_Sources{
GsPath: "gs://some-bucket/builder/build-12345/metadata/sources.jsonpb",
IsDeploymentDirty: false,
}
Convey("Base case", func() {
sources, err := commands.SourcesFromCFTTestRequest(request)
So(err, ShouldBeNil)
So(sources, ShouldResembleProto, expectedSources)
})
Convey("Invalid input", func() {
Convey("No gs:// prefix", func() {
request.PrimaryDut.ProvisionState.SystemImage.SystemImagePath.Path = "/a/b/c"
_, err := commands.SourcesFromCFTTestRequest(request)
So(err, ShouldErrLike, "system_image_path.path: must start with gs://")
})
Convey("Trailing slash", func() {
request.PrimaryDut.ProvisionState.SystemImage.SystemImagePath.Path = "gs://some-bucket/builder/build-12345/"
_, err := commands.SourcesFromCFTTestRequest(request)
So(err, ShouldErrLike, "system_image_path.path: must not have trailing '/'")
})
})
Convey("Local testing", func() {
request.PrimaryDut.ProvisionState.SystemImage.SystemImagePath = &_go.StoragePath{
HostType: _go.StoragePath_LOCAL,
Path: "/builds/build-12345",
}
sources, err := commands.SourcesFromCFTTestRequest(request)
So(err, ShouldBeNil)
So(sources, ShouldBeNil)
})
Convey("Lacros testing", func() {
request.PrimaryDut.ProvisionState.Packages = []*api.ProvisionState_Package{
{
PortagePackage: &buildapi.Portage_Package{},
},
}
expectedSources.IsDeploymentDirty = true
sources, err := commands.SourcesFromCFTTestRequest(request)
So(err, ShouldBeNil)
So(sources, ShouldResembleProto, expectedSources)
})
Convey("Firmware testing", func() {
request.PrimaryDut.ProvisionState.Firmware = &buildapi.FirmwareConfig{
MainRoPayload: &buildapi.FirmwarePayload{},
}
expectedSources.IsDeploymentDirty = true
sources, err := commands.SourcesFromCFTTestRequest(request)
So(err, ShouldBeNil)
So(sources, ShouldResembleProto, expectedSources)
})
})
}
func TestRdbPublishPublishCmd_ExtractDepsSuccess(t *testing.T) {
t.Parallel()
// Common setup
ctrCipd := crostoolrunner.CtrCipdInfo{Version: "prod"}
ctr := &crostoolrunner.CrosToolRunner{CtrCipdInfo: ctrCipd}
cont := containers.NewCrosPublishTemplatedContainer(
containers.CrosRdbPublishTemplatedContainerType,
"container/image/path",
ctr)
exec := executors.NewCrosPublishExecutor(
cont,
executors.CrosRdbPublishExecutorType)
cmd := commands.NewRdbPublishUploadCmd(exec)
Convey("Populate TestResultForRdb with full info", t, func() {
ctx := context.Background()
createTime := timestamppb.New(parseTime("2022-09-07T18:53:33.983328614Z"))
startedTime := timestamppb.New(parseTime("2022-09-07T20:53:33.983328614Z"))
duration := &duration.Duration{Seconds: 60}
primaryDUT := &labapi.Dut{
Id: &labapi.Dut_Id{Value: "0wgtfqin2033834d-ecghcra"},
DutType: &labapi.Dut_Chromeos{
Chromeos: &labapi.Dut_ChromeOS{
Name: "0wgtfqin2033834d-ecghcra",
DutModel: &labapi.DutModel{
BuildTarget: "hatch",
ModelName: "nipperkin",
},
Chameleon: &labapi.Chameleon{
Types: []labapi.Chameleon_Type{
labapi.Chameleon_V2,
labapi.Chameleon_V3,
},
},
ModemInfo: &labapi.ModemInfo{
Type: labapi.ModemType_MODEM_TYPE_FIBOCOMM_L850GL,
},
},
},
}
wantTestResult := &artifactpb.TestResult{
TestInvocation: &artifactpb.TestInvocation{
PrimaryExecutionInfo: &artifactpb.ExecutionInfo{
BuildInfo: &artifactpb.BuildInfo{
Name: "hatch-cq/R106-15048.0.0",
Board: "hatch",
BuildTarget: "hatch",
BuildMetadata: &artifactpb.BuildMetadata{
Sku: &artifactpb.BuildMetadata_Sku{
HwidSku: "CRAASK-HULX D4B-F4E-F3F-B2K-L3I-Q6I",
DlmSkuId: "16968",
},
Chipset: &artifactpb.BuildMetadata_Chipset{
WifiChip: "INTEL_GFP2_AX211",
WifiRouterModels: "gale",
},
Cellular: &artifactpb.BuildMetadata_Cellular{
Carrier: "CARRIER_ESIM",
},
Firmware: &artifactpb.BuildMetadata_Firmware{},
Kernel: &artifactpb.BuildMetadata_Kernel{},
Lacros: &artifactpb.BuildMetadata_Lacros{},
Chameleon: &labapi.Chameleon{
Types: []labapi.Chameleon_Type{
labapi.Chameleon_V2,
labapi.Chameleon_V3,
},
},
ModemInfo: &labapi.ModemInfo{
Type: labapi.ModemType_MODEM_TYPE_FIBOCOMM_L850GL,
},
},
},
DutInfo: &artifactpb.DutInfo{
Dut: primaryDUT,
ProvisionState: &testapi.ProvisionState{
SystemImage: &testapi.ProvisionState_SystemImage{
SystemImagePath: &_go.StoragePath{
HostType: _go.StoragePath_GS,
Path: "gs://some-bucket/builder/build-12345",
},
},
},
},
EnvInfo: &artifactpb.ExecutionInfo_SkylabInfo{
SkylabInfo: &artifactpb.SkylabInfo{
DroneInfo: &artifactpb.DroneInfo{
Drone: "skylab-drone-deployment-prod-6dc79d4f9-czjlj",
DroneServer: "chromeos4-row4-rack1-drone8",
},
BuildbucketInfo: &artifactpb.BuildbucketInfo{
Id: 100,
AncestorIds: []int64{98, 99},
Builder: &artifactpb.BuilderID{
Project: "chromeos",
Bucket: "test_runner",
Builder: "test_runner-dev",
},
},
SwarmingInfo: &artifactpb.SwarmingInfo{
TaskId: "taskId0",
SuiteTaskId: "parentId0",
TaskName: "bb-100-chromeos/test_runner/test_runner-dev",
Pool: "ChromeOSSkylab",
LabelPool: "DUT_POOL_QUOTA",
},
},
},
InventoryInfo: &artifactpb.InventoryInfo{
UfsZone: "ZONE_SFO36_OS",
},
},
DutTopology: &labapi.DutTopology{
Id: &labapi.DutTopology_Id{Value: "0wgtfqin2033834d-ecghcra"},
Duts: []*labapi.Dut{primaryDUT},
},
SchedulingMetadata: &artifactpb.SchedulingMetadata{
SchedulingArgs: map[string]string{
"display_name": "hatch-cq/R102-14632.0.0-62834-8818718496810023809/wificell-cq/tast.wificell-cq",
"analytics_name": "Bluetooth_Sa_Perbuild",
"ctp-fwd-task-name": "Bluetooth_Sa_Perbuild",
"qs_account": "unmanaged_p2",
"parent_task_id": "parentId1",
"branch-trigger": "DEV",
"bug_id": "1234",
},
},
ProjectTrackerMetadata: &artifactpb.ProjectTrackerMetadata{
BugId: "1234",
},
},
TestRuns: []*artifactpb.TestRun{
{
TestCaseInfo: &artifactpb.TestCaseInfo{
TestCaseResult: &testapi.TestCaseResult{
TestHarness: &testapi.TestHarness{
TestHarnessType: &testapi.TestHarness_Tast_{
Tast: &testapi.TestHarness_Tast{},
},
},
TestCaseId: &testapi.TestCase_Id{
Value: "tast.rlz_CheckPing",
},
Verdict: &testapi.TestCaseResult_Pass_{},
StartTime: startedTime,
Duration: duration,
},
DisplayName: "hatch-cq/R102-14632.0.0-62834-8818718496810023809/wificell-cq/tast.wificell-cq",
Suite: "arc-cts-vm",
Branch: "main",
MainBuilderName: "main-release",
Channel: "DEV",
},
LogsInfo: []*configpb.StoragePath{
{
HostType: configpb.StoragePath_GS,
Path: "gs://some-bucket/builder/build-12345",
},
},
TestHarness: &testapi.TestHarness{
TestHarnessType: &testapi.TestHarness_Tast_{
Tast: &testapi.TestHarness_Tast{},
},
},
TimeInfo: &artifactpb.TimingInfo{
QueuedTime: createTime,
StartedTime: startedTime,
Duration: duration,
},
},
},
}
// Sets up the build info.
buildPb := &bbpb.Build{
Id: 100,
Status: bbpb.Status_SUCCESS,
Builder: &bbpb.BuilderID{
Project: "chromeos",
Bucket: "test_runner",
Builder: "test_runner-dev",
},
AncestorIds: []int64{98, 99},
Tags: []*buildbucketpb.StringPair{
{Key: "display_name", Value: "hatch-cq/R102-14632.0.0-62834-8818718496810023809/wificell-cq/tast.wificell-cq"},
{Key: "analytics_name", Value: "Bluetooth_Sa_Perbuild"},
{Key: "ctp-fwd-task-name", Value: "Bluetooth_Sa_Perbuild"},
{Key: "qs_account", Value: "unmanaged_p2"},
{Key: "parent_task_id", Value: "parentId1"},
{Key: "branch-trigger", Value: "DEV"},
{Key: "bug_id", Value: "1234"},
},
CreateTime: createTime,
Infra: &bbpb.BuildInfra{Swarming: &bbpb.BuildInfra_Swarming{
TaskId: "taskId1",
ParentRunId: "parentId1",
BotDimensions: []*buildbucketpb.StringPair{
{Key: "label-wifi_chip", Value: "INTEL_GFP2_AX211"},
{Key: "label-wifi_router_models", Value: "gale"},
{Key: "label-hwid_sku", Value: "CRAASK-HULX D4B-F4E-F3F-B2K-L3I-Q6I"},
{Key: "label-dlm_sku_id", Value: "16968"},
{Key: "label-carrier", Value: "CARRIER_ESIM"},
{Key: "drone", Value: "skylab-drone-deployment-prod-6dc79d4f9-czjlj"},
{Key: "drone_server", Value: "chromeos4-row4-rack1-drone8"},
{Key: "pool", Value: "ChromeOSSkylab"},
{Key: "label-pool", Value: "DUT_POOL_QUOTA"},
{Key: "ufs_zone", Value: "ZONE_SFO36_OS"},
},
}},
}
buildState, ctx, err := build.Start(ctx, buildPb)
defer func() { buildState.End(err) }()
sk := &data.HwTestStateKeeper{
CurrentInvocationId: "Inv-1234",
TesthausURL: "www.testhaus.com",
BaseVariant: map[string]string{
"board": "asurada",
"model": "hayato",
"build_target": "asurada",
},
CftTestRequest: &skylab_test_runner.CFTTestRequest{
PrimaryDut: &skylab_test_runner.CFTTestRequest_Device{
DutModel: &labapi.DutModel{
BuildTarget: primaryDUT.GetChromeos().GetDutModel().GetBuildTarget(),
},
ProvisionState: &api.ProvisionState{
SystemImage: &api.ProvisionState_SystemImage{
SystemImagePath: &_go.StoragePath{
HostType: _go.StoragePath_GS,
Path: "gs://some-bucket/builder/build-12345",
},
},
},
},
AutotestKeyvals: map[string]string{
"build_target": "hatch",
"build": "hatch-cq/R106-15048.0.0",
"suite": "arc-cts-vm",
"branch": "main",
"master_build_config": "main-release",
},
},
Devices: map[string]*testapi.CrosTestRequest_Device{
common.Primary: {
Dut: primaryDUT,
},
},
GcsURL: "gs://some-bucket/builder/build-12345",
BuildState: buildState,
DutTopology: &labapi.DutTopology{
Id: &labapi.DutTopology_Id{Value: "0wgtfqin2033834d-ecghcra"},
Duts: []*labapi.Dut{primaryDUT},
},
TestResponses: &testapi.CrosTestResponse{
TestCaseResults: []*testapi.TestCaseResult{
{
TestHarness: &testapi.TestHarness{
TestHarnessType: &testapi.TestHarness_Tast_{
Tast: &testapi.TestHarness_Tast{},
},
},
TestCaseId: &testapi.TestCase_Id{
Value: "tast.rlz_CheckPing",
},
Verdict: &testapi.TestCaseResult_Pass_{},
StartTime: startedTime,
Duration: duration,
},
},
},
}
// Extract deps first
err = cmd.ExtractDependencies(ctx, sk)
So(err, ShouldBeNil)
So(sk.TestResultForRdb, ShouldResembleProto, wantTestResult)
})
Convey("ProvisionStartCmd extract deps with TestResultForRdb", t, func() {
ctx := context.Background()
wantInvId := "Inv-1234"
wantTesthausURL := "www.testhaus.com"
wantBaseVariant := map[string]string{
"board": "asurada",
"model": "hayato",
"build_target": "asurada",
}
sk := &data.HwTestStateKeeper{
CurrentInvocationId: wantInvId,
TesthausURL: wantTesthausURL,
CftTestRequest: &skylab_test_runner.CFTTestRequest{
PrimaryDut: &skylab_test_runner.CFTTestRequest_Device{
ProvisionState: &api.ProvisionState{
SystemImage: &api.ProvisionState_SystemImage{
SystemImagePath: &_go.StoragePath{
HostType: _go.StoragePath_GS,
Path: "gs://some-bucket/builder/build-12345",
},
},
},
},
},
BaseVariant: wantBaseVariant,
TestResultForRdb: &artifact.TestResult{Version: 1234},
}
// Extract deps first
err := cmd.ExtractDependencies(ctx, sk)
So(err, ShouldBeNil)
So(cmd.CurrentInvocationId, ShouldEqual, wantInvId)
So(cmd.TesthausURL, ShouldEqual, wantTesthausURL)
So(cmd.Sources, ShouldResembleProto, &metadata.PublishRdbMetadata_Sources{
GsPath: "gs://some-bucket/builder/build-12345/metadata/sources.jsonpb",
IsDeploymentDirty: false,
})
So(cmd.BaseVariant, ShouldEqual, wantBaseVariant)
})
Convey("ProvisionStartCmd extract deps without TestResultForRdb", t, func() {
ctx := context.Background()
wantInvId := "Inv-1234"
wantTesthausURL := "www.testhaus.com"
wantBaseVariant := map[string]string{
"board": "asurada",
"model": "hayato",
"build_target": "asurada",
}
sk := &data.HwTestStateKeeper{
CurrentInvocationId: wantInvId,
TesthausURL: wantTesthausURL,
BaseVariant: wantBaseVariant,
CftTestRequest: &skylab_test_runner.CFTTestRequest{
PrimaryDut: &skylab_test_runner.CFTTestRequest_Device{
ProvisionState: &api.ProvisionState{},
},
},
}
// Extract deps first
err := cmd.ExtractDependencies(ctx, sk)
So(cmd.CurrentInvocationId, ShouldEqual, wantInvId)
So(cmd.TesthausURL, ShouldEqual, wantTesthausURL)
So(cmd.BaseVariant, ShouldEqual, wantBaseVariant)
So(err, ShouldErrLike, "missing dependency: BuildState")
})
}