1// Copyright 2016 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	"path/filepath"
19	"strconv"
20	"strings"
21
22	"android/soong/android"
23	"android/soong/tradefed"
24)
25
26type TestProperties struct {
27	// if set, build against the gtest library. Defaults to true.
28	Gtest *bool
29
30	// if set, use the isolated gtest runner. Defaults to false.
31	Isolated *bool
32
33	// List of APEXes that this module tests. The module has access to
34	// the private part of the listed APEXes even when it is not included in the
35	// APEXes.
36	Test_for []string
37}
38
39// Test option struct.
40type TestOptions struct {
41	// The UID that you want to run the test as on a device.
42	Run_test_as *string
43	// A list of free-formed strings without spaces that categorize the test.
44	Test_suite_tag []string
45}
46
47type TestBinaryProperties struct {
48	// Create a separate binary for each source file.  Useful when there is
49	// global state that can not be torn down and reset between each test suite.
50	Test_per_src *bool
51
52	// Disables the creation of a test-specific directory when used with
53	// relative_install_path. Useful if several tests need to be in the same
54	// directory, but test_per_src doesn't work.
55	No_named_install_directory *bool
56
57	// list of files or filegroup modules that provide data that should be installed alongside
58	// the test
59	Data []string `android:"path,arch_variant"`
60
61	// list of shared library modules that should be installed alongside the test
62	Data_libs []string `android:"arch_variant"`
63
64	// list of compatibility suites (for example "cts", "vts") that the module should be
65	// installed into.
66	Test_suites []string `android:"arch_variant"`
67
68	// the name of the test configuration (for example "AndroidTest.xml") that should be
69	// installed with the module.
70	Test_config *string `android:"path,arch_variant"`
71
72	// the name of the test configuration template (for example "AndroidTestTemplate.xml") that
73	// should be installed with the module.
74	Test_config_template *string `android:"path,arch_variant"`
75
76	// Test options.
77	Test_options TestOptions
78
79	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
80	// with root permission.
81	Require_root *bool
82
83	// Add RunCommandTargetPreparer to stop framework before the test and start it after the test.
84	Disable_framework *bool
85
86	// Add MinApiLevelModuleController to auto generated test config. If the device property of
87	// "ro.product.first_api_level" < Test_min_api_level, then skip this module.
88	Test_min_api_level *int64
89
90	// Add MinApiLevelModuleController to auto generated test config. If the device property of
91	// "ro.build.version.sdk" < Test_min_sdk_version, then skip this module.
92	Test_min_sdk_version *int64
93
94	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
95	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
96	// explicitly.
97	Auto_gen_config *bool
98
99	// Add parameterized mainline modules to auto generated test config. The options will be
100	// handled by TradeFed to download and install the specified modules on the device.
101	Test_mainline_modules []string
102}
103
104func init() {
105	android.RegisterModuleType("cc_test", TestFactory)
106	android.RegisterModuleType("cc_test_library", TestLibraryFactory)
107	android.RegisterModuleType("cc_benchmark", BenchmarkFactory)
108	android.RegisterModuleType("cc_test_host", TestHostFactory)
109	android.RegisterModuleType("cc_benchmark_host", BenchmarkHostFactory)
110}
111
112// cc_test generates a test config file and an executable binary file to test
113// specific functionality on a device. The executable binary gets an implicit
114// static_libs dependency on libgtests unless the gtest flag is set to false.
115func TestFactory() android.Module {
116	module := NewTest(android.HostAndDeviceSupported)
117	return module.Init()
118}
119
120// cc_test_library creates an archive of files (i.e. .o files) which is later
121// referenced by another module (such as cc_test, cc_defaults or cc_test_library)
122// for archiving or linking.
123func TestLibraryFactory() android.Module {
124	module := NewTestLibrary(android.HostAndDeviceSupported)
125	return module.Init()
126}
127
128// cc_benchmark compiles an executable binary that performs benchmark testing
129// of a specific component in a device. Additional files such as test suites
130// and test configuration are installed on the side of the compiled executed
131// binary.
132func BenchmarkFactory() android.Module {
133	module := NewBenchmark(android.HostAndDeviceSupported)
134	return module.Init()
135}
136
137// cc_test_host compiles a test host binary.
138func TestHostFactory() android.Module {
139	module := NewTest(android.HostSupported)
140	return module.Init()
141}
142
143// cc_benchmark_host compiles an executable binary that performs benchmark
144// testing of a specific component in the host. Additional files such as
145// test suites and test configuration are installed on the side of the
146// compiled executed binary.
147func BenchmarkHostFactory() android.Module {
148	module := NewBenchmark(android.HostSupported)
149	return module.Init()
150}
151
152type testPerSrc interface {
153	testPerSrc() bool
154	srcs() []string
155	isAllTestsVariation() bool
156	setSrc(string, string)
157	unsetSrc()
158}
159
160func (test *testBinary) testPerSrc() bool {
161	return Bool(test.Properties.Test_per_src)
162}
163
164func (test *testBinary) srcs() []string {
165	return test.baseCompiler.Properties.Srcs
166}
167
168func (test *testBinary) dataPaths() []android.DataPath {
169	return test.data
170}
171
172func (test *testBinary) isAllTestsVariation() bool {
173	stem := test.binaryDecorator.Properties.Stem
174	return stem != nil && *stem == ""
175}
176
177func (test *testBinary) setSrc(name, src string) {
178	test.baseCompiler.Properties.Srcs = []string{src}
179	test.binaryDecorator.Properties.Stem = StringPtr(name)
180}
181
182func (test *testBinary) unsetSrc() {
183	test.baseCompiler.Properties.Srcs = nil
184	test.binaryDecorator.Properties.Stem = StringPtr("")
185}
186
187var _ testPerSrc = (*testBinary)(nil)
188
189func TestPerSrcMutator(mctx android.BottomUpMutatorContext) {
190	if m, ok := mctx.Module().(*Module); ok {
191		if test, ok := m.linker.(testPerSrc); ok {
192			numTests := len(test.srcs())
193			if test.testPerSrc() && numTests > 0 {
194				if duplicate, found := android.CheckDuplicate(test.srcs()); found {
195					mctx.PropertyErrorf("srcs", "found a duplicate entry %q", duplicate)
196					return
197				}
198				testNames := make([]string, numTests)
199				for i, src := range test.srcs() {
200					testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
201				}
202				// In addition to creating one variation per test source file,
203				// create an additional "all tests" variation named "", and have it
204				// depends on all other test_per_src variations. This is useful to
205				// create subsequent dependencies of a given module on all
206				// test_per_src variations created above: by depending on
207				// variation "", that module will transitively depend on all the
208				// other test_per_src variations without the need to know their
209				// name or even their number.
210				testNames = append(testNames, "")
211				tests := mctx.CreateLocalVariations(testNames...)
212				all_tests := tests[numTests]
213				all_tests.(*Module).linker.(testPerSrc).unsetSrc()
214				// Prevent the "all tests" variation from being installable nor
215				// exporting to Make, as it won't create any output file.
216				all_tests.(*Module).Properties.PreventInstall = true
217				all_tests.(*Module).Properties.HideFromMake = true
218				for i, src := range test.srcs() {
219					tests[i].(*Module).linker.(testPerSrc).setSrc(testNames[i], src)
220					mctx.AddInterVariantDependency(testPerSrcDepTag, all_tests, tests[i])
221				}
222			}
223		}
224	}
225}
226
227type testDecorator struct {
228	Properties TestProperties
229	linker     *baseLinker
230}
231
232func (test *testDecorator) gtest() bool {
233	return BoolDefault(test.Properties.Gtest, true)
234}
235
236func (test *testDecorator) testFor() []string {
237	return test.Properties.Test_for
238}
239
240func (test *testDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
241	if !test.gtest() {
242		return flags
243	}
244
245	flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_HAS_STD_STRING")
246	if ctx.Host() {
247		flags.Local.CFlags = append(flags.Local.CFlags, "-O0", "-g")
248
249		switch ctx.Os() {
250		case android.Windows:
251			flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_OS_WINDOWS")
252		case android.Linux:
253			flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_OS_LINUX")
254		case android.Darwin:
255			flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_OS_MAC")
256		}
257	} else {
258		flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_OS_LINUX_ANDROID")
259	}
260
261	return flags
262}
263
264func (test *testDecorator) linkerDeps(ctx BaseModuleContext, deps Deps) Deps {
265	if test.gtest() {
266		if ctx.useSdk() && ctx.Device() {
267			deps.StaticLibs = append(deps.StaticLibs, "libgtest_main_ndk_c++", "libgtest_ndk_c++")
268		} else if BoolDefault(test.Properties.Isolated, false) {
269			deps.StaticLibs = append(deps.StaticLibs, "libgtest_isolated_main")
270			// The isolated library requires liblog, but adding it
271			// as a static library means unit tests cannot override
272			// liblog functions. Instead make it a shared library
273			// dependency.
274			deps.SharedLibs = append(deps.SharedLibs, "liblog")
275		} else {
276			deps.StaticLibs = append(deps.StaticLibs, "libgtest_main", "libgtest")
277		}
278	}
279
280	return deps
281}
282
283func (test *testDecorator) linkerInit(ctx BaseModuleContext, linker *baseLinker) {
284	// 1. Add ../../lib[64] to rpath so that out/host/linux-x86/nativetest/<test dir>/<test> can
285	// find out/host/linux-x86/lib[64]/library.so
286	// 2. Add ../../../lib[64] to rpath so that out/host/linux-x86/testcases/<test dir>/<CPU>/<test> can
287	// also find out/host/linux-x86/lib[64]/library.so
288	runpaths := []string{"../../lib", "../../../lib"}
289	for _, runpath := range runpaths {
290		if ctx.toolchain().Is64Bit() {
291			runpath += "64"
292		}
293		linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, runpath)
294	}
295
296	// add "" to rpath so that test binaries can find libraries in their own test directory
297	linker.dynamicProperties.RunPaths = append(linker.dynamicProperties.RunPaths, "")
298}
299
300func (test *testDecorator) linkerProps() []interface{} {
301	return []interface{}{&test.Properties}
302}
303
304func NewTestInstaller() *baseInstaller {
305	return NewBaseInstaller("nativetest", "nativetest64", InstallInData)
306}
307
308type testBinary struct {
309	testDecorator
310	*binaryDecorator
311	*baseCompiler
312	Properties TestBinaryProperties
313	data       []android.DataPath
314	testConfig android.Path
315}
316
317func (test *testBinary) linkerProps() []interface{} {
318	props := append(test.testDecorator.linkerProps(), test.binaryDecorator.linkerProps()...)
319	props = append(props, &test.Properties)
320	return props
321}
322
323func (test *testBinary) linkerInit(ctx BaseModuleContext) {
324	test.testDecorator.linkerInit(ctx, test.binaryDecorator.baseLinker)
325	test.binaryDecorator.linkerInit(ctx)
326}
327
328func (test *testBinary) linkerDeps(ctx DepsContext, deps Deps) Deps {
329	deps = test.testDecorator.linkerDeps(ctx, deps)
330	deps = test.binaryDecorator.linkerDeps(ctx, deps)
331	deps.DataLibs = append(deps.DataLibs, test.Properties.Data_libs...)
332	return deps
333}
334
335func (test *testBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
336	flags = test.binaryDecorator.linkerFlags(ctx, flags)
337	flags = test.testDecorator.linkerFlags(ctx, flags)
338	return flags
339}
340
341func (test *testBinary) install(ctx ModuleContext, file android.Path) {
342	dataSrcPaths := android.PathsForModuleSrc(ctx, test.Properties.Data)
343
344	for _, dataSrcPath := range dataSrcPaths {
345		test.data = append(test.data, android.DataPath{SrcPath: dataSrcPath})
346	}
347
348	ctx.VisitDirectDepsWithTag(dataLibDepTag, func(dep android.Module) {
349		depName := ctx.OtherModuleName(dep)
350		ccDep, ok := dep.(LinkableInterface)
351
352		if !ok {
353			ctx.ModuleErrorf("data_lib %q is not a linkable cc module", depName)
354		}
355		ccModule, ok := dep.(*Module)
356		if !ok {
357			ctx.ModuleErrorf("data_lib %q is not a cc module", depName)
358		}
359		if ccDep.OutputFile().Valid() {
360			test.data = append(test.data,
361				android.DataPath{SrcPath: ccDep.OutputFile().Path(),
362					RelativeInstallPath: ccModule.installer.relativeInstallPath()})
363		}
364	})
365
366	var api_level_prop string
367	var configs []tradefed.Config
368	var min_level string
369	for _, module := range test.Properties.Test_mainline_modules {
370		configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: "mainline-param", Value: module})
371	}
372	if Bool(test.Properties.Require_root) {
373		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
374	} else {
375		var options []tradefed.Option
376		options = append(options, tradefed.Option{Name: "force-root", Value: "false"})
377		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", options})
378	}
379	if Bool(test.Properties.Disable_framework) {
380		var options []tradefed.Option
381		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.StopServicesSetup", options})
382	}
383	if Bool(test.testDecorator.Properties.Isolated) {
384		configs = append(configs, tradefed.Option{Name: "not-shardable", Value: "true"})
385	}
386	if test.Properties.Test_options.Run_test_as != nil {
387		configs = append(configs, tradefed.Option{Name: "run-test-as", Value: String(test.Properties.Test_options.Run_test_as)})
388	}
389	for _, tag := range test.Properties.Test_options.Test_suite_tag {
390		configs = append(configs, tradefed.Option{Name: "test-suite-tag", Value: tag})
391	}
392	if test.Properties.Test_min_api_level != nil && test.Properties.Test_min_sdk_version != nil {
393		ctx.PropertyErrorf("test_min_api_level", "'test_min_api_level' and 'test_min_sdk_version' should not be set at the same time.")
394	} else if test.Properties.Test_min_api_level != nil {
395		api_level_prop = "ro.product.first_api_level"
396		min_level = strconv.FormatInt(int64(*test.Properties.Test_min_api_level), 10)
397	} else if test.Properties.Test_min_sdk_version != nil {
398		api_level_prop = "ro.build.version.sdk"
399		min_level = strconv.FormatInt(int64(*test.Properties.Test_min_sdk_version), 10)
400	}
401	if api_level_prop != "" {
402		var options []tradefed.Option
403		options = append(options, tradefed.Option{Name: "min-api-level", Value: min_level})
404		options = append(options, tradefed.Option{Name: "api-level-prop", Value: api_level_prop})
405		configs = append(configs, tradefed.Object{"module_controller", "com.android.tradefed.testtype.suite.module.MinApiLevelModuleController", options})
406	}
407
408	test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
409		test.Properties.Test_config_template, test.Properties.Test_suites, configs, test.Properties.Auto_gen_config)
410
411	test.binaryDecorator.baseInstaller.dir = "nativetest"
412	test.binaryDecorator.baseInstaller.dir64 = "nativetest64"
413
414	if !Bool(test.Properties.No_named_install_directory) {
415		test.binaryDecorator.baseInstaller.relative = ctx.ModuleName()
416	} else if String(test.binaryDecorator.baseInstaller.Properties.Relative_install_path) == "" {
417		ctx.PropertyErrorf("no_named_install_directory", "Module install directory may only be disabled if relative_install_path is set")
418	}
419
420	test.binaryDecorator.baseInstaller.install(ctx, file)
421}
422
423func NewTest(hod android.HostOrDeviceSupported) *Module {
424	module, binary := NewBinary(hod)
425	module.multilib = android.MultilibBoth
426	binary.baseInstaller = NewTestInstaller()
427
428	test := &testBinary{
429		testDecorator: testDecorator{
430			linker: binary.baseLinker,
431		},
432		binaryDecorator: binary,
433		baseCompiler:    NewBaseCompiler(),
434	}
435	module.compiler = test
436	module.linker = test
437	module.installer = test
438	return module
439}
440
441type testLibrary struct {
442	testDecorator
443	*libraryDecorator
444}
445
446func (test *testLibrary) linkerProps() []interface{} {
447	return append(test.testDecorator.linkerProps(), test.libraryDecorator.linkerProps()...)
448}
449
450func (test *testLibrary) linkerInit(ctx BaseModuleContext) {
451	test.testDecorator.linkerInit(ctx, test.libraryDecorator.baseLinker)
452	test.libraryDecorator.linkerInit(ctx)
453}
454
455func (test *testLibrary) linkerDeps(ctx DepsContext, deps Deps) Deps {
456	deps = test.testDecorator.linkerDeps(ctx, deps)
457	deps = test.libraryDecorator.linkerDeps(ctx, deps)
458	return deps
459}
460
461func (test *testLibrary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
462	flags = test.libraryDecorator.linkerFlags(ctx, flags)
463	flags = test.testDecorator.linkerFlags(ctx, flags)
464	return flags
465}
466
467func NewTestLibrary(hod android.HostOrDeviceSupported) *Module {
468	module, library := NewLibrary(android.HostAndDeviceSupported)
469	library.baseInstaller = NewTestInstaller()
470	test := &testLibrary{
471		testDecorator: testDecorator{
472			linker: library.baseLinker,
473		},
474		libraryDecorator: library,
475	}
476	module.linker = test
477	return module
478}
479
480type BenchmarkProperties struct {
481	// list of files or filegroup modules that provide data that should be installed alongside
482	// the test
483	Data []string `android:"path"`
484
485	// list of compatibility suites (for example "cts", "vts") that the module should be
486	// installed into.
487	Test_suites []string `android:"arch_variant"`
488
489	// the name of the test configuration (for example "AndroidTest.xml") that should be
490	// installed with the module.
491	Test_config *string `android:"path,arch_variant"`
492
493	// the name of the test configuration template (for example "AndroidTestTemplate.xml") that
494	// should be installed with the module.
495	Test_config_template *string `android:"path,arch_variant"`
496
497	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
498	// with root permission.
499	Require_root *bool
500
501	// Flag to indicate whether or not to create test config automatically. If AndroidTest.xml
502	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
503	// explicitly.
504	Auto_gen_config *bool
505}
506
507type benchmarkDecorator struct {
508	*binaryDecorator
509	Properties BenchmarkProperties
510	data       android.Paths
511	testConfig android.Path
512}
513
514func (benchmark *benchmarkDecorator) linkerInit(ctx BaseModuleContext) {
515	runpath := "../../lib"
516	if ctx.toolchain().Is64Bit() {
517		runpath += "64"
518	}
519	benchmark.baseLinker.dynamicProperties.RunPaths = append(benchmark.baseLinker.dynamicProperties.RunPaths, runpath)
520	benchmark.binaryDecorator.linkerInit(ctx)
521}
522
523func (benchmark *benchmarkDecorator) linkerProps() []interface{} {
524	props := benchmark.binaryDecorator.linkerProps()
525	props = append(props, &benchmark.Properties)
526	return props
527}
528
529func (benchmark *benchmarkDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
530	deps = benchmark.binaryDecorator.linkerDeps(ctx, deps)
531	deps.StaticLibs = append(deps.StaticLibs, "libgoogle-benchmark")
532	return deps
533}
534
535func (benchmark *benchmarkDecorator) install(ctx ModuleContext, file android.Path) {
536	benchmark.data = android.PathsForModuleSrc(ctx, benchmark.Properties.Data)
537
538	var configs []tradefed.Config
539	if Bool(benchmark.Properties.Require_root) {
540		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RootTargetPreparer", nil})
541	}
542	benchmark.testConfig = tradefed.AutoGenNativeBenchmarkTestConfig(ctx, benchmark.Properties.Test_config,
543		benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites, configs, benchmark.Properties.Auto_gen_config)
544
545	benchmark.binaryDecorator.baseInstaller.dir = filepath.Join("benchmarktest", ctx.ModuleName())
546	benchmark.binaryDecorator.baseInstaller.dir64 = filepath.Join("benchmarktest64", ctx.ModuleName())
547	benchmark.binaryDecorator.baseInstaller.install(ctx, file)
548}
549
550func NewBenchmark(hod android.HostOrDeviceSupported) *Module {
551	module, binary := NewBinary(hod)
552	module.multilib = android.MultilibBoth
553	binary.baseInstaller = NewBaseInstaller("benchmarktest", "benchmarktest64", InstallInData)
554
555	benchmark := &benchmarkDecorator{
556		binaryDecorator: binary,
557	}
558	module.linker = benchmark
559	module.installer = benchmark
560	return module
561}
562