Merge "Add test_module_config_host" into main
diff --git a/java/app.go b/java/app.go
index 2abc451..7b25775 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1337,6 +1337,8 @@
OutputFile: a.OutputFile(),
TestConfig: a.testConfig,
HostRequiredModuleNames: a.HostRequiredModuleNames(),
+ TestSuites: a.testProperties.Test_suites,
+ IsHost: false,
})
}
diff --git a/java/java.go b/java/java.go
index 72536cd..c5bdf9d 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1438,6 +1438,14 @@
j.Test.generateAndroidBuildActionsWithConfig(ctx, configs)
android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{})
+ android.SetProvider(ctx, tradefed.BaseTestProviderKey, tradefed.BaseTestProviderData{
+ InstalledFiles: j.data,
+ OutputFile: j.outputFile,
+ TestConfig: j.testConfig,
+ RequiredModuleNames: j.RequiredModuleNames(),
+ TestSuites: j.testProperties.Test_suites,
+ IsHost: true,
+ })
}
func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
diff --git a/tradefed/providers.go b/tradefed/providers.go
index f41e09e..66cb625 100644
--- a/tradefed/providers.go
+++ b/tradefed/providers.go
@@ -16,6 +16,11 @@
TestConfig android.Path
// Other modules we require to be installed to run tests. We expect base to build them.
HostRequiredModuleNames []string
+ RequiredModuleNames []string
+ // List of test suites base uses.
+ TestSuites []string
+ // Used for bases that are Host
+ IsHost bool
}
var BaseTestProviderKey = blueprint.NewProvider[BaseTestProviderData]()
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index ba6ab94..6867537 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -17,6 +17,7 @@
// Register the license_kind module type.
func RegisterTestModuleConfigBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("test_module_config", TestModuleConfigFactory)
+ ctx.RegisterModuleType("test_module_config_host", TestModuleConfigHostFactory)
}
type testModuleConfigModule struct {
@@ -32,6 +33,12 @@
provider tradefed.BaseTestProviderData
}
+// Host is mostly the same as non-host, just some diffs for AddDependency and
+// AndroidMkEntries, but the properties are the same.
+type testModuleConfigHostModule struct {
+ testModuleConfigModule
+}
+
// Properties to list in Android.bp for this module.
type tradefedProperties struct {
// Module name of the base test that we will run.
@@ -68,8 +75,9 @@
}
var (
- testModuleConfigTag = dependencyTag{name: "TestModuleConfigBase"}
- pctx = android.NewPackageContext("android/soong/tradefed_modules")
+ testModuleConfigTag = dependencyTag{name: "TestModuleConfigBase"}
+ testModuleConfigHostTag = dependencyTag{name: "TestModuleConfigHostBase"}
+ pctx = android.NewPackageContext("android/soong/tradefed_modules")
)
func (m *testModuleConfigModule) InstallInTestcases() bool {
@@ -77,6 +85,10 @@
}
func (m *testModuleConfigModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ if m.Base == nil {
+ ctx.ModuleErrorf("'base' field must be set to a 'android_test' module.")
+ return
+ }
ctx.AddDependency(ctx.Module(), testModuleConfigTag, *m.Base)
}
@@ -143,35 +155,41 @@
//
// If we change to symlinks, this all needs to change.
func (m *testModuleConfigModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ m.validateBase(ctx, &testModuleConfigTag, "android_test", false)
+ m.generateManifestAndConfig(ctx)
- ctx.VisitDirectDepsWithTag(testModuleConfigTag, func(dep android.Module) {
- if provider, ok := android.OtherModuleProvider(ctx, dep, tradefed.BaseTestProviderKey); ok {
- m.base = dep
- m.provider = provider
- } else {
- ctx.ModuleErrorf("The base module '%s' does not provide test BaseTestProviderData. Only 'android_test' modules are supported.", dep.Name())
- return
+}
+
+// Any test suites in base should not be repeated in the derived class, except "general-tests".
+// We may restrict derived tests to only be "general-tests" as it doesn't make sense to add a slice
+// of a test to compatibility suite.
+//
+// Returns ErrorMessage, false on problems
+// Returns _, true if okay.
+func (m *testModuleConfigModule) validateTestSuites(ctx android.ModuleContext) bool {
+ if len(m.tradefedProperties.Test_suites) == 0 {
+ ctx.ModuleErrorf("At least one test-suite must be set or this won't run. Use \"general-tests\"")
+ return false
+ }
+
+ derivedSuites := make(map[string]bool)
+ // See if any suites in base is also in derived (other than general-tests)
+ for _, s := range m.tradefedProperties.Test_suites {
+ if s != "general-tests" {
+ derivedSuites[s] = true
}
- })
+ }
+ if len(derivedSuites) == 0 {
+ return true
+ }
+ for _, baseSuite := range m.provider.TestSuites {
+ if derivedSuites[baseSuite] {
+ ctx.ModuleErrorf("TestSuite %s exists in the base, do not add it here", baseSuite)
+ return false
+ }
+ }
- // 1) A manifest file listing the base.
- installDir := android.PathForModuleInstall(ctx, ctx.ModuleName())
- out := android.PathForModuleOut(ctx, "test_module_config.manifest")
- android.WriteFileRule(ctx, out, fmt.Sprintf("{%q: %q}", "base", *m.tradefedProperties.Base))
- ctx.InstallFile(installDir, out.Base(), out)
-
- // 2) Module.config / AndroidTest.xml
- // Note, there is still a "test-tag" element with base's module name, but
- // Tradefed team says its ignored anyway.
- m.testConfig = m.fixTestConfig(ctx, m.provider.TestConfig)
-
- // 3) Write ARCH/Module.apk in testcases.
- // Handled by soong_app_prebuilt and OutputFile in entries.
- // Nothing to do here.
-
- // 4) Copy base's data files.
- // Handled by soong_app_prebuilt and LOCAL_COMPATIBILITY_SUPPORT_FILES.
- // Nothing to do here.
+ return true
}
func TestModuleConfigFactory() android.Module {
@@ -184,6 +202,16 @@
return module
}
+func TestModuleConfigHostFactory() android.Module {
+ module := &testModuleConfigHostModule{}
+
+ module.AddProperties(&module.tradefedProperties)
+ android.InitAndroidMultiTargetsArchModule(module, android.HostSupported, android.MultilibCommon)
+ android.InitDefaultableModule(module)
+
+ return module
+}
+
// Implements android.AndroidMkEntriesProvider
var _ android.AndroidMkEntriesProvider = (*testModuleConfigModule)(nil)
@@ -198,22 +226,96 @@
// Out update config file with extra options.
entries.SetPath("LOCAL_FULL_TEST_CONFIG", m.testConfig)
entries.SetString("LOCAL_MODULE_TAGS", "tests")
- // Required for atest to run additional tradefed testtypes
- entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", m.provider.HostRequiredModuleNames...)
-
- // Clear the JNI symbols because they belong to base not us. Either transform the names in the string
- // or clear the variable because we don't need it, we are copying bases libraries not generating
- // new ones.
- entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", "")
// Don't append to base's test-suites, only use the ones we define, so clear it before
// appending to it.
entries.SetString("LOCAL_COMPATIBILITY_SUITE", "")
- if len(m.tradefedProperties.Test_suites) > 0 {
- entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...)
- } else {
- entries.AddCompatibilityTestSuites("null-suite")
+ entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...)
+
+ if len(m.provider.HostRequiredModuleNames) > 0 {
+ entries.AddStrings("LOCAL_HOST_REQUIRED_MODULES", m.provider.HostRequiredModuleNames...)
+ }
+ if len(m.provider.RequiredModuleNames) > 0 {
+ entries.AddStrings("LOCAL_REQUIRED_MODULES", m.provider.RequiredModuleNames...)
+ }
+
+ if m.provider.IsHost == false {
+ // Not needed for jar_host_test
+ //
+ // Clear the JNI symbols because they belong to base not us. Either transform the names in the string
+ // or clear the variable because we don't need it, we are copying bases libraries not generating
+ // new ones.
+ entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", "")
}
})
return entriesList
}
+
+func (m *testModuleConfigHostModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+ if m.Base == nil {
+ ctx.ModuleErrorf("'base' field must be set to a 'java_test_host' module")
+ return
+ }
+ ctx.AddVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), testModuleConfigHostTag, *m.Base)
+}
+
+// File to write:
+// 1) out/host/linux-x86/testcases/derived-module/test_module_config.manifest # contains base's name.
+// 2) out/host/linux-x86/testcases/derived-module/derived-module.config # Update AnroidTest.xml
+// 3) out/host/linux-x86/testcases/derived-module/base.jar
+// - written via soong_java_prebuilt.mk
+//
+// 4) out/host/linux-x86/testcases/derived-module/* # data dependencies from base.
+// - written via soong_java_prebuilt.mk
+func (m *testModuleConfigHostModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+ m.validateBase(ctx, &testModuleConfigHostTag, "java_test_host", true)
+ m.generateManifestAndConfig(ctx)
+}
+
+// Ensure the base listed is the right type by checking that we get the expected provider data.
+// Returns false on errors and the context is updated with an error indicating the baseType expected.
+func (m *testModuleConfigModule) validateBase(ctx android.ModuleContext, depTag *dependencyTag, baseType string, baseShouldBeHost bool) {
+ ctx.VisitDirectDepsWithTag(*depTag, func(dep android.Module) {
+ if provider, ok := android.OtherModuleProvider(ctx, dep, tradefed.BaseTestProviderKey); ok {
+ if baseShouldBeHost == provider.IsHost {
+ m.base = dep
+ m.provider = provider
+ } else {
+ if baseShouldBeHost {
+ ctx.ModuleErrorf("'android_test' module used as base, but 'java_test_host' expected.")
+ } else {
+ ctx.ModuleErrorf("'java_test_host' module used as base, but 'android_test' expected.")
+ }
+ }
+ } else {
+ ctx.ModuleErrorf("'%s' module used as base but it is not a '%s' module.", *m.Base, baseType)
+ }
+ })
+}
+
+// Actions to write:
+// 1. manifest file to testcases dir
+// 2. New Module.config / AndroidTest.xml file with our options.
+func (m *testModuleConfigModule) generateManifestAndConfig(ctx android.ModuleContext) {
+ if !m.validateTestSuites(ctx) {
+ return
+ }
+ // Ensure the provider is accurate
+ if m.provider.TestConfig == nil {
+ return
+ }
+
+ // 1) A manifest file listing the base, write text to a tiny file.
+ installDir := android.PathForModuleInstall(ctx, ctx.ModuleName())
+ manifest := android.PathForModuleOut(ctx, "test_module_config.manifest")
+ android.WriteFileRule(ctx, manifest, fmt.Sprintf("{%q: %q}", "base", *m.tradefedProperties.Base))
+ // build/soong/android/androidmk.go has this comment:
+ // Assume the primary install file is last
+ // so we need to Install our file last.
+ ctx.InstallFile(installDir, manifest.Base(), manifest)
+
+ // 2) Module.config / AndroidTest.xml
+ m.testConfig = m.fixTestConfig(ctx, m.provider.TestConfig)
+}
+
+var _ android.AndroidMkEntriesProvider = (*testModuleConfigHostModule)(nil)
diff --git a/tradefed_modules/test_module_config_test.go b/tradefed_modules/test_module_config_test.go
index ff53043..41dd3d4 100644
--- a/tradefed_modules/test_module_config_test.go
+++ b/tradefed_modules/test_module_config_test.go
@@ -16,6 +16,7 @@
import (
"android/soong/android"
"android/soong/java"
+ "strconv"
"strings"
"testing"
)
@@ -43,6 +44,7 @@
base: "base",
exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
include_annotations: ["android.platform.test.annotations.LargeTest"],
+ test_suites: ["general-tests"],
}
`
@@ -92,7 +94,7 @@
}
// Ensure we error for a base we don't support.
-func TestModuleConfigBadBaseShouldFail(t *testing.T) {
+func TestModuleConfigWithHostBaseShouldFailWithExplicitMessage(t *testing.T) {
badBp := `
java_test_host {
name: "base",
@@ -104,16 +106,60 @@
base: "base",
exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
include_annotations: ["android.platform.test.annotations.LargeTest"],
+ test_suites: ["general-tests"],
}`
- ctx := android.GroupFixturePreparers(
+ android.GroupFixturePreparers(
java.PrepareForTestWithJavaDefaultModules,
android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
).ExtendWithErrorHandler(
- android.FixtureExpectsAtLeastOneErrorMatchingPattern("does not provide test BaseTestProviderData")).
+ android.FixtureExpectsAtLeastOneErrorMatchingPattern("'java_test_host' module used as base, but 'android_test' expected")).
RunTestWithBp(t, badBp)
+}
- ctx.ModuleForTests("derived_test", "android_common")
+func TestModuleConfigBadBaseShouldFailWithGeneralMessage(t *testing.T) {
+ badBp := `
+ java_library {
+ name: "base",
+ srcs: ["a.java"],
+ }
+
+ test_module_config {
+ name: "derived_test",
+ base: "base",
+ exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+ include_annotations: ["android.platform.test.annotations.LargeTest"],
+ test_suites: ["general-tests"],
+ }`
+
+ android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+ ).ExtendWithErrorHandler(
+ android.FixtureExpectsOneErrorPattern("'base' module used as base but it is not a 'android_test' module.")).
+ RunTestWithBp(t, badBp)
+}
+
+func TestModuleConfigNoBaseShouldFail(t *testing.T) {
+ badBp := `
+ java_library {
+ name: "base",
+ srcs: ["a.java"],
+ }
+
+ test_module_config {
+ name: "derived_test",
+ exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+ include_annotations: ["android.platform.test.annotations.LargeTest"],
+ test_suites: ["general-tests"],
+ }`
+
+ android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+ ).ExtendWithErrorHandler(
+ android.FixtureExpectsOneErrorPattern("'base' field must be set to a 'android_test' module.")).
+ RunTestWithBp(t, badBp)
}
// Ensure we error for a base we don't support.
@@ -128,6 +174,7 @@
test_module_config {
name: "derived_test",
base: "base",
+ test_suites: ["general-tests"],
}`
ctx := android.GroupFixturePreparers(
@@ -152,12 +199,14 @@
name: "derived_test",
base: "base",
include_annotations: ["android.platform.test.annotations.LargeTest"],
+ test_suites: ["general-tests"],
}
test_module_config {
name: "another_derived_test",
base: "base",
include_annotations: ["android.platform.test.annotations.LargeTest"],
+ test_suites: ["general-tests"],
}`
ctx := android.GroupFixturePreparers(
@@ -190,6 +239,114 @@
}
}
+// Test_module_config_host rule is allowed to depend on java_test_host
+func TestModuleConfigHostBasics(t *testing.T) {
+ bp := `
+ java_test_host {
+ name: "base",
+ srcs: ["a.java"],
+ test_suites: ["suiteA", "general-tests", "suiteB"],
+ }
+
+ test_module_config_host {
+ name: "derived_test",
+ base: "base",
+ exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+ include_annotations: ["android.platform.test.annotations.LargeTest"],
+ test_suites: ["general-tests"],
+ }`
+
+ ctx := android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+ ).RunTestWithBp(t, bp)
+
+ variant := ctx.Config.BuildOS.String() + "_common"
+ derived := ctx.ModuleForTests("derived_test", variant)
+ mod := derived.Module().(*testModuleConfigHostModule)
+ allEntries := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)
+ entries := allEntries[0]
+ android.AssertArrayString(t, "", entries.EntryMap["LOCAL_MODULE"], []string{"derived_test"})
+
+ if !mod.Host() {
+ t.Errorf("host bit is not set for a java_test_host module.")
+ }
+ actualData, _ := strconv.ParseBool(entries.EntryMap["LOCAL_IS_UNIT_TEST"][0])
+ android.AssertBoolEquals(t, "LOCAL_IS_UNIT_TEST", true, actualData)
+
+}
+
+// When you pass an 'android_test' as base, the warning message is a bit obscure,
+// talking about variants, but it is something. Ideally we could do better.
+func TestModuleConfigHostBadBaseShouldFailWithVariantWarning(t *testing.T) {
+ badBp := `
+ android_test {
+ name: "base",
+ sdk_version: "current",
+ srcs: ["a.java"],
+ }
+
+ test_module_config_host {
+ name: "derived_test",
+ base: "base",
+ exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+ include_annotations: ["android.platform.test.annotations.LargeTest"],
+ }`
+
+ android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+ ).ExtendWithErrorHandler(
+ android.FixtureExpectsAtLeastOneErrorMatchingPattern("missing variant")).
+ RunTestWithBp(t, badBp)
+}
+
+func TestModuleConfigHostNeedsATestSuite(t *testing.T) {
+ badBp := `
+ java_test_host {
+ name: "base",
+ srcs: ["a.java"],
+ }
+
+ test_module_config_host {
+ name: "derived_test",
+ base: "base",
+ exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+ include_annotations: ["android.platform.test.annotations.LargeTest"],
+ }`
+
+ android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+ ).ExtendWithErrorHandler(
+ android.FixtureExpectsAtLeastOneErrorMatchingPattern("At least one test-suite must be set")).
+ RunTestWithBp(t, badBp)
+}
+
+func TestModuleConfigHostDuplicateTestSuitesGiveErrors(t *testing.T) {
+ badBp := `
+ java_test_host {
+ name: "base",
+ srcs: ["a.java"],
+ test_suites: ["general-tests", "some-compat"],
+ }
+
+ test_module_config_host {
+ name: "derived_test",
+ base: "base",
+ exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
+ include_annotations: ["android.platform.test.annotations.LargeTest"],
+ test_suites: ["general-tests", "some-compat"],
+ }`
+
+ android.GroupFixturePreparers(
+ java.PrepareForTestWithJavaDefaultModules,
+ android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
+ ).ExtendWithErrorHandler(
+ android.FixtureExpectsAtLeastOneErrorMatchingPattern("TestSuite some-compat exists in the base")).
+ RunTestWithBp(t, badBp)
+}
+
// Use for situations where the entries map contains pairs: [srcPath:installedPath1, srcPath2:installedPath2]
// and we want to compare the RHS of the pairs, i.e. installedPath1, installedPath2
func assertEntryPairValues(t *testing.T, actual []string, expected []string) {