// Copyright 2017 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 python import ( "fmt" "github.com/google/blueprint/proptools" "android/soong/android" "android/soong/tradefed" ) // This file contains the module types for building Python test. func init() { registerPythonTestComponents(android.InitRegistrationContext) } func registerPythonTestComponents(ctx android.RegistrationContext) { ctx.RegisterModuleType("python_test_host", PythonTestHostFactory) ctx.RegisterModuleType("python_test", PythonTestFactory) } func NewTest(hod android.HostOrDeviceSupported) *PythonTestModule { return &PythonTestModule{PythonBinaryModule: *NewBinary(hod)} } func PythonTestHostFactory() android.Module { return NewTest(android.HostSupported).init() } func PythonTestFactory() android.Module { module := NewTest(android.HostAndDeviceSupported) module.multilib = android.MultilibBoth return module.init() } type TestProperties struct { // the name of the test configuration (for example "AndroidTest.xml") that should be // installed with the module. Test_config *string `android:"path,arch_variant"` // the name of the test configuration template (for example "AndroidTestTemplate.xml") that // should be installed with the module. Test_config_template *string `android:"path,arch_variant"` // list of files or filegroup modules that provide data that should be installed alongside // the test Data []string `android:"path,arch_variant"` // list of java modules that provide data that should be installed alongside the test. Java_data []string // Test options. Test_options TestOptions // list of device binary modules that should be installed alongside the test // This property adds 64bit AND 32bit variants of the dependency Data_device_bins_both []string `android:"arch_variant"` } type TestOptions struct { android.CommonTestOptions // Runner for the test. Supports "tradefed" and "mobly" (for multi-device tests). Default is "tradefed". Runner *string // Metadata to describe the test configuration. Metadata []Metadata } type Metadata struct { Name string Value string } type PythonTestModule struct { PythonBinaryModule testProperties TestProperties testConfig android.Path data []android.DataPath } func (p *PythonTestModule) init() android.Module { p.AddProperties(&p.properties, &p.protoProperties) p.AddProperties(&p.binaryProperties) p.AddProperties(&p.testProperties) android.InitAndroidArchModule(p, p.hod, p.multilib) android.InitDefaultableModule(p) android.InitBazelModule(p) if p.isTestHost() && p.testProperties.Test_options.Unit_test == nil { p.testProperties.Test_options.Unit_test = proptools.BoolPtr(true) } return p } func (p *PythonTestModule) isTestHost() bool { return p.hod == android.HostSupported } var dataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"} // python_test_host DepsMutator uses this method to add multilib dependencies of // data_device_bin_both func (p *PythonTestModule) addDataDeviceBinsDeps(ctx android.BottomUpMutatorContext, filter string) { if len(p.testProperties.Data_device_bins_both) < 1 { return } var maybeAndroidTarget *android.Target androidTargetList := android.FirstTarget(ctx.Config().Targets[android.Android], filter) if len(androidTargetList) > 0 { maybeAndroidTarget = &androidTargetList[0] } if maybeAndroidTarget != nil { ctx.AddFarVariationDependencies( maybeAndroidTarget.Variations(), dataDeviceBinsTag, p.testProperties.Data_device_bins_both..., ) } } func (p *PythonTestModule) DepsMutator(ctx android.BottomUpMutatorContext) { p.PythonBinaryModule.DepsMutator(ctx) if p.isTestHost() { p.addDataDeviceBinsDeps(ctx, "lib32") p.addDataDeviceBinsDeps(ctx, "lib64") } } func (p *PythonTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { // We inherit from only the library's GenerateAndroidBuildActions, and then // just use buildBinary() so that the binary is not installed into the location // it would be for regular binaries. p.PythonLibraryModule.GenerateAndroidBuildActions(ctx) p.buildBinary(ctx) var configs []tradefed.Option for _, metadata := range p.testProperties.Test_options.Metadata { configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: metadata.Name, Value: metadata.Value}) } runner := proptools.StringDefault(p.testProperties.Test_options.Runner, "tradefed") if runner == "tradefed" { p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{ TestConfigProp: p.testProperties.Test_config, TestConfigTemplateProp: p.testProperties.Test_config_template, TestSuites: p.binaryProperties.Test_suites, OptionsForAutogenerated: configs, AutoGenConfig: p.binaryProperties.Auto_gen_config, DeviceTemplate: "${PythonBinaryHostTestConfigTemplate}", HostTemplate: "${PythonBinaryHostTestConfigTemplate}", }) } else if runner == "mobly" { if p.testProperties.Test_config != nil || p.testProperties.Test_config_template != nil || p.binaryProperties.Auto_gen_config != nil { panic(fmt.Errorf("cannot set test_config, test_config_template or auto_gen_config for mobly test")) } for _, testSuite := range p.binaryProperties.Test_suites { if testSuite == "cts" { configs = append(configs, tradefed.Option{Name: "test-suite-tag", Value: "cts"}) break } } p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{ OptionsForAutogenerated: configs, DeviceTemplate: "${PythonBinaryHostMoblyTestConfigTemplate}", HostTemplate: "${PythonBinaryHostMoblyTestConfigTemplate}", }) } else { panic(fmt.Errorf("unknown python test runner '%s', should be 'tradefed' or 'mobly'", runner)) } p.installedDest = ctx.InstallFile(installDir(ctx, "nativetest", "nativetest64", ctx.ModuleName()), p.installSource.Base(), p.installSource) for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Data) { p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath}) } if p.isTestHost() && len(p.testProperties.Data_device_bins_both) > 0 { ctx.VisitDirectDepsWithTag(dataDeviceBinsTag, func(dep android.Module) { p.data = append(p.data, android.DataPath{SrcPath: android.OutputFileForModule(ctx, dep, "")}) }) } // Emulate the data property for java_data dependencies. for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) { for _, javaDataSrcPath := range android.OutputFilesForModule(ctx, javaData, "") { p.data = append(p.data, android.DataPath{SrcPath: javaDataSrcPath}) } } } func (p *PythonTestModule) AndroidMkEntries() []android.AndroidMkEntries { entriesList := p.PythonBinaryModule.AndroidMkEntries() if len(entriesList) != 1 { panic("Expected 1 entry") } entries := &entriesList[0] entries.Class = "NATIVE_TESTS" entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { //entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...) if p.testConfig != nil { entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String()) } entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true)) entries.AddStrings("LOCAL_TEST_DATA", android.AndroidMkDataPaths(p.data)...) p.testProperties.Test_options.SetAndroidMkEntries(entries) }) return entriesList }