Merge changes Ifa21a69b,I540a19b0 into main
* changes:
Allow test_suite to nest
Implement test_suite.
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index 7a04c19..5c13d64 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -383,6 +383,19 @@
// 4) Module.config / AndroidTest.xml
m.testConfig = m.fixTestConfig(ctx, m.provider.TestConfig)
+
+ // 5) We provide so we can be listed in test_suites.
+ android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
+ InstalledFiles: m.supportFiles.Paths(),
+ OutputFile: baseApk,
+ TestConfig: m.testConfig,
+ HostRequiredModuleNames: m.provider.HostRequiredModuleNames,
+ RequiredModuleNames: m.provider.RequiredModuleNames,
+ TestSuites: m.tradefedProperties.Test_suites,
+ IsHost: m.provider.IsHost,
+ LocalCertificate: m.provider.LocalCertificate,
+ IsUnitTest: m.provider.IsUnitTest,
+ })
}
var _ android.AndroidMkEntriesProvider = (*testModuleConfigHostModule)(nil)
diff --git a/tradefed_modules/test_suite.go b/tradefed_modules/test_suite.go
index a6c2727..00585f5 100644
--- a/tradefed_modules/test_suite.go
+++ b/tradefed_modules/test_suite.go
@@ -15,17 +15,32 @@
package tradefed_modules
import (
- "fmt"
+ "encoding/json"
+ "path"
+ "path/filepath"
"android/soong/android"
+ "android/soong/tradefed"
+ "github.com/google/blueprint"
)
+const testSuiteModuleType = "test_suite"
+
+type testSuiteTag struct{
+ blueprint.BaseDependencyTag
+}
+
+type testSuiteManifest struct {
+ Name string `json:"name"`
+ Files []string `json:"files"`
+}
+
func init() {
RegisterTestSuiteBuildComponents(android.InitRegistrationContext)
}
func RegisterTestSuiteBuildComponents(ctx android.RegistrationContext) {
- ctx.RegisterModuleType("test_suite", TestSuiteFactory)
+ ctx.RegisterModuleType(testSuiteModuleType, TestSuiteFactory)
}
var PrepareForTestWithTestSuiteBuildComponents = android.GroupFixturePreparers(
@@ -43,10 +58,64 @@
testSuiteProperties
}
+func (t *testSuiteModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ for _, test := range t.Tests {
+ if ctx.OtherModuleDependencyVariantExists(ctx.Config().BuildOSCommonTarget.Variations(), test) {
+ // Host tests.
+ ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), testSuiteTag{}, test)
+ } else {
+ // Target tests.
+ ctx.AddDependency(ctx.Module(), testSuiteTag{}, test)
+ }
+ }
+}
+
func (t *testSuiteModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
suiteName := ctx.ModuleName()
+ modulesByName := make(map[string]android.Module)
+ ctx.WalkDeps(func(child, parent android.Module) bool {
+ // Recurse into test_suite dependencies.
+ if ctx.OtherModuleType(child) == testSuiteModuleType {
+ ctx.Phony(suiteName, android.PathForPhony(ctx, child.Name()))
+ return true
+ }
+
+ // Only write out top level test suite dependencies here.
+ if _, ok := ctx.OtherModuleDependencyTag(child).(testSuiteTag); !ok {
+ return false
+ }
+
+ if !child.InstallInTestcases() {
+ ctx.ModuleErrorf("test_suite only supports modules installed in testcases. %q is not installed in testcases.", child.Name())
+ return false
+ }
+
+ modulesByName[child.Name()] = child
+ return false
+ })
+
+ var files []string
+ for name, module := range modulesByName {
+ // Get the test provider data from the child.
+ tp, ok := android.OtherModuleProvider(ctx, module, tradefed.BaseTestProviderKey)
+ if !ok {
+ // TODO: Consider printing out a list of all module types.
+ ctx.ModuleErrorf("%q is not a test module.", name)
+ continue
+ }
+
+ files = append(files, packageModuleFiles(ctx, suiteName, module, tp)...)
+ ctx.Phony(suiteName, android.PathForPhony(ctx, name))
+ }
+
manifestPath := android.PathForSuiteInstall(ctx, suiteName, suiteName+".json")
- android.WriteFileRule(ctx, manifestPath, fmt.Sprintf(`{"name": %q}`, suiteName))
+ b, err := json.Marshal(testSuiteManifest{Name: suiteName, Files: files})
+ if err != nil {
+ ctx.ModuleErrorf("Failed to marshal manifest: %v", err)
+ return
+ }
+ android.WriteFileRule(ctx, manifestPath, string(b))
+
ctx.Phony(suiteName, manifestPath)
}
@@ -54,8 +123,51 @@
module := &testSuiteModule{}
module.AddProperties(&module.testSuiteProperties)
- android.InitAndroidModule(module)
+ android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibCommon)
android.InitDefaultableModule(module)
return module
}
+
+func packageModuleFiles(ctx android.ModuleContext, suiteName string, module android.Module, tp tradefed.BaseTestProviderData) []string {
+
+ hostOrTarget := "target"
+ if tp.IsHost {
+ hostOrTarget = "host"
+ }
+
+ // suiteRoot at out/soong/packaging/<suiteName>.
+ suiteRoot := android.PathForSuiteInstall(ctx, suiteName)
+
+ var installed android.InstallPaths
+ // Install links to installed files from the module.
+ if installFilesInfo, ok := android.OtherModuleProvider(ctx, module, android.InstallFilesProvider); ok {
+ for _, f := range installFilesInfo.InstallFiles {
+ // rel is anything under .../<partition>, normally under .../testcases.
+ rel := android.Rel(ctx, f.PartitionDir(), f.String())
+
+ // Install the file under <suiteRoot>/<host|target>/<partition>.
+ installDir := suiteRoot.Join(ctx, hostOrTarget, f.Partition(), path.Dir(rel))
+ linkTo, err := filepath.Rel(installDir.String(), f.String())
+ if err != nil {
+ ctx.ModuleErrorf("Failed to get relative path from %s to %s: %v", installDir.String(), f.String(), err)
+ continue
+ }
+ installed = append(installed, ctx.InstallAbsoluteSymlink(installDir, path.Base(rel), linkTo))
+ }
+ }
+
+ // Install config file.
+ if tp.TestConfig != nil {
+ moduleRoot := suiteRoot.Join(ctx, hostOrTarget, "testcases", module.Name())
+ installed = append(installed, ctx.InstallFile(moduleRoot, module.Name() + ".config", tp.TestConfig))
+ }
+
+ // Add to phony and manifest, manifestpaths are relative to suiteRoot.
+ var manifestEntries []string
+ for _, f := range installed {
+ manifestEntries = append(manifestEntries, android.Rel(ctx, suiteRoot.String(), f.String()))
+ ctx.Phony(suiteName, f)
+ }
+ return manifestEntries
+}
diff --git a/tradefed_modules/test_suite_test.go b/tradefed_modules/test_suite_test.go
index 647ba4d..3c0a9eb 100644
--- a/tradefed_modules/test_suite_test.go
+++ b/tradefed_modules/test_suite_test.go
@@ -16,6 +16,8 @@
import (
"android/soong/android"
"android/soong/java"
+ "encoding/json"
+ "slices"
"testing"
)
@@ -44,10 +46,106 @@
]
}
`)
- manifestPath := ctx.ModuleForTests("my-suite", "").Output("out/soong/test_suites/my-suite/my-suite.json")
- got := android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)
- want := `{"name": "my-suite"}` + "\n"
- if got != want {
- t.Errorf("my-suite.json content was %q, want %q", got, want)
+ manifestPath := ctx.ModuleForTests("my-suite", "android_common").Output("out/soong/test_suites/my-suite/my-suite.json")
+ var actual testSuiteManifest
+ if err := json.Unmarshal([]byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)), &actual); err != nil {
+ t.Errorf("failed to unmarshal manifest: %v", err)
}
+ slices.Sort(actual.Files)
+
+ expected := testSuiteManifest{
+ Name: "my-suite",
+ Files: []string{
+ "target/testcases/TestModule1/TestModule1.config",
+ "target/testcases/TestModule1/arm64/TestModule1.apk",
+ "target/testcases/TestModule2/TestModule2.config",
+ "target/testcases/TestModule2/arm64/TestModule2.apk",
+ },
+ }
+
+ android.AssertDeepEquals(t, "manifests differ", expected, actual)
+}
+
+func TestTestSuitesWithNested(t *testing.T) {
+ t.Parallel()
+ ctx := android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+ ).RunTestWithBp(t, `
+ android_test {
+ name: "TestModule1",
+ sdk_version: "current",
+ }
+
+ android_test {
+ name: "TestModule2",
+ sdk_version: "current",
+ }
+
+ android_test {
+ name: "TestModule3",
+ sdk_version: "current",
+ }
+
+ test_suite {
+ name: "my-child-suite",
+ description: "a child test suite",
+ tests: [
+ "TestModule1",
+ "TestModule2",
+ ]
+ }
+
+ test_suite {
+ name: "my-all-tests-suite",
+ description: "a parent test suite",
+ tests: [
+ "TestModule1",
+ "TestModule3",
+ "my-child-suite",
+ ]
+ }
+ `)
+ manifestPath := ctx.ModuleForTests("my-all-tests-suite", "android_common").Output("out/soong/test_suites/my-all-tests-suite/my-all-tests-suite.json")
+ var actual testSuiteManifest
+ if err := json.Unmarshal([]byte(android.ContentFromFileRuleForTests(t, ctx.TestContext, manifestPath)), &actual); err != nil {
+ t.Errorf("failed to unmarshal manifest: %v", err)
+ }
+ slices.Sort(actual.Files)
+
+ expected := testSuiteManifest{
+ Name: "my-all-tests-suite",
+ Files: []string{
+ "target/testcases/TestModule1/TestModule1.config",
+ "target/testcases/TestModule1/arm64/TestModule1.apk",
+ "target/testcases/TestModule2/TestModule2.config",
+ "target/testcases/TestModule2/arm64/TestModule2.apk",
+ "target/testcases/TestModule3/TestModule3.config",
+ "target/testcases/TestModule3/arm64/TestModule3.apk",
+ },
+ }
+
+ android.AssertDeepEquals(t, "manifests differ", expected, actual)
+}
+
+func TestTestSuitesNotInstalledInTestcases(t *testing.T) {
+ t.Parallel()
+ android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestSuiteBuildComponents),
+ ).ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern([]string{
+ `"SomeHostTest" is not installed in testcases`,
+ })).RunTestWithBp(t, `
+ java_test_host {
+ name: "SomeHostTest",
+ srcs: ["a.java"],
+ }
+ test_suite {
+ name: "my-suite",
+ description: "a test suite",
+ tests: [
+ "SomeHostTest",
+ ]
+ }
+ `)
}