blob: dbdbe860959b46b338b2b98efd9ee5fbc3f2abe8 [file] [log] [blame]
// Copyright 2019 The Chromium 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 generator
import (
_struct "github.com/golang/protobuf/ptypes/struct"
"github.com/golang/protobuf/ptypes/wrappers"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"go.chromium.org/chromiumos/infra/proto/go/chromiumos"
"go.chromium.org/chromiumos/infra/proto/go/testplans"
bbproto "go.chromium.org/luci/buildbucket/proto"
"testing"
"testplans/internal/git"
)
const (
GS_BUCKET = "gs://chromeos-image-archive"
GS_PATH_PREFIX = "gs/path/"
)
func makeBuildbucketBuild(buildTarget string, status bbproto.Status, changes []*bbproto.GerritChange, critical bool) *bbproto.Build {
var criticalVal bbproto.Trinary
if critical {
criticalVal = bbproto.Trinary_YES
} else {
criticalVal = bbproto.Trinary_NO
}
b := &bbproto.Build{
Critical: criticalVal,
Input: &bbproto.Build_Input{},
Output: &bbproto.Build_Output{
Properties: &_struct.Struct{
Fields: map[string]*_struct.Value{
"build_target": {
Kind: &_struct.Value_StructValue{StructValue: &_struct.Struct{
Fields: map[string]*_struct.Value{
"name": {Kind: &_struct.Value_StringValue{StringValue: buildTarget}},
},
}},
},
"artifacts": {
Kind: &_struct.Value_StructValue{StructValue: &_struct.Struct{
Fields: map[string]*_struct.Value{
"gs_bucket": {Kind: &_struct.Value_StringValue{StringValue: GS_BUCKET}},
"gs_path": {Kind: &_struct.Value_StringValue{StringValue: GS_PATH_PREFIX + buildTarget}},
},
}},
},
},
},
},
Status: status,
}
for _, c := range changes {
b.Input.GerritChanges = append(b.Input.GerritChanges, c)
}
return b
}
func TestCreateCombinedTestPlan_success(t *testing.T) {
reefGceTestCfg := &testplans.GceTestCfg{GceTest: []*testplans.GceTestCfg_GceTest{
{TestType: "GCE reef", Common: &testplans.TestSuiteCommon{Critical: &wrappers.BoolValue{Value: true}}},
}}
reefMoblabVmTestCfg := &testplans.MoblabVmTestCfg{MoblabTest: []*testplans.MoblabVmTestCfg_MoblabTest{
{TestType: "Moblab reef", Common: &testplans.TestSuiteCommon{Critical: &wrappers.BoolValue{Value: true}}},
}}
kevinHWTestCfg := &testplans.HwTestCfg{HwTest: []*testplans.HwTestCfg_HwTest{
{
Suite: "HW kevin",
SkylabBoard: "kev",
},
}}
kevinTastVMTestCfg := &testplans.TastVmTestCfg{TastVmTest: []*testplans.TastVmTestCfg_TastVmTest{
{SuiteName: "Tast kevin"},
}}
kevinVMTestCfg := &testplans.VmTestCfg{VmTest: []*testplans.VmTestCfg_VmTest{
{TestType: "VM kevin"},
}}
testReqs := &testplans.TargetTestRequirementsCfg{
PerTargetTestRequirements: []*testplans.PerTargetTestRequirements{
{TargetCriteria: &testplans.TargetCriteria{
TargetType: &testplans.TargetCriteria_BuildTarget{BuildTarget: "reef"}},
GceTestCfg: reefGceTestCfg,
MoblabVmTestCfg: reefMoblabVmTestCfg},
{TargetCriteria: &testplans.TargetCriteria{
TargetType: &testplans.TargetCriteria_BuildTarget{BuildTarget: "kevin"}},
HwTestCfg: kevinHWTestCfg,
TastVmTestCfg: kevinTastVMTestCfg,
VmTestCfg: kevinVMTestCfg},
},
}
sourceTreeTestCfg := &testplans.SourceTreeTestCfg{
SourceTreeTestRestriction: []*testplans.SourceTreeTestRestriction{
{SourceTree: &testplans.SourceTree{Path: "hw/tests/not/needed/here"},
TestRestriction: &testplans.TestRestriction{DisableHwTests: true}}}}
bbBuilds := []*bbproto.Build{
makeBuildbucketBuild("kevin", bbproto.Status_SUCCESS, []*bbproto.GerritChange{
{Host: "test-review.googlesource.com", Change: 123, Patchset: 2},
}, true),
makeBuildbucketBuild("reef", bbproto.Status_SUCCESS, []*bbproto.GerritChange{
{Host: "test-review.googlesource.com", Change: 123, Patchset: 2},
}, true),
}
chRevData := git.GetChangeRevsForTest([]*git.ChangeRev{
{
ChangeRevKey: git.ChangeRevKey{
Host: "test-review.googlesource.com",
ChangeNum: 123,
Revision: 2,
},
Project: "chromiumos/repo/name",
Files: []string{"a/b/c"},
},
})
repoToSrcRoot := map[string]string{"chromiumos/repo/name": "src/to/file"}
actualTestPlan, err := CreateTestPlan(testReqs, sourceTreeTestCfg, bbBuilds, chRevData, repoToSrcRoot)
if err != nil {
t.Error(err)
}
expectedTestPlan := &testplans.GenerateTestPlanResponse{
TestUnit: []*testplans.TestUnit{
{
BuildTarget: &chromiumos.BuildTarget{Name: "reef"},
TestCfg: &testplans.TestUnit_GceTestCfg{GceTestCfg: reefGceTestCfg},
BuildPayload: &testplans.BuildPayload{
ArtifactsGsBucket: GS_BUCKET,
ArtifactsGsPath: GS_PATH_PREFIX + "reef",
}},
{
BuildTarget: &chromiumos.BuildTarget{Name: "reef"},
TestCfg: &testplans.TestUnit_MoblabVmTestCfg{MoblabVmTestCfg: reefMoblabVmTestCfg},
BuildPayload: &testplans.BuildPayload{
ArtifactsGsBucket: GS_BUCKET,
ArtifactsGsPath: GS_PATH_PREFIX + "reef",
}},
{
BuildTarget: &chromiumos.BuildTarget{Name: "kevin"},
TestCfg: &testplans.TestUnit_HwTestCfg{HwTestCfg: kevinHWTestCfg},
BuildPayload: &testplans.BuildPayload{
ArtifactsGsBucket: GS_BUCKET,
ArtifactsGsPath: GS_PATH_PREFIX + "kevin",
}},
{
BuildTarget: &chromiumos.BuildTarget{Name: "kevin"},
TestCfg: &testplans.TestUnit_TastVmTestCfg{TastVmTestCfg: kevinTastVMTestCfg},
BuildPayload: &testplans.BuildPayload{
ArtifactsGsBucket: GS_BUCKET,
ArtifactsGsPath: GS_PATH_PREFIX + "kevin",
}},
{
BuildTarget: &chromiumos.BuildTarget{Name: "kevin"},
TestCfg: &testplans.TestUnit_VmTestCfg{VmTestCfg: kevinVMTestCfg},
BuildPayload: &testplans.BuildPayload{
ArtifactsGsBucket: GS_BUCKET,
ArtifactsGsPath: GS_PATH_PREFIX + "kevin",
}},
},
GceTestUnits: []*testplans.GceTestUnit{
{Common: &testplans.TestUnitCommon{
BuildPayload: &testplans.BuildPayload{
ArtifactsGsBucket: GS_BUCKET,
ArtifactsGsPath: GS_PATH_PREFIX + "reef",
},
BuildTarget: &chromiumos.BuildTarget{Name: "reef"}},
GceTestCfg: reefGceTestCfg},
},
MoblabVmTestUnits: []*testplans.MoblabVmTestUnit{
{Common: &testplans.TestUnitCommon{
BuildPayload: &testplans.BuildPayload{
ArtifactsGsBucket: GS_BUCKET,
ArtifactsGsPath: GS_PATH_PREFIX + "reef",
},
BuildTarget: &chromiumos.BuildTarget{Name: "reef"}},
MoblabVmTestCfg: reefMoblabVmTestCfg},
},
HwTestUnits: []*testplans.HwTestUnit{
{Common: &testplans.TestUnitCommon{
BuildPayload: &testplans.BuildPayload{
ArtifactsGsBucket: GS_BUCKET,
ArtifactsGsPath: GS_PATH_PREFIX + "kevin",
},
BuildTarget: &chromiumos.BuildTarget{Name: "kevin"}},
HwTestCfg: kevinHWTestCfg},
},
TastVmTestUnits: []*testplans.TastVmTestUnit{
{Common: &testplans.TestUnitCommon{
BuildPayload: &testplans.BuildPayload{
ArtifactsGsBucket: GS_BUCKET,
ArtifactsGsPath: GS_PATH_PREFIX + "kevin",
},
BuildTarget: &chromiumos.BuildTarget{Name: "kevin"}},
TastVmTestCfg: kevinTastVMTestCfg},
},
VmTestUnits: []*testplans.VmTestUnit{
{Common: &testplans.TestUnitCommon{
BuildPayload: &testplans.BuildPayload{
ArtifactsGsBucket: GS_BUCKET,
ArtifactsGsPath: GS_PATH_PREFIX + "kevin",
},
BuildTarget: &chromiumos.BuildTarget{Name: "kevin"}},
VmTestCfg: kevinVMTestCfg},
}}
if diff := cmp.Diff(expectedTestPlan, actualTestPlan, cmpopts.EquateEmpty()); diff != "" {
t.Errorf("CreateCombinedTestPlan bad result (-want/+got)\n%s", diff)
}
}
func TestCreateCombinedTestPlan_successDespiteOneFailedBuilder(t *testing.T) {
// In this test, the kevin builder failed, so the output test plan will not contain a test unit
// for kevin.
reefGceTestCfg := &testplans.GceTestCfg{GceTest: []*testplans.GceTestCfg_GceTest{
{TestType: "GCE reef"},
}}
kevinVMTestCfg := &testplans.VmTestCfg{VmTest: []*testplans.VmTestCfg_VmTest{
{TestType: "VM kevin"},
}}
testReqs := &testplans.TargetTestRequirementsCfg{
PerTargetTestRequirements: []*testplans.PerTargetTestRequirements{
{TargetCriteria: &testplans.TargetCriteria{
TargetType: &testplans.TargetCriteria_BuildTarget{BuildTarget: "reef"}},
GceTestCfg: reefGceTestCfg},
{TargetCriteria: &testplans.TargetCriteria{
TargetType: &testplans.TargetCriteria_BuildTarget{BuildTarget: "kevin"}},
VmTestCfg: kevinVMTestCfg},
},
}
sourceTreeTestCfg := &testplans.SourceTreeTestCfg{
SourceTreeTestRestriction: []*testplans.SourceTreeTestRestriction{
{SourceTree: &testplans.SourceTree{Path: "hw/tests/not/needed/here"},
TestRestriction: &testplans.TestRestriction{DisableHwTests: true}}}}
bbBuilds := []*bbproto.Build{
makeBuildbucketBuild("kevin", bbproto.Status_FAILURE, []*bbproto.GerritChange{
{Host: "test-review.googlesource.com", Change: 123, Patchset: 2},
}, true),
makeBuildbucketBuild("reef", bbproto.Status_SUCCESS, []*bbproto.GerritChange{
{Host: "test-review.googlesource.com", Change: 123, Patchset: 2},
}, true),
}
chRevData := git.GetChangeRevsForTest([]*git.ChangeRev{
{
ChangeRevKey: git.ChangeRevKey{
Host: "test-review.googlesource.com",
ChangeNum: 123,
Revision: 2,
},
Project: "chromiumos/repo/name",
Files: []string{"a/b/c"},
},
})
repoToSrcRoot := map[string]string{"chromiumos/repo/name": "src/to/file"}
actualTestPlan, err := CreateTestPlan(testReqs, sourceTreeTestCfg, bbBuilds, chRevData, repoToSrcRoot)
if err != nil {
t.Error(err)
}
expectedTestPlan := &testplans.GenerateTestPlanResponse{
TestUnit: []*testplans.TestUnit{
{
BuildTarget: &chromiumos.BuildTarget{Name: "reef"},
TestCfg: &testplans.TestUnit_GceTestCfg{GceTestCfg: reefGceTestCfg},
BuildPayload: &testplans.BuildPayload{
ArtifactsGsBucket: GS_BUCKET,
ArtifactsGsPath: GS_PATH_PREFIX + "reef",
}},
},
GceTestUnits: []*testplans.GceTestUnit{
{Common: &testplans.TestUnitCommon{
BuildPayload: &testplans.BuildPayload{
ArtifactsGsBucket: GS_BUCKET,
ArtifactsGsPath: GS_PATH_PREFIX + "reef",
},
BuildTarget: &chromiumos.BuildTarget{Name: "reef"}},
GceTestCfg: reefGceTestCfg},
}}
if diff := cmp.Diff(expectedTestPlan, actualTestPlan, cmpopts.EquateEmpty()); diff != "" {
t.Errorf("CreateCombinedTestPlan bad result (-want/+got)\n%s", diff)
}
}
func TestCreateCombinedTestPlan_skipsUnnecessaryHardwareTest(t *testing.T) {
kevinHWTestCfg := &testplans.HwTestCfg{HwTest: []*testplans.HwTestCfg_HwTest{
{
Suite: "HW kevin",
SkylabBoard: "kev",
},
}}
testReqs := &testplans.TargetTestRequirementsCfg{
PerTargetTestRequirements: []*testplans.PerTargetTestRequirements{
{TargetCriteria: &testplans.TargetCriteria{
TargetType: &testplans.TargetCriteria_BuildTarget{BuildTarget: "kevin"}},
HwTestCfg: kevinHWTestCfg},
},
}
sourceTreeTestCfg := &testplans.SourceTreeTestCfg{
SourceTreeTestRestriction: []*testplans.SourceTreeTestRestriction{
{SourceTree: &testplans.SourceTree{Path: "no/hw/tests/here/some/file"},
TestRestriction: &testplans.TestRestriction{DisableHwTests: true}}}}
bbBuilds := []*bbproto.Build{
makeBuildbucketBuild("kevin", bbproto.Status_SUCCESS, []*bbproto.GerritChange{
{Host: "test-review.googlesource.com", Change: 123, Patchset: 2},
}, true),
}
chRevData := git.GetChangeRevsForTest([]*git.ChangeRev{
{
ChangeRevKey: git.ChangeRevKey{
Host: "test-review.googlesource.com",
ChangeNum: 123,
Revision: 2,
},
Project: "chromiumos/test/repo/name",
Files: []string{"some/file"},
},
})
repoToSrcRoot := map[string]string{"chromiumos/test/repo/name": "no/hw/tests/here"}
actualTestPlan, err := CreateTestPlan(testReqs, sourceTreeTestCfg, bbBuilds, chRevData, repoToSrcRoot)
if err != nil {
t.Error(err)
}
expectedTestPlan := &testplans.GenerateTestPlanResponse{
TestUnit: []*testplans.TestUnit{}}
if diff := cmp.Diff(expectedTestPlan, actualTestPlan, cmpopts.EquateEmpty()); diff != "" {
t.Errorf("CreateCombinedTestPlan bad result (-want/+got)\n%s", diff)
}
}
func TestCreateCombinedTestPlan_inputMissingTargetType(t *testing.T) {
testReqs := &testplans.TargetTestRequirementsCfg{
PerTargetTestRequirements: []*testplans.PerTargetTestRequirements{
// This is missing a TargetType.
{TargetCriteria: &testplans.TargetCriteria{}},
{TargetCriteria: &testplans.TargetCriteria{
TargetType: &testplans.TargetCriteria_BuildTarget{BuildTarget: "kevin"}}},
},
}
sourceTreeTestCfg := &testplans.SourceTreeTestCfg{}
bbBuilds := []*bbproto.Build{}
if _, err := CreateTestPlan(testReqs, sourceTreeTestCfg, bbBuilds, &git.ChangeRevData{}, map[string]string{}); err == nil {
t.Errorf("Expected an error to be returned")
}
}
func TestCreateCombinedTestPlan_skipsPointlessBuild(t *testing.T) {
kevinHWTestCfg := &testplans.HwTestCfg{HwTest: []*testplans.HwTestCfg_HwTest{
{
Suite: "HW kevin",
SkylabBoard: "kev",
},
}}
testReqs := &testplans.TargetTestRequirementsCfg{
PerTargetTestRequirements: []*testplans.PerTargetTestRequirements{
{TargetCriteria: &testplans.TargetCriteria{
TargetType: &testplans.TargetCriteria_BuildTarget{BuildTarget: "kevin"}},
HwTestCfg: kevinHWTestCfg},
},
}
sourceTreeTestCfg := &testplans.SourceTreeTestCfg{
SourceTreeTestRestriction: []*testplans.SourceTreeTestRestriction{
{SourceTree: &testplans.SourceTree{Path: "hw/tests/not/needed/here"},
TestRestriction: &testplans.TestRestriction{DisableHwTests: true}}}}
bbBuild := makeBuildbucketBuild("kevin", bbproto.Status_SUCCESS, []*bbproto.GerritChange{
{Host: "test-review.googlesource.com", Change: 123, Patchset: 2},
}, true)
bbBuild.Output.Properties.Fields["pointless_build"] = &_struct.Value{Kind: &_struct.Value_BoolValue{BoolValue: true}}
bbBuilds := []*bbproto.Build{bbBuild}
chRevData := git.GetChangeRevsForTest([]*git.ChangeRev{
{
ChangeRevKey: git.ChangeRevKey{
Host: "test-review.googlesource.com",
ChangeNum: 123,
Revision: 2,
},
Project: "chromiumos/repo/name",
Files: []string{"a/b/c"},
},
})
repoToSrcRoot := map[string]string{"chromiumos/repo/name": "src/to/file"}
actualTestPlan, err := CreateTestPlan(testReqs, sourceTreeTestCfg, bbBuilds, chRevData, repoToSrcRoot)
if err != nil {
t.Error(err)
}
expectedTestPlan := &testplans.GenerateTestPlanResponse{
TestUnit: []*testplans.TestUnit{}}
if diff := cmp.Diff(expectedTestPlan, actualTestPlan, cmpopts.EquateEmpty()); diff != "" {
t.Errorf("CreateCombinedTestPlan bad result (-want/+got)\n%s", diff)
}
}
func TestCreateTestPlan_succeedsOnNoBuildTarget(t *testing.T) {
testReqs := &testplans.TargetTestRequirementsCfg{}
sourceTreeTestCfg := &testplans.SourceTreeTestCfg{}
bbBuilds := []*bbproto.Build{
// build target is empty.
makeBuildbucketBuild("", bbproto.Status_FAILURE, []*bbproto.GerritChange{}, true),
}
chRevData := git.GetChangeRevsForTest([]*git.ChangeRev{})
repoToSrcRoot := map[string]string{}
_, err := CreateTestPlan(testReqs, sourceTreeTestCfg, bbBuilds, chRevData, repoToSrcRoot)
if err != nil {
t.Errorf("expected no error, but got %v", err)
}
}
func TestCreateCombinedTestPlan_skipsNonCritical(t *testing.T) {
// In this test, the build is not critical, so no test unit will be produced.
reefGceTestCfg := &testplans.GceTestCfg{GceTest: []*testplans.GceTestCfg_GceTest{
{TestType: "GCE reef"},
}}
testReqs := &testplans.TargetTestRequirementsCfg{
PerTargetTestRequirements: []*testplans.PerTargetTestRequirements{
{TargetCriteria: &testplans.TargetCriteria{
TargetType: &testplans.TargetCriteria_BuildTarget{BuildTarget: "reef"}},
GceTestCfg: reefGceTestCfg},
},
}
sourceTreeTestCfg := &testplans.SourceTreeTestCfg{
SourceTreeTestRestriction: []*testplans.SourceTreeTestRestriction{
{SourceTree: &testplans.SourceTree{Path: "hw/tests/not/needed/here"},
TestRestriction: &testplans.TestRestriction{DisableHwTests: true}}}}
bbBuilds := []*bbproto.Build{
makeBuildbucketBuild("reef", bbproto.Status_SUCCESS, []*bbproto.GerritChange{
{Host: "test-review.googlesource.com", Change: 123, Patchset: 2},
}, false),
}
chRevData := git.GetChangeRevsForTest([]*git.ChangeRev{
{
ChangeRevKey: git.ChangeRevKey{
Host: "test-review.googlesource.com",
ChangeNum: 123,
Revision: 2,
},
Project: "chromiumos/repo/name",
Files: []string{"a/b/c"},
},
})
repoToSrcRoot := map[string]string{"chromiumos/repo/name": "src/to/file"}
actualTestPlan, err := CreateTestPlan(testReqs, sourceTreeTestCfg, bbBuilds, chRevData, repoToSrcRoot)
if err != nil {
t.Error(err)
}
expectedTestPlan := &testplans.GenerateTestPlanResponse{
TestUnit: []*testplans.TestUnit{},
GceTestUnits: []*testplans.GceTestUnit{}}
if diff := cmp.Diff(expectedTestPlan, actualTestPlan, cmpopts.EquateEmpty()); diff != "" {
t.Errorf("CreateCombinedTestPlan bad result (-want/+got)\n%s", diff)
}
}