• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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