Loading tradefed_modules/test_module_config.go +13 −0 Original line number Diff line number Diff line Loading @@ -383,6 +383,19 @@ func (m *testModuleConfigModule) generateManifestAndConfig(ctx android.ModuleCon // 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) Loading tradefed_modules/test_suite.go +116 −4 Original line number Diff line number Diff line Loading @@ -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( Loading @@ -43,10 +58,64 @@ type testSuiteModule struct { 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) } Loading @@ -54,8 +123,51 @@ func TestSuiteFactory() android.Module { 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 } tradefed_modules/test_suite_test.go +103 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package tradefed_modules import ( "android/soong/android" "android/soong/java" "encoding/json" "slices" "testing" ) Loading Loading @@ -44,10 +46,106 @@ func TestTestSuites(t *testing.T) { ] } `) 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", ] } `) } Loading
tradefed_modules/test_module_config.go +13 −0 Original line number Diff line number Diff line Loading @@ -383,6 +383,19 @@ func (m *testModuleConfigModule) generateManifestAndConfig(ctx android.ModuleCon // 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) Loading
tradefed_modules/test_suite.go +116 −4 Original line number Diff line number Diff line Loading @@ -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( Loading @@ -43,10 +58,64 @@ type testSuiteModule struct { 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) } Loading @@ -54,8 +123,51 @@ func TestSuiteFactory() android.Module { 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 }
tradefed_modules/test_suite_test.go +103 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package tradefed_modules import ( "android/soong/android" "android/soong/java" "encoding/json" "slices" "testing" ) Loading Loading @@ -44,10 +46,106 @@ func TestTestSuites(t *testing.T) { ] } `) 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", ] } `) }