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&{EXTRA_TEST_RUNNER_CONFIGS}&'${extraTestRunnerConfigs}'&g;s&{OUTPUT_FILENAME}&'${outputFileName}'&g;s&{TEST_INSTALL_BASE}&'${testInstallBase}'&g' $template > $out", 44 CommandDeps: []string{"$template"}, 45}, "name", "template", "extraConfigs", "outputFileName", "testInstallBase", "extraTestRunnerConfigs") 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, name string, output android.WritablePath, template string, configs []Config, testRunnerConfigs []Option, outputFileName string, testInstallBase string) { 111 if template == "" { 112 ctx.ModuleErrorf("Empty template") 113 } 114 var configStrings []string 115 for _, config := range configs { 116 configStrings = append(configStrings, config.Config()) 117 } 118 extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent)) 119 extraConfigs = proptools.NinjaAndShellEscape(extraConfigs) 120 121 var testRunnerConfigStrings []string 122 for _, config := range testRunnerConfigs { 123 testRunnerConfigStrings = append(testRunnerConfigStrings, config.Config()) 124 } 125 extraTestRunnerConfigs := strings.Join(testRunnerConfigStrings, fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent)) 126 if len(extraTestRunnerConfigs) > 0 { 127 extraTestRunnerConfigs += fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent) 128 } 129 extraTestRunnerConfigs = proptools.NinjaAndShellEscape(extraTestRunnerConfigs) 130 131 ctx.Build(pctx, android.BuildParams{ 132 Rule: autogenTestConfig, 133 Description: "test config", 134 Output: output, 135 Args: map[string]string{ 136 "name": name, 137 "template": template, 138 "extraConfigs": extraConfigs, 139 "outputFileName": outputFileName, 140 "testInstallBase": testInstallBase, 141 "extraTestRunnerConfigs": extraTestRunnerConfigs, 142 }, 143 }) 144} 145 146// AutoGenTestConfigOptions is used so that we can supply many optional 147// arguments to the AutoGenTestConfig function. 148type AutoGenTestConfigOptions struct { 149 Name string 150 OutputFileName string 151 TestConfigProp *string 152 TestConfigTemplateProp *string 153 TestSuites []string 154 Config []Config 155 OptionsForAutogenerated []Option 156 TestRunnerOptions []Option 157 AutoGenConfig *bool 158 UnitTest *bool 159 TestInstallBase string 160 DeviceTemplate string 161 HostTemplate string 162 HostUnitTestTemplate string 163} 164 165func AutoGenTestConfig(ctx android.ModuleContext, options AutoGenTestConfigOptions) android.Path { 166 configs := append([]Config{}, options.Config...) 167 for _, c := range options.OptionsForAutogenerated { 168 configs = append(configs, c) 169 } 170 name := options.Name 171 if name == "" { 172 name = ctx.ModuleName() 173 } 174 path, autogenPath := testConfigPath(ctx, options.TestConfigProp, options.TestSuites, options.AutoGenConfig, options.TestConfigTemplateProp) 175 if autogenPath != nil { 176 templatePath := getTestConfigTemplate(ctx, options.TestConfigTemplateProp) 177 if templatePath.Valid() { 178 autogenTemplate(ctx, name, autogenPath, templatePath.String(), configs, options.TestRunnerOptions, options.OutputFileName, options.TestInstallBase) 179 } else { 180 if ctx.Device() { 181 autogenTemplate(ctx, name, autogenPath, options.DeviceTemplate, configs, options.TestRunnerOptions, options.OutputFileName, options.TestInstallBase) 182 } else { 183 if Bool(options.UnitTest) { 184 autogenTemplate(ctx, name, autogenPath, options.HostUnitTestTemplate, configs, options.TestRunnerOptions, options.OutputFileName, options.TestInstallBase) 185 } else { 186 autogenTemplate(ctx, name, autogenPath, options.HostTemplate, configs, options.TestRunnerOptions, options.OutputFileName, options.TestInstallBase) 187 } 188 } 189 } 190 return autogenPath 191 } 192 if len(options.OptionsForAutogenerated) > 0 { 193 ctx.ModuleErrorf("Extra tradefed configurations were provided for an autogenerated xml file, but the autogenerated xml file was not used.") 194 } 195 return path 196} 197 198var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", blueprint.RuleParams{ 199 Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs}", 200 CommandDeps: []string{ 201 "${AutoGenTestConfigScript}", 202 "${EmptyTestConfig}", 203 "$template", 204 }, 205}, "name", "template", "extraConfigs") 206 207func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string, 208 testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool, configs []Config) android.Path { 209 path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp) 210 var configStrings []string 211 if autogenPath != nil { 212 template := "${InstrumentationTestConfigTemplate}" 213 moduleTemplate := getTestConfigTemplate(ctx, testConfigTemplateProp) 214 if moduleTemplate.Valid() { 215 template = moduleTemplate.String() 216 } 217 for _, config := range configs { 218 configStrings = append(configStrings, config.Config()) 219 } 220 extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent)) 221 extraConfigs = fmt.Sprintf("--extra-configs '%s'", extraConfigs) 222 223 ctx.Build(pctx, android.BuildParams{ 224 Rule: autogenInstrumentationTest, 225 Description: "test config", 226 Input: manifest, 227 Output: autogenPath, 228 Args: map[string]string{ 229 "name": ctx.ModuleName(), 230 "template": template, 231 "extraConfigs": extraConfigs, 232 }, 233 }) 234 return autogenPath 235 } 236 return path 237} 238 239var Bool = proptools.Bool 240var BoolDefault = proptools.BoolDefault 241