blob: f521916a329c35d0d2fa5e62c0560753c41824ab [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package compatibility
import (
"fmt"
testpb "go.chromium.org/chromiumos/config/go/test/api"
test_api_v1 "go.chromium.org/chromiumos/config/go/test/api/v1"
"go.chromium.org/chromiumos/infra/proto/go/chromiumos"
bbpb "go.chromium.org/luci/buildbucket/proto"
"go.chromium.org/luci/common/data/stringset"
)
// buildCouldMatchCoverageRule returns whether bi could possibly match rule.
// This is true iff:
//
// 1. bi.buildTarget matches a program in rule. If rule has set a board variant,
// the match checks bi.buildTarget against <program>-<variant>.
//
// 2. bi.profile matches the profile in rule (both can possibly be empty).
func buildCouldMatchCoverageRule(bi *buildInfo, rule *testpb.CoverageRule, programAttr *testpb.DutAttribute) (bool, error) {
if len(rule.GetDutTargets()) != 1 {
return false, fmt.Errorf("expected exactly one DutTarget in CoverageRule, got %q", rule)
}
dutTarget := rule.GetDutTargets()[0]
programs, err := getAttrFromCriteria(dutTarget.GetCriteria(), programAttr)
if err != nil {
return false, err
}
// If board variant is set in the ProvisionConfig, match the bi.buildTarget
// against <program>-<variant> instead of just program. programs is still
// pointing to the slice in the CoverageRule, so create a new slice with
// the variant added instead of modifying the slice in the CoverageRule.
provisionConfig := dutTarget.GetProvisionConfig()
if len(provisionConfig.GetBoardVariant()) > 0 {
programsWithVariant := []string{}
for _, program := range programs {
programsWithVariant = append(programsWithVariant, program+"-"+provisionConfig.GetBoardVariant())
}
if !stringset.NewFromSlice(programsWithVariant...).Has(bi.buildTarget) {
return false, nil
}
} else {
if !stringset.NewFromSlice(programs...).Has(bi.buildTarget) {
return false, nil
}
}
// Build targets match, return true iff profiles match.
return bi.profile == dutTarget.GetProvisionConfig().GetProfile(), nil
}
// TestableBuilds computes the builds in generateTestPlanReq that could possibly
// match a CoverageRule in hwTestPlans or vmTestPlans. A build can match a
// CoverageRule if its target matches the target(s), variant, and profile
// required by the CoverageRule. The builds in generateTestPlanReq do not
// necessarily need to be completed with test artifacts, they just need to have
// build target information set on the input property. TestableBuilds is
// different from ToCTP1, which actually selects specific builds and programs to
// test based off completed builds; TestableBuilds computes which builds could
// possibly be tested, e.g. to know which builds to collect.
func TestableBuilds(
hwTestPlans []*test_api_v1.HWTestPlan,
vmTestPlans []*test_api_v1.VMTestPlan,
builds []*bbpb.Build,
builderConfigs *chromiumos.BuilderConfigs,
dutAttributeList *testpb.DutAttributeList,
) ([]*bbpb.Build, error) {
// Extract the build protos into buildInfos, including builds that don't have
// test artifacts (builds aren't necessarily completed yet).
buildInfos, err := buildsToBuildInfos(builds, builderConfigs, false)
if err != nil {
return nil, err
}
programAttr, err := getDutAttribute(dutAttributeList, "attr-program")
if err != nil {
return nil, err
}
allCoverageRules := make([]*testpb.CoverageRule, 0)
for _, plan := range hwTestPlans {
allCoverageRules = append(allCoverageRules, plan.GetCoverageRules()...)
}
for _, plan := range vmTestPlans {
allCoverageRules = append(allCoverageRules, plan.GetCoverageRules()...)
}
testableBuilds := make([]*bbpb.Build, 0)
CheckBuild:
for _, buildInfo := range buildInfos {
for _, rule := range allCoverageRules {
couldMatch, err := buildCouldMatchCoverageRule(buildInfo, rule, programAttr)
if err != nil {
return nil, err
}
if couldMatch {
testableBuilds = append(testableBuilds, buildInfo.build)
continue CheckBuild
}
}
}
return testableBuilds, nil
}