1// Copyright 2024 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package cc 16 17import ( 18 "android/soong/android" 19 "android/soong/android/team_proto" 20 "log" 21 "strings" 22 "testing" 23 24 "github.com/google/blueprint" 25 "google.golang.org/protobuf/proto" 26) 27 28func TestTestOnlyProvider(t *testing.T) { 29 t.Parallel() 30 ctx := android.GroupFixturePreparers( 31 prepareForCcTest, 32 android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { 33 ctx.RegisterModuleType("cc_test_host", TestHostFactory) 34 }), 35 ).RunTestWithBp(t, ` 36 // These should be test-only 37 cc_fuzz { name: "cc-fuzz" } 38 cc_test { name: "cc-test", gtest:false } 39 cc_benchmark { name: "cc-benchmark" } 40 cc_library { name: "cc-library-forced", 41 test_only: true } 42 cc_test_library {name: "cc-test-library", gtest: false} 43 cc_test_host {name: "cc-test-host", gtest: false} 44 45 // These should not be. 46 cc_genrule { name: "cc_genrule", cmd: "echo foo", out: ["out"] } 47 cc_library { name: "cc_library" } 48 cc_library { name: "cc_library_false", test_only: false } 49 cc_library_static { name: "cc_static" } 50 cc_library_shared { name: "cc_library_shared" } 51 52 cc_object { name: "cc-object" } 53 `) 54 55 // Visit all modules and ensure only the ones that should 56 // marked as test-only are marked as test-only. 57 58 actualTestOnly := []string{} 59 ctx.VisitAllModules(func(m blueprint.Module) { 60 if provider, ok := android.OtherModuleProvider(ctx.TestContext.OtherModuleProviderAdaptor(), m, android.TestOnlyProviderKey); ok { 61 if provider.TestOnly { 62 actualTestOnly = append(actualTestOnly, m.Name()) 63 } 64 } 65 }) 66 expectedTestOnlyModules := []string{ 67 "cc-test", 68 "cc-library-forced", 69 "cc-fuzz", 70 "cc-benchmark", 71 "cc-test-library", 72 "cc-test-host", 73 } 74 75 notEqual, left, right := android.ListSetDifference(expectedTestOnlyModules, actualTestOnly) 76 if notEqual { 77 t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right) 78 } 79} 80 81func TestTestOnlyValueWithTestPerSrcProp(t *testing.T) { 82 t.Parallel() 83 ctx := android.GroupFixturePreparers( 84 prepareForCcTest, 85 ).RunTestWithBp(t, ` 86 // These should be test-only 87 cc_test { name: "cc-test", 88 gtest: false, 89 test_per_src: true, 90 srcs: ["foo_test.cpp"], 91 test_options: { unit_test: false, }, 92 } 93 `) 94 95 // Ensure all variation of test-per-src tests are marked test-only. 96 ctx.VisitAllModules(func(m blueprint.Module) { 97 testOnly := false 98 if provider, ok := android.OtherModuleProvider(ctx.TestContext.OtherModuleProviderAdaptor(), m, android.TestOnlyProviderKey); ok { 99 if provider.TestOnly { 100 testOnly = true 101 } 102 } 103 if module, ok := m.(*Module); ok { 104 if testModule, ok := module.installer.(*testBinary); ok { 105 if !testOnly && *testModule.Properties.Test_per_src { 106 t.Errorf("%v is not test-only but should be", m) 107 } 108 } 109 } 110 }) 111} 112 113func TestTestOnlyInTeamsProto(t *testing.T) { 114 t.Parallel() 115 ctx := android.GroupFixturePreparers( 116 android.PrepareForTestWithTeamBuildComponents, 117 prepareForCcTest, 118 android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { 119 ctx.RegisterParallelSingletonType("all_teams", android.AllTeamsFactory) 120 ctx.RegisterModuleType("cc_test_host", TestHostFactory) 121 122 }), 123 ).RunTestWithBp(t, ` 124 package { default_team: "someteam"} 125 126 // These should be test-only 127 cc_fuzz { name: "cc-fuzz" } 128 cc_test { name: "cc-test", gtest:false } 129 cc_benchmark { name: "cc-benchmark" } 130 cc_library { name: "cc-library-forced", 131 test_only: true } 132 cc_test_library {name: "cc-test-library", gtest: false} 133 cc_test_host {name: "cc-test-host", gtest: false} 134 135 // These should not be. 136 cc_genrule { name: "cc_genrule", cmd: "echo foo", out: ["out"] } 137 cc_library { name: "cc_library" } 138 cc_library_static { name: "cc_static" } 139 cc_library_shared { name: "cc_library_shared" } 140 141 cc_object { name: "cc-object" } 142 team { 143 name: "someteam", 144 trendy_team_id: "cool_team", 145 } 146 `) 147 148 var teams *team_proto.AllTeams 149 teams = getTeamProtoOutput(t, ctx) 150 151 // map of module name -> trendy team name. 152 actualTrueModules := []string{} 153 for _, teamProto := range teams.Teams { 154 if Bool(teamProto.TestOnly) { 155 actualTrueModules = append(actualTrueModules, teamProto.GetTargetName()) 156 } 157 } 158 expectedTestOnlyModules := []string{ 159 "cc-test", 160 "cc-library-forced", 161 "cc-fuzz", 162 "cc-benchmark", 163 "cc-test-library", 164 "cc-test-host", 165 } 166 167 notEqual, left, right := android.ListSetDifference(expectedTestOnlyModules, actualTrueModules) 168 if notEqual { 169 t.Errorf("test-only: Expected but not found: %v, Found but not expected: %v", left, right) 170 } 171} 172 173// Don't allow setting test-only on things that are always tests or never tests. 174func TestInvalidTestOnlyTargets(t *testing.T) { 175 testCases := []string{ 176 ` cc_test { name: "cc-test", test_only: true, gtest: false, srcs: ["foo.cc"], } `, 177 ` cc_binary { name: "cc-binary", test_only: true, srcs: ["foo.cc"], } `, 178 ` cc_test_library {name: "cc-test-library", test_only: true, gtest: false} `, 179 ` cc_test_host {name: "cc-test-host", test_only: true, gtest: false} `, 180 ` cc_defaults {name: "cc-defaults", test_only: true} `, 181 } 182 183 for i, bp := range testCases { 184 ctx := android.GroupFixturePreparers( 185 prepareForCcTest, 186 android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { 187 ctx.RegisterModuleType("cc_test_host", TestHostFactory) 188 })). 189 ExtendWithErrorHandler(android.FixtureIgnoreErrors). 190 RunTestWithBp(t, bp) 191 if len(ctx.Errs) == 0 { 192 t.Errorf("Expected err setting test_only in testcase #%d", i) 193 } 194 if len(ctx.Errs) > 1 { 195 t.Errorf("Too many errs: [%s] %v", bp, ctx.Errs) 196 } 197 198 if len(ctx.Errs) == 1 { 199 if !strings.Contains(ctx.Errs[0].Error(), "unrecognized property \"test_only\"") { 200 t.Errorf("ERR: %s bad bp: %s", ctx.Errs[0], bp) 201 } 202 } 203 } 204} 205 206func getTeamProtoOutput(t *testing.T, ctx *android.TestResult) *team_proto.AllTeams { 207 teams := new(team_proto.AllTeams) 208 config := ctx.SingletonForTests("all_teams") 209 allOutputs := config.AllOutputs() 210 211 protoPath := allOutputs[0] 212 213 out := config.MaybeOutput(protoPath) 214 outProto := []byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, out)) 215 if err := proto.Unmarshal(outProto, teams); err != nil { 216 log.Fatalln("Failed to parse teams proto:", err) 217 } 218 return teams 219} 220