1// Copyright 2018 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 tradefed 16 17import ( 18 "fmt" 19 "strings" 20 21 "github.com/google/blueprint" 22 "github.com/google/blueprint/proptools" 23 24 "android/soong/android" 25) 26 27const test_xml_indent = " " 28 29func getTestConfigTemplate(ctx android.ModuleContext, prop *string) android.OptionalPath { 30 return ctx.ExpandOptionalSource(prop, "test_config_template") 31} 32 33func getTestConfig(ctx android.ModuleContext, prop *string) android.Path { 34 if p := ctx.ExpandOptionalSource(prop, "test_config"); p.Valid() { 35 return p.Path() 36 } else if p := android.ExistentPathForSource(ctx, ctx.ModuleDir(), "AndroidTest.xml"); p.Valid() { 37 return p.Path() 38 } 39 return nil 40} 41 42var autogenTestConfig = pctx.StaticRule("autogenTestConfig", blueprint.RuleParams{ 43 Command: "sed 's&{MODULE}&${name}&g;s&{EXTRA_CONFIGS}&'${extraConfigs}'&g;s&{OUTPUT_FILENAME}&'${outputFileName}'&g;s&{TEST_INSTALL_BASE}&'${testInstallBase}'&g' $template > $out", 44 CommandDeps: []string{"$template"}, 45}, "name", "template", "extraConfigs", "outputFileName", "testInstallBase") 46 47func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string, autoGenConfig *bool, testConfigTemplateProp *string) (path android.Path, autogenPath android.WritablePath) { 48 p := getTestConfig(ctx, prop) 49 if !Bool(autoGenConfig) && p != nil { 50 return p, nil 51 } else if BoolDefault(autoGenConfig, true) && (!android.InList("cts", testSuites) || testConfigTemplateProp != nil) { 52 outputFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".config") 53 return nil, outputFile 54 } else { 55 // CTS modules can be used for test data, so test config files must be 56 // explicitly created using AndroidTest.xml or test_config_template. 57 return nil, nil 58 } 59} 60 61type Config interface { 62 Config() string 63} 64 65type Option struct { 66 Name string 67 Key string 68 Value string 69} 70 71var _ Config = Option{} 72 73func (o Option) Config() string { 74 if o.Key != "" { 75 return fmt.Sprintf(`<option name="%s" key="%s" value="%s" />`, o.Name, o.Key, o.Value) 76 } 77 return fmt.Sprintf(`<option name="%s" value="%s" />`, o.Name, o.Value) 78} 79 80// It can be a template of object or target_preparer. 81type Object struct { 82 // Set it as a target_preparer if object type == "target_preparer". 83 Type string 84 Class string 85 Options []Option 86} 87 88var _ Config = Object{} 89 90func (ob Object) Config() string { 91 var optionStrings []string 92 for _, option := range ob.Options { 93 optionStrings = append(optionStrings, option.Config()) 94 } 95 var options string 96 if len(ob.Options) == 0 { 97 options = "" 98 } else { 99 optionDelimiter := fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent) 100 options = optionDelimiter + strings.Join(optionStrings, optionDelimiter) 101 } 102 if ob.Type == "target_preparer" { 103 return fmt.Sprintf(`<target_preparer class="%s">%s\n%s</target_preparer>`, ob.Class, options, test_xml_indent) 104 } else { 105 return fmt.Sprintf(`<object type="%s" class="%s">%s\n%s</object>`, ob.Type, ob.Class, options, test_xml_indent) 106 } 107 108} 109 110func autogenTemplate(ctx android.ModuleContext, output android.WritablePath, template string, configs []Config, testInstallBase string) { 111 autogenTemplateWithNameAndOutputFile(ctx, ctx.ModuleName(), output, template, configs, "", testInstallBase) 112} 113 114func autogenTemplateWithName(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config, testInstallBase string) { 115 autogenTemplateWithNameAndOutputFile(ctx, name, output, template, configs, "", testInstallBase) 116} 117 118func autogenTemplateWithNameAndOutputFile(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config, outputFileName string, testInstallBase string) { 119 var configStrings []string 120 for _, config := range configs { 121 configStrings = append(configStrings, config.Config()) 122 } 123 extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent)) 124 extraConfigs = proptools.NinjaAndShellEscape(extraConfigs) 125 126 ctx.Build(pctx, android.BuildParams{ 127 Rule: autogenTestConfig, 128 Description: "test config", 129 Output: output, 130 Args: map[string]string{ 131 "name": name, 132 "template": template, 133 "extraConfigs": extraConfigs, 134 "outputFileName": outputFileName, 135 "testInstallBase": testInstallBase, 136 }, 137 }) 138} 139 140func AutoGenNativeTestConfig(ctx android.ModuleContext, testConfigProp *string, 141 testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool, testInstallBase string) android.Path { 142 143 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 144 if autogenPath != nil { 145 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 146 if templatePath.Valid() { 147 autogenTemplate(ctx, autogenPath, templatePath.String(), config, testInstallBase) 148 } else { 149 if ctx.Device() { 150 autogenTemplate(ctx, autogenPath, "${NativeTestConfigTemplate}", config, testInstallBase) 151 } else { 152 autogenTemplate(ctx, autogenPath, "${NativeHostTestConfigTemplate}", config, testInstallBase) 153 } 154 } 155 return autogenPath 156 } 157 return path 158} 159 160func AutoGenShellTestConfig(ctx android.ModuleContext, testConfigProp *string, 161 testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool, outputFileName string) android.Path { 162 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 163 if autogenPath != nil { 164 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 165 if templatePath.Valid() { 166 autogenTemplateWithNameAndOutputFile(ctx, ctx.ModuleName(), autogenPath, templatePath.String(), config, outputFileName, "") 167 } else { 168 autogenTemplateWithNameAndOutputFile(ctx, ctx.ModuleName(), autogenPath, "${ShellTestConfigTemplate}", config, outputFileName, "") 169 } 170 return autogenPath 171 } 172 return path 173} 174 175func AutoGenNativeBenchmarkTestConfig(ctx android.ModuleContext, testConfigProp *string, 176 testConfigTemplateProp *string, testSuites []string, configs []Config, autoGenConfig *bool) android.Path { 177 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 178 if autogenPath != nil { 179 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 180 if templatePath.Valid() { 181 autogenTemplate(ctx, autogenPath, templatePath.String(), configs, "") 182 } else { 183 autogenTemplate(ctx, autogenPath, "${NativeBenchmarkTestConfigTemplate}", configs, "") 184 } 185 return autogenPath 186 } 187 return path 188} 189 190func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, 191 testSuites []string, autoGenConfig *bool, unitTest *bool) android.Path { 192 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 193 if autogenPath != nil { 194 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 195 if templatePath.Valid() { 196 autogenTemplate(ctx, autogenPath, templatePath.String(), nil, "") 197 } else { 198 if ctx.Device() { 199 autogenTemplate(ctx, autogenPath, "${JavaTestConfigTemplate}", nil, "") 200 } else { 201 if Bool(unitTest) { 202 autogenTemplate(ctx, autogenPath, "${JavaHostUnitTestConfigTemplate}", nil, "") 203 } else { 204 autogenTemplate(ctx, autogenPath, "${JavaHostTestConfigTemplate}", nil, "") 205 } 206 } 207 } 208 return autogenPath 209 } 210 return path 211} 212 213func AutoGenPythonBinaryHostTestConfig(ctx android.ModuleContext, testConfigProp *string, 214 testConfigTemplateProp *string, testSuites []string, autoGenConfig *bool) android.Path { 215 216 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 217 if autogenPath != nil { 218 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 219 if templatePath.Valid() { 220 autogenTemplate(ctx, autogenPath, templatePath.String(), nil, "") 221 } else { 222 autogenTemplate(ctx, autogenPath, "${PythonBinaryHostTestConfigTemplate}", nil, "") 223 } 224 return autogenPath 225 } 226 return path 227} 228 229func AutoGenRustTestConfig(ctx android.ModuleContext, testConfigProp *string, 230 testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool) android.Path { 231 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 232 if autogenPath != nil { 233 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 234 if templatePath.Valid() { 235 autogenTemplate(ctx, autogenPath, templatePath.String(), config, "") 236 } else { 237 if ctx.Device() { 238 autogenTemplate(ctx, autogenPath, "${RustDeviceTestConfigTemplate}", config, "") 239 } else { 240 autogenTemplate(ctx, autogenPath, "${RustHostTestConfigTemplate}", config, "") 241 } 242 } 243 return autogenPath 244 } 245 return path 246} 247 248func AutoGenRustBenchmarkConfig(ctx android.ModuleContext, testConfigProp *string, 249 testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool) android.Path { 250 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 251 if autogenPath != nil { 252 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 253 if templatePath.Valid() { 254 autogenTemplate(ctx, autogenPath, templatePath.String(), config, "") 255 } else { 256 if ctx.Device() { 257 autogenTemplate(ctx, autogenPath, "${RustDeviceBenchmarkConfigTemplate}", config, "") 258 } else { 259 autogenTemplate(ctx, autogenPath, "${RustHostBenchmarkConfigTemplate}", config, "") 260 } 261 } 262 return autogenPath 263 } 264 return path 265} 266 267func AutoGenRobolectricTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, 268 testSuites []string, autoGenConfig *bool) android.Path { 269 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 270 if autogenPath != nil { 271 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 272 if templatePath.Valid() { 273 autogenTemplate(ctx, autogenPath, templatePath.String(), nil, "") 274 } else { 275 autogenTemplate(ctx, autogenPath, "${RobolectricTestConfigTemplate}", nil, "") 276 } 277 return autogenPath 278 } 279 return path 280} 281 282var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", blueprint.RuleParams{ 283 Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs}", 284 CommandDeps: []string{ 285 "${AutoGenTestConfigScript}", 286 "${EmptyTestConfig}", 287 "$template", 288 }, 289}, "name", "template", "extraConfigs") 290 291func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string, 292 testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool, configs []Config) android.Path { 293 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 294 var configStrings []string 295 if autogenPath != nil { 296 template := "${InstrumentationTestConfigTemplate}" 297 moduleTemplate := getTestConfigTemplate(ctx, testConfigTemplateProp) 298 if moduleTemplate.Valid() { 299 template = moduleTemplate.String() 300 } 301 for _, config := range configs { 302 configStrings = append(configStrings, config.Config()) 303 } 304 extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent)) 305 extraConfigs = fmt.Sprintf("--extra-configs '%s'", extraConfigs) 306 307 ctx.Build(pctx, android.BuildParams{ 308 Rule: autogenInstrumentationTest, 309 Description: "test config", 310 Input: manifest, 311 Output: autogenPath, 312 Args: map[string]string{ 313 "name": ctx.ModuleName(), 314 "template": template, 315 "extraConfigs": extraConfigs, 316 }, 317 }) 318 return autogenPath 319 } 320 return path 321} 322 323var Bool = proptools.Bool 324var BoolDefault = proptools.BoolDefault 325