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' $template > $out", 44 CommandDeps: []string{"$template"}, 45}, "name", "template", "extraConfigs", "outputFileName") 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) { 111 autogenTemplateWithNameAndOutputFile(ctx, ctx.ModuleName(), output, template, configs, "") 112} 113 114func autogenTemplateWithName(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config) { 115 autogenTemplateWithNameAndOutputFile(ctx, name, output, template, configs, "") 116} 117 118func autogenTemplateWithNameAndOutputFile(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config, outputFileName 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 }, 136 }) 137} 138 139func AutoGenNativeTestConfig(ctx android.ModuleContext, testConfigProp *string, 140 testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool) android.Path { 141 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 142 if autogenPath != nil { 143 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 144 if templatePath.Valid() { 145 autogenTemplate(ctx, autogenPath, templatePath.String(), config) 146 } else { 147 if ctx.Device() { 148 autogenTemplate(ctx, autogenPath, "${NativeTestConfigTemplate}", config) 149 } else { 150 autogenTemplate(ctx, autogenPath, "${NativeHostTestConfigTemplate}", config) 151 } 152 } 153 return autogenPath 154 } 155 return path 156} 157 158func AutoGenShellTestConfig(ctx android.ModuleContext, testConfigProp *string, 159 testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool, outputFileName string) android.Path { 160 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 161 if autogenPath != nil { 162 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 163 if templatePath.Valid() { 164 autogenTemplateWithNameAndOutputFile(ctx, ctx.ModuleName(), autogenPath, templatePath.String(), config, outputFileName) 165 } else { 166 autogenTemplateWithNameAndOutputFile(ctx, ctx.ModuleName(), autogenPath, "${ShellTestConfigTemplate}", config, outputFileName) 167 } 168 return autogenPath 169 } 170 return path 171} 172 173func AutoGenNativeBenchmarkTestConfig(ctx android.ModuleContext, testConfigProp *string, 174 testConfigTemplateProp *string, testSuites []string, configs []Config, autoGenConfig *bool) android.Path { 175 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 176 if autogenPath != nil { 177 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 178 if templatePath.Valid() { 179 autogenTemplate(ctx, autogenPath, templatePath.String(), configs) 180 } else { 181 autogenTemplate(ctx, autogenPath, "${NativeBenchmarkTestConfigTemplate}", configs) 182 } 183 return autogenPath 184 } 185 return path 186} 187 188func AutoGenJavaTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, 189 testSuites []string, autoGenConfig *bool) android.Path { 190 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 191 if autogenPath != nil { 192 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 193 if templatePath.Valid() { 194 autogenTemplate(ctx, autogenPath, templatePath.String(), nil) 195 } else { 196 if ctx.Device() { 197 autogenTemplate(ctx, autogenPath, "${JavaTestConfigTemplate}", nil) 198 } else { 199 autogenTemplate(ctx, autogenPath, "${JavaHostTestConfigTemplate}", nil) 200 } 201 } 202 return autogenPath 203 } 204 return path 205} 206 207func AutoGenPythonBinaryHostTestConfig(ctx android.ModuleContext, testConfigProp *string, 208 testConfigTemplateProp *string, testSuites []string, autoGenConfig *bool) android.Path { 209 210 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 211 if autogenPath != nil { 212 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 213 if templatePath.Valid() { 214 autogenTemplate(ctx, autogenPath, templatePath.String(), nil) 215 } else { 216 autogenTemplate(ctx, autogenPath, "${PythonBinaryHostTestConfigTemplate}", nil) 217 } 218 return autogenPath 219 } 220 return path 221} 222 223func AutoGenRustTestConfig(ctx android.ModuleContext, testConfigProp *string, 224 testConfigTemplateProp *string, testSuites []string, config []Config, autoGenConfig *bool) android.Path { 225 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 226 if autogenPath != nil { 227 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 228 if templatePath.Valid() { 229 autogenTemplate(ctx, autogenPath, templatePath.String(), config) 230 } else { 231 if ctx.Device() { 232 autogenTemplate(ctx, autogenPath, "${RustDeviceTestConfigTemplate}", config) 233 } else { 234 autogenTemplate(ctx, autogenPath, "${RustHostTestConfigTemplate}", config) 235 } 236 } 237 return autogenPath 238 } 239 return path 240} 241 242func AutoGenRobolectricTestConfig(ctx android.ModuleContext, testConfigProp *string, testConfigTemplateProp *string, 243 testSuites []string, autoGenConfig *bool) android.Path { 244 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 245 if autogenPath != nil { 246 templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp) 247 if templatePath.Valid() { 248 autogenTemplate(ctx, autogenPath, templatePath.String(), nil) 249 } else { 250 autogenTemplate(ctx, autogenPath, "${RobolectricTestConfigTemplate}", nil) 251 } 252 return autogenPath 253 } 254 return path 255} 256 257var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", blueprint.RuleParams{ 258 Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs}", 259 CommandDeps: []string{ 260 "${AutoGenTestConfigScript}", 261 "${EmptyTestConfig}", 262 "$template", 263 }, 264}, "name", "template", "extraConfigs") 265 266func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string, 267 testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool, configs []Config) android.Path { 268 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 269 var configStrings []string 270 if autogenPath != nil { 271 template := "${InstrumentationTestConfigTemplate}" 272 moduleTemplate := getTestConfigTemplate(ctx, testConfigTemplateProp) 273 if moduleTemplate.Valid() { 274 template = moduleTemplate.String() 275 } 276 for _, config := range configs { 277 configStrings = append(configStrings, config.Config()) 278 } 279 extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent)) 280 extraConfigs = fmt.Sprintf("--extra-configs '%s'", extraConfigs) 281 282 ctx.Build(pctx, android.BuildParams{ 283 Rule: autogenInstrumentationTest, 284 Description: "test config", 285 Input: manifest, 286 Output: autogenPath, 287 Args: map[string]string{ 288 "name": ctx.ModuleName(), 289 "template": template, 290 "extraConfigs": extraConfigs, 291 }, 292 }) 293 return autogenPath 294 } 295 return path 296} 297 298var Bool = proptools.Bool 299var BoolDefault = proptools.BoolDefault 300