Support require_root in auto-gen test configs

require_root is added to allow auto-generated test config to include
RootTargetPreparer so the test runs with root permission.

Bug: 134509111
Test: add "require_root: true" to init_benchmarks and libpower_test
  build the modules, confirm the extra target preparer is added in the
  test configs.

Change-Id: Ia07503e338935d6aa92560e7cf7b18d2a4c51243
diff --git a/cc/test.go b/cc/test.go
index dae2a37..c735fd9 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -64,6 +64,10 @@
 
 	// Test options.
 	Test_options TestOptions
+
+	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
+	// with root permission.
+	Require_root *bool
 }
 
 func init() {
@@ -273,18 +277,19 @@
 
 func (test *testBinary) install(ctx ModuleContext, file android.Path) {
 	test.data = android.PathsForModuleSrc(ctx, test.Properties.Data)
-	optionsMap := map[string]string{}
-	if Bool(test.testDecorator.Properties.Isolated) {
-		optionsMap["not-shardable"] = "true"
+	var configs []tradefed.Config
+	if Bool(test.Properties.Require_root) {
+		configs = append(configs, tradefed.Preparer{"com.android.tradefed.targetprep.RootTargetPreparer"})
 	}
-
+	if Bool(test.testDecorator.Properties.Isolated) {
+		configs = append(configs, tradefed.Option{"not-shardable", "true"})
+	}
 	if test.Properties.Test_options.Run_test_as != nil {
-		optionsMap["run-test-as"] = String(test.Properties.Test_options.Run_test_as)
+		configs = append(configs, tradefed.Option{"run-test-as", String(test.Properties.Test_options.Run_test_as)})
 	}
 
 	test.testConfig = tradefed.AutoGenNativeTestConfig(ctx, test.Properties.Test_config,
-		test.Properties.Test_config_template,
-		test.Properties.Test_suites, optionsMap)
+		test.Properties.Test_config_template, test.Properties.Test_suites, configs)
 
 	test.binaryDecorator.baseInstaller.dir = "nativetest"
 	test.binaryDecorator.baseInstaller.dir64 = "nativetest64"
@@ -371,6 +376,10 @@
 	// 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"`
+
+	// Add RootTargetPreparer to auto generated test config. This guarantees the test to run
+	// with root permission.
+	Require_root *bool
 }
 
 type benchmarkDecorator struct {
@@ -403,8 +412,12 @@
 
 func (benchmark *benchmarkDecorator) install(ctx ModuleContext, file android.Path) {
 	benchmark.data = android.PathsForModuleSrc(ctx, benchmark.Properties.Data)
+	var configs []tradefed.Config
+	if Bool(benchmark.Properties.Require_root) {
+		configs = append(configs, tradefed.Preparer{"com.android.tradefed.targetprep.RootTargetPreparer"})
+	}
 	benchmark.testConfig = tradefed.AutoGenNativeBenchmarkTestConfig(ctx, benchmark.Properties.Test_config,
-		benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites)
+		benchmark.Properties.Test_config_template, benchmark.Properties.Test_suites, configs)
 
 	benchmark.binaryDecorator.baseInstaller.dir = filepath.Join("benchmarktest", ctx.ModuleName())
 	benchmark.binaryDecorator.baseInstaller.dir64 = filepath.Join("benchmarktest64", ctx.ModuleName())
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index da5dabe..952b022 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"sort"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -39,9 +38,9 @@
 }
 
 var autogenTestConfig = pctx.StaticRule("autogenTestConfig", blueprint.RuleParams{
-	Command:     "sed 's&{MODULE}&${name}&g;s&{EXTRA_OPTIONS}&'${extraOptions}'&g' $template > $out",
+	Command:     "sed 's&{MODULE}&${name}&g;s&{EXTRA_CONFIGS}&'${extraConfigs}'&g' $template > $out",
 	CommandDeps: []string{"$template"},
-}, "name", "template", "extraOptions")
+}, "name", "template", "extraConfigs")
 
 func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string) (path android.Path, autogenPath android.WritablePath) {
 	if p := getTestConfig(ctx, prop); p != nil {
@@ -57,17 +56,38 @@
 	}
 }
 
-func autogenTemplate(ctx android.ModuleContext, output android.WritablePath, template string, optionsMap map[string]string) {
-	// If no test option found, delete {EXTRA_OPTIONS} line.
-	var options []string
-	for optionName, value := range optionsMap {
-		if value != "" {
-			options = append(options, fmt.Sprintf(`<option name="%s" value="%s" />`, optionName, value))
-		}
+type Config interface {
+	Config() string
+}
+
+type Option struct {
+	Name  string
+	Value string
+}
+
+var _ Config = Option{}
+
+func (o Option) Config() string {
+	return fmt.Sprintf(`<option name="%s" value="%s" />`, o.Name, o.Value)
+}
+
+type Preparer struct {
+	Class string
+}
+
+var _ Config = Preparer{}
+
+func (p Preparer) Config() string {
+	return fmt.Sprintf(`<target_preparer class="%s" />`, p.Class)
+}
+
+func autogenTemplate(ctx android.ModuleContext, output android.WritablePath, template string, configs []Config) {
+	var configStrings []string
+	for _, config := range configs {
+		configStrings = append(configStrings, config.Config())
 	}
-	sort.Strings(options)
-	extraOptions := strings.Join(options, "\n        ")
-	extraOptions = proptools.NinjaAndShellEscape(extraOptions)
+	extraConfigs := strings.Join(configStrings, "\n        ")
+	extraConfigs = proptools.NinjaAndShellEscape(extraConfigs)
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        autogenTestConfig,
@@ -76,26 +96,23 @@
 		Args: map[string]string{
 			"name":         ctx.ModuleName(),
 			"template":     template,
-			"extraOptions": extraOptions,
+			"extraConfigs": extraConfigs,
 		},
 	})
 }
 
 func AutoGenNativeTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string, testSuites []string,
-	optionsMap map[string]string) android.Path {
+	testConfigTemplateProp *string, testSuites []string, config []Config) android.Path {
 	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
-			autogenTemplate(ctx, autogenPath, templatePath.String(), optionsMap)
+			autogenTemplate(ctx, autogenPath, templatePath.String(), config)
 		} else {
 			if ctx.Device() {
-				autogenTemplate(ctx, autogenPath, "${NativeTestConfigTemplate}",
-					optionsMap)
+				autogenTemplate(ctx, autogenPath, "${NativeTestConfigTemplate}", config)
 			} else {
-				autogenTemplate(ctx, autogenPath, "${NativeHostTestConfigTemplate}",
-					optionsMap)
+				autogenTemplate(ctx, autogenPath, "${NativeHostTestConfigTemplate}", config)
 			}
 		}
 		return autogenPath
@@ -104,14 +121,14 @@
 }
 
 func AutoGenNativeBenchmarkTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string, testSuites []string) android.Path {
+	testConfigTemplateProp *string, testSuites []string, configs []Config) android.Path {
 	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites)
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if templatePath.Valid() {
-			autogenTemplate(ctx, autogenPath, templatePath.String(), nil)
+			autogenTemplate(ctx, autogenPath, templatePath.String(), configs)
 		} else {
-			autogenTemplate(ctx, autogenPath, "${NativeBenchmarkTestConfigTemplate}", nil)
+			autogenTemplate(ctx, autogenPath, "${NativeBenchmarkTestConfigTemplate}", configs)
 		}
 		return autogenPath
 	}