Add "test-only" flag for cc modules
As part of aosp/3022586 where we added the idea of "test-only" modules and top_level_test_targets, this CL implements that for cc_ modules. We let users set "test-only" on cc_library, but not on other modules where the module kind is implicitly test-only, like cc_test. Here the implementation, not the user decides it is test-only. % gqui from "flatten(~/aosp-main-with-phones/out/soong/ownership/all_teams.pb, teams)" proto team.proto:AllTeams 'select teams.kind, count(*) where teams.top_level_target = true group by teams.kind' aosp_shiba[6:15:47]/0 +--------------+----------+ | teams.kind | count(*) | +--------------+----------+ | art_cc_test | 56 | | cc_benchmark | 68 | | cc_fuzz | 515 | | cc_test | 3518 | | cc_test_host | 6 | +--------------+----------+ % gqui from "flatten(~/aosp-main-with-phones/out/soong/ownership/all_teams.pb, teams)" proto team.proto:AllTeams 'select teams.kind, count(*) where teams.test_only = true group by teams.kind' aosp_shiba[6:16:26]/0 +--------------------------+----------+ | teams.kind | count(*) | +--------------------------+----------+ | art_cc_test | 56 | | art_cc_test_library | 13 | | cc_benchmark | 68 | | cc_fuzz | 515 | | cc_test | 3518 | | cc_test_host | 6 | | cc_test_library | 484 | +--------------------------+----------+ Bug: b/327280661 Test: m nothing --no-skip-soong-tests Test: go test ./cc Test: m all_teams Change-Id: I344436c424a9dfbdcf27e10f42f5cebc3d2b1261
This commit is contained in:
@@ -91,6 +91,7 @@ bootstrap_go_package {
|
||||
"afdo_test.go",
|
||||
"binary_test.go",
|
||||
"cc_test.go",
|
||||
"cc_test_only_property_test.go",
|
||||
"compiler_test.go",
|
||||
"gen_test.go",
|
||||
"genrule_test.go",
|
||||
|
17
cc/cc.go
17
cc/cc.go
@@ -845,6 +845,7 @@ type Module struct {
|
||||
|
||||
VendorProperties VendorProperties
|
||||
Properties BaseProperties
|
||||
sourceProperties android.SourceProperties
|
||||
|
||||
// initialize before calling Init
|
||||
hod android.HostOrDeviceSupported
|
||||
@@ -1262,6 +1263,10 @@ func (c *Module) Init() android.Module {
|
||||
for _, feature := range c.features {
|
||||
c.AddProperties(feature.props()...)
|
||||
}
|
||||
// Allow test-only on libraries that are not cc_test_library
|
||||
if c.library != nil && !c.testLibrary() {
|
||||
c.AddProperties(&c.sourceProperties)
|
||||
}
|
||||
|
||||
android.InitAndroidArchModule(c, c.hod, c.multilib)
|
||||
android.InitApexModule(c)
|
||||
@@ -2135,6 +2140,18 @@ func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
|
||||
if c.testModule {
|
||||
android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
|
||||
}
|
||||
|
||||
// If Test_only is set on a module in bp file, respect the setting, otherwise
|
||||
// see if is a known test module type.
|
||||
testOnly := c.testModule || c.testLibrary()
|
||||
if c.sourceProperties.Test_only != nil {
|
||||
testOnly = Bool(c.sourceProperties.Test_only)
|
||||
}
|
||||
android.SetProvider(ctx, android.TestOnlyProviderKey, android.TestModuleInformation{
|
||||
TestOnly: testOnly,
|
||||
TopLevelTarget: c.testModule,
|
||||
})
|
||||
|
||||
android.SetProvider(ctx, blueprint.SrcsFileProviderKey, blueprint.SrcsFileProviderData{SrcPaths: deps.GeneratedSources.Strings()})
|
||||
|
||||
android.CollectDependencyAconfigFiles(ctx, &c.mergedAconfigFiles)
|
||||
|
187
cc/cc_test_only_property_test.go
Normal file
187
cc/cc_test_only_property_test.go
Normal file
@@ -0,0 +1,187 @@
|
||||
// Copyright 2024 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cc
|
||||
|
||||
import (
|
||||
"android/soong/android"
|
||||
"android/soong/android/team_proto"
|
||||
"log"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/blueprint"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func TestTestOnlyProvider(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := android.GroupFixturePreparers(
|
||||
prepareForCcTest,
|
||||
android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
|
||||
ctx.RegisterModuleType("cc_test_host", TestHostFactory)
|
||||
}),
|
||||
).RunTestWithBp(t, `
|
||||
// These should be test-only
|
||||
cc_fuzz { name: "cc-fuzz" }
|
||||
cc_test { name: "cc-test", gtest:false }
|
||||
cc_benchmark { name: "cc-benchmark" }
|
||||
cc_library { name: "cc-library-forced",
|
||||
test_only: true }
|
||||
cc_test_library {name: "cc-test-library", gtest: false}
|
||||
cc_test_host {name: "cc-test-host", gtest: false}
|
||||
|
||||
// These should not be.
|
||||
cc_genrule { name: "cc_genrule", cmd: "echo foo", out: ["out"] }
|
||||
cc_library { name: "cc_library" }
|
||||
cc_library { name: "cc_library_false", test_only: false }
|
||||
cc_library_static { name: "cc_static" }
|
||||
cc_library_shared { name: "cc_library_shared" }
|
||||
|
||||
cc_object { name: "cc-object" }
|
||||
`)
|
||||
|
||||
// Visit all modules and ensure only the ones that should
|
||||
// marked as test-only are marked as test-only.
|
||||
|
||||
actualTestOnly := []string{}
|
||||
ctx.VisitAllModules(func(m blueprint.Module) {
|
||||
if provider, ok := android.OtherModuleProvider(ctx.TestContext.OtherModuleProviderAdaptor(), m, android.TestOnlyProviderKey); ok {
|
||||
if provider.TestOnly {
|
||||
actualTestOnly = append(actualTestOnly, m.Name())
|
||||
}
|
||||
}
|
||||
})
|
||||
expectedTestOnlyModules := []string{
|
||||
"cc-test",
|
||||
"cc-library-forced",
|
||||
"cc-fuzz",
|
||||
"cc-benchmark",
|
||||
"cc-test-library",
|
||||
"cc-test-host",
|
||||
}
|
||||
|
||||
notEqual, left, right := android.ListSetDifference(expectedTestOnlyModules, actualTestOnly)
|
||||
if notEqual {
|
||||
t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTestOnlyInTeamsProto(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := android.GroupFixturePreparers(
|
||||
android.PrepareForTestWithTeamBuildComponents,
|
||||
prepareForCcTest,
|
||||
android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
|
||||
ctx.RegisterParallelSingletonType("all_teams", android.AllTeamsFactory)
|
||||
ctx.RegisterModuleType("cc_test_host", TestHostFactory)
|
||||
|
||||
}),
|
||||
).RunTestWithBp(t, `
|
||||
package { default_team: "someteam"}
|
||||
|
||||
// These should be test-only
|
||||
cc_fuzz { name: "cc-fuzz" }
|
||||
cc_test { name: "cc-test", gtest:false }
|
||||
cc_benchmark { name: "cc-benchmark" }
|
||||
cc_library { name: "cc-library-forced",
|
||||
test_only: true }
|
||||
cc_test_library {name: "cc-test-library", gtest: false}
|
||||
cc_test_host {name: "cc-test-host", gtest: false}
|
||||
|
||||
// These should not be.
|
||||
cc_genrule { name: "cc_genrule", cmd: "echo foo", out: ["out"] }
|
||||
cc_library { name: "cc_library" }
|
||||
cc_library_static { name: "cc_static" }
|
||||
cc_library_shared { name: "cc_library_shared" }
|
||||
|
||||
cc_object { name: "cc-object" }
|
||||
team {
|
||||
name: "someteam",
|
||||
trendy_team_id: "cool_team",
|
||||
}
|
||||
`)
|
||||
|
||||
var teams *team_proto.AllTeams
|
||||
teams = getTeamProtoOutput(t, ctx)
|
||||
|
||||
// map of module name -> trendy team name.
|
||||
actualTrueModules := []string{}
|
||||
for _, teamProto := range teams.Teams {
|
||||
if Bool(teamProto.TestOnly) {
|
||||
actualTrueModules = append(actualTrueModules, teamProto.GetTargetName())
|
||||
}
|
||||
}
|
||||
expectedTestOnlyModules := []string{
|
||||
"cc-test",
|
||||
"cc-library-forced",
|
||||
"cc-fuzz",
|
||||
"cc-benchmark",
|
||||
"cc-test-library",
|
||||
"cc-test-host",
|
||||
}
|
||||
|
||||
notEqual, left, right := android.ListSetDifference(expectedTestOnlyModules, actualTrueModules)
|
||||
if notEqual {
|
||||
t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right)
|
||||
}
|
||||
}
|
||||
|
||||
// Don't allow setting test-only on things that are always tests or never tests.
|
||||
func TestInvalidTestOnlyTargets(t *testing.T) {
|
||||
testCases := []string{
|
||||
` cc_test { name: "cc-test", test_only: true, gtest: false, srcs: ["foo.cc"], } `,
|
||||
` cc_binary { name: "cc-binary", test_only: true, srcs: ["foo.cc"], } `,
|
||||
` cc_test_library {name: "cc-test-library", test_only: true, gtest: false} `,
|
||||
` cc_test_host {name: "cc-test-host", test_only: true, gtest: false} `,
|
||||
` cc_defaults {name: "cc-defaults", test_only: true} `,
|
||||
}
|
||||
|
||||
for i, bp := range testCases {
|
||||
ctx := android.GroupFixturePreparers(
|
||||
prepareForCcTest,
|
||||
android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
|
||||
ctx.RegisterModuleType("cc_test_host", TestHostFactory)
|
||||
})).
|
||||
ExtendWithErrorHandler(android.FixtureIgnoreErrors).
|
||||
RunTestWithBp(t, bp)
|
||||
if len(ctx.Errs) == 0 {
|
||||
t.Errorf("Expected err setting test_only in testcase #%d", i)
|
||||
}
|
||||
if len(ctx.Errs) > 1 {
|
||||
t.Errorf("Too many errs: [%s] %v", bp, ctx.Errs)
|
||||
}
|
||||
|
||||
if len(ctx.Errs) == 1 {
|
||||
if !strings.Contains(ctx.Errs[0].Error(), "unrecognized property \"test_only\"") {
|
||||
t.Errorf("ERR: %s bad bp: %s", ctx.Errs[0], bp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getTeamProtoOutput(t *testing.T, ctx *android.TestResult) *team_proto.AllTeams {
|
||||
teams := new(team_proto.AllTeams)
|
||||
config := ctx.SingletonForTests("all_teams")
|
||||
allOutputs := config.AllOutputs()
|
||||
|
||||
protoPath := allOutputs[0]
|
||||
|
||||
out := config.MaybeOutput(protoPath)
|
||||
outProto := []byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, out))
|
||||
if err := proto.Unmarshal(outProto, teams); err != nil {
|
||||
log.Fatalln("Failed to parse teams proto:", err)
|
||||
}
|
||||
return teams
|
||||
}
|
Reference in New Issue
Block a user