aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuexi Ma <yuexima@google.com>2020-12-09 10:31:30 -0800
committerYuexi Ma <yuexima@google.com>2021-03-12 12:02:30 -0800
commitf4dfe227ce2b488142af9f4b699ac396c9259605 (patch)
treed3f1c618c92a13154896c62df630182789cf7b9b
parent81d8abd58a048e495731c783f5403c2a0bee5ec3 (diff)
downloadplatform_test_app_compat_csuite-f4dfe227ce2b488142af9f4b699ac396c9259605.tar.gz
platform_test_app_compat_csuite-f4dfe227ce2b488142af9f4b699ac396c9259605.tar.bz2
platform_test_app_compat_csuite-f4dfe227ce2b488142af9f4b699ac396c9259605.zip
Add a csuite_test Soong rule
Create a csuite_test Soong build rule to enable users to create csuite tests with their own custom module template and without having to modify C-Suite's core. Test: go test . Bug: 175317761 Change-Id: I2f665203adfc9adab0881ea37e4e3a6d56860974
-rw-r--r--PREUPLOAD.cfg1
-rw-r--r--tools/csuite_test/Android.bp16
-rw-r--r--tools/csuite_test/csuite_test.go119
-rw-r--r--tools/csuite_test/csuite_test_test.go235
-rw-r--r--tools/csuite_test/go.mod14
5 files changed, 385 insertions, 0 deletions
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 948fa79..91c7914 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,5 +1,6 @@
[Builtin Hooks]
bpfmt = true
+gofmt = true
google_java_format = true
pylint = true
xmllint = true
diff --git a/tools/csuite_test/Android.bp b/tools/csuite_test/Android.bp
new file mode 100644
index 0000000..e44d78c
--- /dev/null
+++ b/tools/csuite_test/Android.bp
@@ -0,0 +1,16 @@
+bootstrap_go_package {
+ name: "soong-csuite",
+ pkgPath: "android/soong/csuite",
+ deps: [
+ "blueprint",
+ "soong-android",
+ "soong-java",
+ ],
+ srcs: [
+ "csuite_test.go",
+ ],
+ testSrcs: [
+ "csuite_test_test.go",
+ ],
+ pluginFor: ["soong_build"],
+}
diff --git a/tools/csuite_test/csuite_test.go b/tools/csuite_test/csuite_test.go
new file mode 100644
index 0000000..b34226a
--- /dev/null
+++ b/tools/csuite_test/csuite_test.go
@@ -0,0 +1,119 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package csuite
+
+import (
+ "android/soong/android"
+ "android/soong/java"
+ "strings"
+)
+
+var (
+ pctx = android.NewPackageContext("android/soong/csuite")
+)
+
+func init() {
+ android.RegisterModuleType("csuite_test", CSuiteTestFactory)
+}
+
+type csuiteTestProperties struct {
+ // Local path to a module template xml file.
+ // The content of the template will be used to generate test modules at runtime.
+ Test_config_template *string `android:"path"`
+}
+
+type CSuiteTest struct {
+ // Java TestHost.
+ java.TestHost
+
+ // C-Suite test properties struct.
+ csuiteTestProperties csuiteTestProperties
+
+ // Local path to a xml config file to be included in the test plan.
+ Test_plan_include *string `android:"path"`
+}
+
+func (cSuiteTest *CSuiteTest) generateTestConfigTemplate(rule *android.RuleBuilder, ctx android.ModuleContext) android.ModuleGenPath {
+ if cSuiteTest.csuiteTestProperties.Test_config_template == nil {
+ ctx.ModuleErrorf(`'test_config_template' is missing.`)
+ }
+ inputPath := android.PathForModuleSrc(ctx, *cSuiteTest.csuiteTestProperties.Test_config_template)
+ genPath := android.PathForModuleGen(ctx, planConfigDirName, ctx.ModuleName()+configTemplateFileExtension)
+ rule.Command().Textf("cp").Input(inputPath).Output(genPath)
+ return genPath
+}
+
+func (cSuiteTest *CSuiteTest) generatePlanConfig(templatePathString string, ctx android.ModuleContext) android.ModuleGenPath {
+ planName := ctx.ModuleName()
+ genPath := android.PathForModuleGen(ctx, planConfigDirName, planName+planFileExtension)
+ content := strings.Replace(planTemplateContent, "{planName}", planName, -1)
+ content = strings.Replace(content, "{templatePath}", templatePathString, -1)
+ android.WriteFileRule(ctx, genPath, content)
+ return genPath
+}
+
+func (cSuiteTest *CSuiteTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ rule := android.NewRuleBuilder(pctx, ctx)
+
+ configTemplateOutputPath := cSuiteTest.generateTestConfigTemplate(rule, ctx)
+ cSuiteTest.AddExtraResource(configTemplateOutputPath)
+
+ planOutputFile := cSuiteTest.generatePlanConfig(configTemplateOutputPath.Rel(), ctx)
+ cSuiteTest.AddExtraResource(planOutputFile)
+
+ rule.Build("CSuite", "generate C-Suite config files")
+ cSuiteTest.TestHost.GenerateAndroidBuildActions(ctx)
+}
+
+func CSuiteTestFactory() android.Module {
+ module := &CSuiteTest{}
+ module.AddProperties(&module.csuiteTestProperties)
+ installable := true
+ autoGenConfig := false
+ java.InitTestHost(&module.TestHost, &installable, []string{"csuite"}, &autoGenConfig)
+
+ java.InitJavaModuleMultiTargets(module, android.HostSupported)
+
+ return module
+}
+
+const (
+ planConfigDirName = `config`
+ configTemplateFileExtension = `.xml.template`
+ planFileExtension = `.xml`
+ planTemplateContent = `<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration>
+ <test class="com.android.csuite.config.ModuleGenerator">
+ <option name="template" value="{templatePath}" />
+ </test>
+ <include name="csuite-base" />
+ <option name="plan" value="{planName}" />
+</configuration>
+`
+)
diff --git a/tools/csuite_test/csuite_test_test.go b/tools/csuite_test/csuite_test_test.go
new file mode 100644
index 0000000..0223da1
--- /dev/null
+++ b/tools/csuite_test/csuite_test_test.go
@@ -0,0 +1,235 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package csuite
+
+import (
+ "android/soong/android"
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+)
+
+var buildDir string
+
+func TestValidBpDoesNotThrowError(t *testing.T) {
+ ctx, config := createContextAndConfig(t, `
+ csuite_test {
+ name: "plan_name",
+ test_config_template: "test_config.xml.template"
+ }
+ `)
+
+ _, errs := ctx.PrepareBuildActions(config)
+
+ android.FailIfErrored(t, errs)
+}
+
+func TestBpContainsTestHostPropsThrowsError(t *testing.T) {
+ ctx, _ := createContextAndConfig(t, `
+ csuite_test {
+ name: "plan_name",
+ test_config_template: "test_config.xml.template",
+ data_native_bins: "bin"
+ }
+ `)
+
+ _, errs := ctx.ParseBlueprintsFiles("Android.bp")
+
+ android.FailIfNoMatchingErrors(t, `unrecognized property`, errs)
+}
+
+func TestBpContainsManifestThrowsError(t *testing.T) {
+ ctx, _ := createContextAndConfig(t, `
+ csuite_test {
+ name: "plan_name",
+ test_config_template: "test_config.xml.template",
+ test_config: "AndroidTest.xml"
+ }
+ `)
+
+ _, errs := ctx.ParseBlueprintsFiles("Android.bp")
+
+ android.FailIfNoMatchingErrors(t, `unrecognized property`, errs)
+}
+
+func TestBpMissingNameThrowsError(t *testing.T) {
+ ctx, _ := createContextAndConfig(t, `
+ csuite_test {
+ test_config_template: "test_config.xml.template"
+ }
+ `)
+
+ _, errs := ctx.ParseBlueprintsFiles("Android.bp")
+
+ android.FailIfNoMatchingErrors(t, `'name' is missing`, errs)
+}
+
+func TestBpMissingTemplatePathThrowsError(t *testing.T) {
+ ctx, config := createContextAndConfig(t, `
+ csuite_test {
+ name: "plan_name",
+ }
+ `)
+
+ ctx.ParseBlueprintsFiles("Android.bp")
+ _, errs := ctx.PrepareBuildActions(config)
+
+ android.FailIfNoMatchingErrors(t, `'test_config_template' is missing`, errs)
+}
+
+func TestGeneratedTestPlanContainsPlanName(t *testing.T) {
+ ctx, config := createContextAndConfig(t, `
+ csuite_test {
+ name: "plan_name",
+ test_config_template: "test_config.xml.template"
+ }
+ `)
+
+ _, parsingErrs := ctx.ParseBlueprintsFiles("Android.bp")
+ _, buildErrs := ctx.PrepareBuildActions(config)
+
+ android.FailIfErrored(t, parsingErrs)
+ android.FailIfErrored(t, buildErrs)
+ module := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common")
+ content := android.ContentFromFileRuleForTests(t, module.Output("config/plan_name.xml"))
+ if !strings.Contains(content, "plan_name") {
+ t.Errorf("The plan name is missing from the generated plan: %s", content)
+ }
+}
+
+func TestGeneratedTestPlanContainsTemplatePath(t *testing.T) {
+ ctx, config := createContextAndConfig(t, `
+ csuite_test {
+ name: "plan_name",
+ test_config_template: "test_config.xml.template"
+ }
+ `)
+
+ _, parsingErrs := ctx.ParseBlueprintsFiles("Android.bp")
+ _, buildErrs := ctx.PrepareBuildActions(config)
+
+ android.FailIfErrored(t, parsingErrs)
+ android.FailIfErrored(t, buildErrs)
+ module := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common")
+ content := android.ContentFromFileRuleForTests(t, module.Output("config/plan_name.xml"))
+ if !strings.Contains(content, "config/plan_name.xml.template") {
+ t.Errorf("The template path is missing from the generated plan: %s", content)
+ }
+}
+
+func TestTemplateFileCopyRuleExists(t *testing.T) {
+ ctx, config := createContextAndConfig(t, `
+ csuite_test {
+ name: "plan_name",
+ test_config_template: "test_config.xml.template"
+ }
+ `)
+
+ _, parsingErrs := ctx.ParseBlueprintsFiles("Android.bp")
+ _, buildErrs := ctx.PrepareBuildActions(config)
+
+ android.FailIfErrored(t, parsingErrs)
+ android.FailIfErrored(t, buildErrs)
+ params := ctx.ModuleForTests("plan_name", android.BuildOs.String()+"_common").Rule("CSuite")
+ assertPathsContains(t, getAllInputPaths(params), "test_config.xml.template")
+ assertWritablePathsContainsRel(t, getAllOutputPaths(params), "config/plan_name.xml.template")
+ if !strings.HasPrefix(params.RuleParams.Command, "cp") {
+ t.Errorf("'cp' command is missing.")
+ }
+}
+
+func TestMain(m *testing.M) {
+ run := func() int {
+ setUp()
+ defer tearDown()
+
+ return m.Run()
+ }
+
+ os.Exit(run())
+}
+
+func assertPathsContains(t *testing.T, paths android.Paths, path string) {
+ for _, p := range paths {
+ if p.String() == path {
+ return
+ }
+ }
+ t.Errorf("Cannot find expected path %s", path)
+}
+
+func assertWritablePathsContainsRel(t *testing.T, paths android.WritablePaths, relPath string) {
+ for _, path := range paths {
+ if path.Rel() == relPath {
+ return
+ }
+ }
+ t.Errorf("Cannot find expected relative path %s", relPath)
+}
+
+func getAllOutputPaths(params android.TestingBuildParams) android.WritablePaths {
+ var paths []android.WritablePath
+ if params.Output != nil {
+ paths = append(paths, params.Output)
+ }
+ if params.ImplicitOutput != nil {
+ paths = append(paths, params.ImplicitOutput)
+ }
+ if params.SymlinkOutput != nil {
+ paths = append(paths, params.SymlinkOutput)
+ }
+ paths = append(paths, params.Outputs...)
+ paths = append(paths, params.ImplicitOutputs...)
+ paths = append(paths, params.SymlinkOutputs...)
+
+ return paths
+}
+
+func getAllInputPaths(params android.TestingBuildParams) android.Paths {
+ var paths []android.Path
+ if params.Input != nil {
+ paths = append(paths, params.Input)
+ }
+ if params.Implicit != nil {
+ paths = append(paths, params.Implicit)
+ }
+ paths = append(paths, params.Inputs...)
+ paths = append(paths, params.Implicits...)
+
+ return paths
+}
+
+func setUp() {
+ var err error
+ buildDir, err = ioutil.TempDir("", "soong_csuite_test")
+ if err != nil {
+ panic(err)
+ }
+}
+
+func tearDown() {
+ os.RemoveAll(buildDir)
+}
+
+func createContextAndConfig(t *testing.T, bp string) (*android.TestContext, android.Config) {
+ t.Helper()
+ config := android.TestArchConfig(buildDir, nil, bp, nil)
+ ctx := android.NewTestArchContext(config)
+ ctx.RegisterModuleType("csuite_test", CSuiteTestFactory)
+ ctx.Register()
+
+ return ctx, config
+}
diff --git a/tools/csuite_test/go.mod b/tools/csuite_test/go.mod
new file mode 100644
index 0000000..a373cd1
--- /dev/null
+++ b/tools/csuite_test/go.mod
@@ -0,0 +1,14 @@
+module android/soong/csuite
+
+require (
+ android/soong v0.0.0
+ github.com/google/blueprint v0.0.0
+)
+
+replace android/soong v0.0.0 => ../../../../../build/soong
+
+replace github.com/golang/protobuf v0.0.0 => ../../../../../external/golang-protobuf
+
+replace github.com/google/blueprint v0.0.0 => ../../../../../build/blueprint
+
+go 1.13 \ No newline at end of file