Merge "Add test_module_config_host" into main

This commit is contained in:
Ronald Braunstein
2024-03-29 22:15:08 +00:00
committed by Gerrit Code Review
5 changed files with 317 additions and 43 deletions

View File

@@ -1337,6 +1337,8 @@ func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
OutputFile: a.OutputFile(), OutputFile: a.OutputFile(),
TestConfig: a.testConfig, TestConfig: a.testConfig,
HostRequiredModuleNames: a.HostRequiredModuleNames(), HostRequiredModuleNames: a.HostRequiredModuleNames(),
TestSuites: a.testProperties.Test_suites,
IsHost: false,
}) })
} }

View File

@@ -1438,6 +1438,14 @@ func (j *TestHost) GenerateAndroidBuildActions(ctx android.ModuleContext) {
j.Test.generateAndroidBuildActionsWithConfig(ctx, configs) j.Test.generateAndroidBuildActionsWithConfig(ctx, configs)
android.SetProvider(ctx, testing.TestModuleProviderKey, testing.TestModuleProviderData{}) 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) { func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {

View File

@@ -16,6 +16,11 @@ type BaseTestProviderData struct {
TestConfig android.Path TestConfig android.Path
// Other modules we require to be installed to run tests. We expect base to build them. // Other modules we require to be installed to run tests. We expect base to build them.
HostRequiredModuleNames []string 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]() var BaseTestProviderKey = blueprint.NewProvider[BaseTestProviderData]()

View File

@@ -17,6 +17,7 @@ func init() {
// Register the license_kind module type. // Register the license_kind module type.
func RegisterTestModuleConfigBuildComponents(ctx android.RegistrationContext) { func RegisterTestModuleConfigBuildComponents(ctx android.RegistrationContext) {
ctx.RegisterModuleType("test_module_config", TestModuleConfigFactory) ctx.RegisterModuleType("test_module_config", TestModuleConfigFactory)
ctx.RegisterModuleType("test_module_config_host", TestModuleConfigHostFactory)
} }
type testModuleConfigModule struct { type testModuleConfigModule struct {
@@ -32,6 +33,12 @@ type testModuleConfigModule struct {
provider tradefed.BaseTestProviderData 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. // Properties to list in Android.bp for this module.
type tradefedProperties struct { type tradefedProperties struct {
// Module name of the base test that we will run. // Module name of the base test that we will run.
@@ -69,6 +76,7 @@ type dependencyTag struct {
var ( var (
testModuleConfigTag = dependencyTag{name: "TestModuleConfigBase"} testModuleConfigTag = dependencyTag{name: "TestModuleConfigBase"}
testModuleConfigHostTag = dependencyTag{name: "TestModuleConfigHostBase"}
pctx = android.NewPackageContext("android/soong/tradefed_modules") pctx = android.NewPackageContext("android/soong/tradefed_modules")
) )
@@ -77,6 +85,10 @@ func (m *testModuleConfigModule) InstallInTestcases() bool {
} }
func (m *testModuleConfigModule) DepsMutator(ctx android.BottomUpMutatorContext) { 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) ctx.AddDependency(ctx.Module(), testModuleConfigTag, *m.Base)
} }
@@ -143,35 +155,41 @@ func (m *testModuleConfigModule) composeOptions() []tradefed.Option {
// //
// If we change to symlinks, this all needs to change. // If we change to symlinks, this all needs to change.
func (m *testModuleConfigModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { 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
} }
})
// 1) A manifest file listing the base. // Any test suites in base should not be repeated in the derived class, except "general-tests".
installDir := android.PathForModuleInstall(ctx, ctx.ModuleName()) // We may restrict derived tests to only be "general-tests" as it doesn't make sense to add a slice
out := android.PathForModuleOut(ctx, "test_module_config.manifest") // of a test to compatibility suite.
android.WriteFileRule(ctx, out, fmt.Sprintf("{%q: %q}", "base", *m.tradefedProperties.Base)) //
ctx.InstallFile(installDir, out.Base(), out) // 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
}
// 2) Module.config / AndroidTest.xml derivedSuites := make(map[string]bool)
// Note, there is still a "test-tag" element with base's module name, but // See if any suites in base is also in derived (other than general-tests)
// Tradefed team says its ignored anyway. for _, s := range m.tradefedProperties.Test_suites {
m.testConfig = m.fixTestConfig(ctx, m.provider.TestConfig) 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
}
}
// 3) Write ARCH/Module.apk in testcases. return true
// 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.
} }
func TestModuleConfigFactory() android.Module { func TestModuleConfigFactory() android.Module {
@@ -184,6 +202,16 @@ func TestModuleConfigFactory() android.Module {
return module 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 // Implements android.AndroidMkEntriesProvider
var _ android.AndroidMkEntriesProvider = (*testModuleConfigModule)(nil) var _ android.AndroidMkEntriesProvider = (*testModuleConfigModule)(nil)
@@ -198,22 +226,96 @@ func (m *testModuleConfigModule) AndroidMkEntries() []android.AndroidMkEntries {
// Out update config file with extra options. // Out update config file with extra options.
entries.SetPath("LOCAL_FULL_TEST_CONFIG", m.testConfig) entries.SetPath("LOCAL_FULL_TEST_CONFIG", m.testConfig)
entries.SetString("LOCAL_MODULE_TAGS", "tests") 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 // Don't append to base's test-suites, only use the ones we define, so clear it before
// appending to it. // appending to it.
entries.SetString("LOCAL_COMPATIBILITY_SUITE", "") entries.SetString("LOCAL_COMPATIBILITY_SUITE", "")
if len(m.tradefedProperties.Test_suites) > 0 {
entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...) entries.AddCompatibilityTestSuites(m.tradefedProperties.Test_suites...)
} else {
entries.AddCompatibilityTestSuites("null-suite") 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 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)

View File

@@ -16,6 +16,7 @@ package tradefed_modules
import ( import (
"android/soong/android" "android/soong/android"
"android/soong/java" "android/soong/java"
"strconv"
"strings" "strings"
"testing" "testing"
) )
@@ -43,6 +44,7 @@ const bp = `
base: "base", base: "base",
exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"], exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
include_annotations: ["android.platform.test.annotations.LargeTest"], include_annotations: ["android.platform.test.annotations.LargeTest"],
test_suites: ["general-tests"],
} }
` `
@@ -92,7 +94,7 @@ func TestModuleConfigOptions(t *testing.T) {
} }
// Ensure we error for a base we don't support. // Ensure we error for a base we don't support.
func TestModuleConfigBadBaseShouldFail(t *testing.T) { func TestModuleConfigWithHostBaseShouldFailWithExplicitMessage(t *testing.T) {
badBp := ` badBp := `
java_test_host { java_test_host {
name: "base", name: "base",
@@ -104,16 +106,60 @@ func TestModuleConfigBadBaseShouldFail(t *testing.T) {
base: "base", base: "base",
exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"], exclude_filters: ["android.test.example.devcodelab.DevCodelabTest#testHelloFail"],
include_annotations: ["android.platform.test.annotations.LargeTest"], include_annotations: ["android.platform.test.annotations.LargeTest"],
test_suites: ["general-tests"],
}` }`
ctx := android.GroupFixturePreparers( android.GroupFixturePreparers(
java.PrepareForTestWithJavaDefaultModules, java.PrepareForTestWithJavaDefaultModules,
android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents), android.FixtureRegisterWithContext(RegisterTestModuleConfigBuildComponents),
).ExtendWithErrorHandler( ).ExtendWithErrorHandler(
android.FixtureExpectsAtLeastOneErrorMatchingPattern("does not provide test BaseTestProviderData")). android.FixtureExpectsAtLeastOneErrorMatchingPattern("'java_test_host' module used as base, but 'android_test' expected")).
RunTestWithBp(t, badBp) 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. // Ensure we error for a base we don't support.
@@ -128,6 +174,7 @@ func TestModuleConfigNoFiltersOrAnnotationsShouldFail(t *testing.T) {
test_module_config { test_module_config {
name: "derived_test", name: "derived_test",
base: "base", base: "base",
test_suites: ["general-tests"],
}` }`
ctx := android.GroupFixturePreparers( ctx := android.GroupFixturePreparers(
@@ -152,12 +199,14 @@ func TestModuleConfigMultipleDerivedTestsWriteDistinctMakeEntries(t *testing.T)
name: "derived_test", name: "derived_test",
base: "base", base: "base",
include_annotations: ["android.platform.test.annotations.LargeTest"], include_annotations: ["android.platform.test.annotations.LargeTest"],
test_suites: ["general-tests"],
} }
test_module_config { test_module_config {
name: "another_derived_test", name: "another_derived_test",
base: "base", base: "base",
include_annotations: ["android.platform.test.annotations.LargeTest"], include_annotations: ["android.platform.test.annotations.LargeTest"],
test_suites: ["general-tests"],
}` }`
ctx := android.GroupFixturePreparers( ctx := android.GroupFixturePreparers(
@@ -190,6 +239,114 @@ func TestModuleConfigMultipleDerivedTestsWriteDistinctMakeEntries(t *testing.T)
} }
} }
// 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] // 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 // and we want to compare the RHS of the pairs, i.e. installedPath1, installedPath2
func assertEntryPairValues(t *testing.T, actual []string, expected []string) { func assertEntryPairValues(t *testing.T, actual []string, expected []string) {