1// Copyright 2017 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 python 16 17import ( 18 "fmt" 19 20 "github.com/google/blueprint/proptools" 21 22 "android/soong/android" 23 "android/soong/tradefed" 24) 25 26// This file contains the module types for building Python test. 27 28func init() { 29 registerPythonTestComponents(android.InitRegistrationContext) 30} 31 32func registerPythonTestComponents(ctx android.RegistrationContext) { 33 ctx.RegisterModuleType("python_test_host", PythonTestHostFactory) 34 ctx.RegisterModuleType("python_test", PythonTestFactory) 35} 36 37func NewTest(hod android.HostOrDeviceSupported) *PythonTestModule { 38 p := &PythonTestModule{PythonBinaryModule: *NewBinary(hod)} 39 p.sourceProperties = android.SourceProperties{Test_only: proptools.BoolPtr(true), Top_level_test_target: true} 40 return p 41} 42 43func PythonTestHostFactory() android.Module { 44 return NewTest(android.HostSupported).init() 45} 46 47func PythonTestFactory() android.Module { 48 module := NewTest(android.HostAndDeviceSupported) 49 module.multilib = android.MultilibBoth 50 return module.init() 51} 52 53type TestProperties struct { 54 // the name of the test configuration (for example "AndroidTest.xml") that should be 55 // installed with the module. 56 Test_config *string `android:"path,arch_variant"` 57 58 // the name of the test configuration template (for example "AndroidTestTemplate.xml") that 59 // should be installed with the module. 60 Test_config_template *string `android:"path,arch_variant"` 61 62 // list of files or filegroup modules that provide data that should be installed alongside 63 // the test 64 Data []string `android:"path,arch_variant"` 65 66 // Same as data, but will add dependencies on modules using the device's os variation and 67 // the common arch variation. Useful for a host test that wants to embed a module built for 68 // device. 69 Device_common_data []string `android:"path_device_common"` 70 71 // Same as data, but will add dependencies on modules via a device os variation and the 72 // device's first supported arch's variation. Useful for a host test that wants to embed a 73 // module built for device. 74 Device_first_data []string `android:"path_device_first"` 75 76 // list of java modules that provide data that should be installed alongside the test. 77 Java_data []string 78 79 // Test options. 80 Test_options TestOptions 81 82 // list of device binary modules that should be installed alongside the test 83 // This property adds 64bit AND 32bit variants of the dependency 84 Data_device_bins_both []string `android:"arch_variant"` 85} 86 87type TestOptions struct { 88 android.CommonTestOptions 89 90 // Runner for the test. Supports "tradefed" and "mobly" (for multi-device tests). Default is "tradefed". 91 Runner *string 92 93 // Metadata to describe the test configuration. 94 Metadata []Metadata 95} 96 97type Metadata struct { 98 Name string 99 Value string 100} 101 102type PythonTestModule struct { 103 PythonBinaryModule 104 105 testProperties TestProperties 106 testConfig android.Path 107 data []android.DataPath 108} 109 110func (p *PythonTestModule) init() android.Module { 111 p.AddProperties(&p.properties, &p.protoProperties) 112 p.AddProperties(&p.binaryProperties) 113 p.AddProperties(&p.testProperties) 114 android.InitAndroidArchModule(p, p.hod, p.multilib) 115 android.InitDefaultableModule(p) 116 if p.isTestHost() && p.testProperties.Test_options.Unit_test == nil { 117 p.testProperties.Test_options.Unit_test = proptools.BoolPtr(true) 118 } 119 return p 120} 121 122func (p *PythonTestModule) isTestHost() bool { 123 return p.hod == android.HostSupported 124} 125 126var dataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"} 127 128// python_test_host DepsMutator uses this method to add multilib dependencies of 129// data_device_bin_both 130func (p *PythonTestModule) addDataDeviceBinsDeps(ctx android.BottomUpMutatorContext, filter string) { 131 if len(p.testProperties.Data_device_bins_both) < 1 { 132 return 133 } 134 135 var maybeAndroidTarget *android.Target 136 androidTargetList := android.FirstTarget(ctx.Config().Targets[android.Android], filter) 137 if len(androidTargetList) > 0 { 138 maybeAndroidTarget = &androidTargetList[0] 139 } 140 141 if maybeAndroidTarget != nil { 142 ctx.AddFarVariationDependencies( 143 maybeAndroidTarget.Variations(), 144 dataDeviceBinsTag, 145 p.testProperties.Data_device_bins_both..., 146 ) 147 } 148} 149 150func (p *PythonTestModule) DepsMutator(ctx android.BottomUpMutatorContext) { 151 p.PythonBinaryModule.DepsMutator(ctx) 152 if p.isTestHost() { 153 p.addDataDeviceBinsDeps(ctx, "lib32") 154 p.addDataDeviceBinsDeps(ctx, "lib64") 155 } 156} 157 158func (p *PythonTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 159 // We inherit from only the library's GenerateAndroidBuildActions, and then 160 // just use buildBinary() so that the binary is not installed into the location 161 // it would be for regular binaries. 162 p.PythonLibraryModule.GenerateAndroidBuildActions(ctx) 163 p.buildBinary(ctx) 164 165 var configs []tradefed.Option 166 for _, metadata := range p.testProperties.Test_options.Metadata { 167 configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: metadata.Name, Value: metadata.Value}) 168 } 169 170 runner := proptools.StringDefault(p.testProperties.Test_options.Runner, "tradefed") 171 template := "${PythonBinaryHostTestConfigTemplate}" 172 if runner == "mobly" { 173 // Add tag to enable Atest mobly runner 174 if !android.InList("mobly", p.testProperties.Test_options.Tags) { 175 p.testProperties.Test_options.Tags = append(p.testProperties.Test_options.Tags, "mobly") 176 } 177 template = "${PythonBinaryHostMoblyTestConfigTemplate}" 178 } else if runner != "tradefed" { 179 panic(fmt.Errorf("unknown python test runner '%s', should be 'tradefed' or 'mobly'", runner)) 180 } 181 p.testConfig = tradefed.AutoGenTestConfig(ctx, tradefed.AutoGenTestConfigOptions{ 182 TestConfigProp: p.testProperties.Test_config, 183 TestConfigTemplateProp: p.testProperties.Test_config_template, 184 TestSuites: p.binaryProperties.Test_suites, 185 OptionsForAutogenerated: configs, 186 AutoGenConfig: p.binaryProperties.Auto_gen_config, 187 DeviceTemplate: template, 188 HostTemplate: template, 189 }) 190 191 for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Data) { 192 p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath}) 193 } 194 for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Device_common_data) { 195 p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath}) 196 } 197 for _, dataSrcPath := range android.PathsForModuleSrc(ctx, p.testProperties.Device_first_data) { 198 p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath}) 199 } 200 201 if p.isTestHost() && len(p.testProperties.Data_device_bins_both) > 0 { 202 ctx.VisitDirectDepsProxyWithTag(dataDeviceBinsTag, func(dep android.ModuleProxy) { 203 p.data = append(p.data, android.DataPath{SrcPath: android.OutputFileForModule(ctx, dep, "")}) 204 }) 205 } 206 207 // Emulate the data property for java_data dependencies. 208 for _, javaData := range ctx.GetDirectDepsProxyWithTag(javaDataTag) { 209 for _, javaDataSrcPath := range android.OutputFilesForModule(ctx, javaData, "") { 210 p.data = append(p.data, android.DataPath{SrcPath: javaDataSrcPath}) 211 } 212 } 213 214 installDir := installDir(ctx, "nativetest", "nativetest64", ctx.ModuleName()) 215 installedData := ctx.InstallTestData(installDir, p.data) 216 p.installedDest = ctx.InstallFile(installDir, p.installSource.Base(), p.installSource, installedData...) 217 218 // TODO: Remove the special case for kati 219 if !ctx.Config().KatiEnabled() { 220 // Install the test config in testcases/ directory for atest. 221 // Install configs in the root of $PRODUCT_OUT/testcases/$module 222 testCases := android.PathForModuleInPartitionInstall(ctx, "testcases", ctx.ModuleName()) 223 if ctx.PrimaryArch() { 224 if p.testConfig != nil { 225 ctx.InstallFile(testCases, ctx.ModuleName()+".config", p.testConfig) 226 } 227 dynamicConfig := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "DynamicConfig.xml") 228 if dynamicConfig.Valid() { 229 ctx.InstallFile(testCases, ctx.ModuleName()+".dynamic", dynamicConfig.Path()) 230 } 231 } 232 // Install tests and data in arch specific subdir $PRODUCT_OUT/testcases/$module/$arch 233 testCases = testCases.Join(ctx, ctx.Target().Arch.ArchType.String()) 234 installedData := ctx.InstallTestData(testCases, p.data) 235 ctx.InstallFile(testCases, p.installSource.Base(), p.installSource, installedData...) 236 } 237 238 moduleInfoJSON := ctx.ModuleInfoJSON() 239 moduleInfoJSON.Class = []string{"NATIVE_TESTS"} 240 if len(p.binaryProperties.Test_suites) > 0 { 241 moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, p.binaryProperties.Test_suites...) 242 } else { 243 moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "null-suite") 244 } 245 if p.testConfig != nil { 246 moduleInfoJSON.TestConfig = append(moduleInfoJSON.TestConfig, p.testConfig.String()) 247 } 248 if _, ok := p.testConfig.(android.WritablePath); ok { 249 moduleInfoJSON.AutoTestConfig = []string{"true"} 250 } 251 moduleInfoJSON.TestOptionsTags = append(moduleInfoJSON.TestOptionsTags, p.testProperties.Test_options.Tags...) 252 moduleInfoJSON.Dependencies = append(moduleInfoJSON.Dependencies, p.androidMkSharedLibs...) 253 moduleInfoJSON.SharedLibs = append(moduleInfoJSON.Dependencies, p.androidMkSharedLibs...) 254 moduleInfoJSON.SystemSharedLibs = []string{"none"} 255 if proptools.Bool(p.testProperties.Test_options.Unit_test) { 256 moduleInfoJSON.IsUnitTest = "true" 257 if p.isTestHost() { 258 moduleInfoJSON.CompatibilitySuites = append(moduleInfoJSON.CompatibilitySuites, "host-unit-tests") 259 } 260 } 261} 262 263func (p *PythonTestModule) AndroidMkEntries() []android.AndroidMkEntries { 264 entriesList := p.PythonBinaryModule.AndroidMkEntries() 265 if len(entriesList) != 1 { 266 panic("Expected 1 entry") 267 } 268 entries := &entriesList[0] 269 270 entries.Class = "NATIVE_TESTS" 271 272 entries.ExtraEntries = append(entries.ExtraEntries, 273 func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) { 274 //entries.AddCompatibilityTestSuites(p.binaryProperties.Test_suites...) 275 if p.testConfig != nil { 276 entries.SetString("LOCAL_FULL_TEST_CONFIG", p.testConfig.String()) 277 } 278 279 // ATS 2.0 is the test harness for mobly tests and the test config is for ATS 2.0. 280 // Add "v2" suffix to test config name to distinguish it from the config for TF. 281 if proptools.String(p.testProperties.Test_options.Runner) == "mobly" { 282 entries.SetString("LOCAL_TEST_CONFIG_SUFFIX", "v2") 283 } 284 285 entries.SetBoolIfTrue("LOCAL_DISABLE_AUTO_GENERATE_TEST_CONFIG", !BoolDefault(p.binaryProperties.Auto_gen_config, true)) 286 287 p.testProperties.Test_options.SetAndroidMkEntries(entries) 288 }) 289 290 return entriesList 291} 292