Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c654ad16 authored by Ronald Braunstein's avatar Ronald Braunstein Committed by Gerrit Code Review
Browse files

Merge "Add test_module_config_host" into main

parents e2524fb8 1a6e7c03
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1337,6 +1337,8 @@ func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
		OutputFile:              a.OutputFile(),
		TestConfig:              a.testConfig,
		HostRequiredModuleNames: a.HostRequiredModuleNames(),
		TestSuites:              a.testProperties.Test_suites,
		IsHost:                  false,
	})
}

+8 −0
Original line number Diff line number Diff line
@@ -1438,6 +1438,14 @@ func (j *TestHost) GenerateAndroidBuildActions(ctx android.ModuleContext) {

	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) {
+5 −0
Original line number Diff line number Diff line
@@ -16,6 +16,11 @@ type BaseTestProviderData struct {
	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]()
+140 −38
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ func init() {
// 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 @@ type testModuleConfigModule struct {
	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.
@@ -69,6 +76,7 @@ type dependencyTag struct {

var (
	testModuleConfigTag     = dependencyTag{name: "TestModuleConfigBase"}
	testModuleConfigHostTag = dependencyTag{name: "TestModuleConfigHostBase"}
	pctx                    = android.NewPackageContext("android/soong/tradefed_modules")
)

@@ -77,6 +85,10 @@ func (m *testModuleConfigModule) InstallInTestcases() bool {
}

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 @@ func (m *testModuleConfigModule) composeOptions() []tradefed.Option {
//
// 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
}
	})

	// 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)
// 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
	}

	// 3) Write ARCH/Module.apk in testcases.
	// Handled by soong_app_prebuilt and OutputFile in entries.
	// Nothing to do here.
	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
		}
	}

	// 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 @@ func TestModuleConfigFactory() android.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
var _ android.AndroidMkEntriesProvider = (*testModuleConfigModule)(nil)

@@ -198,22 +226,96 @@ func (m *testModuleConfigModule) AndroidMkEntries() []android.AndroidMkEntries {
		// 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

		// 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", "")
		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
}

		// 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...)
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 {
			entries.AddCompatibilityTestSuites("null-suite")
			ctx.ModuleErrorf("'%s' module used as base but it is not a '%s' module.", *m.Base, baseType)
		}
	})
	return entriesList
}

// 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)
+161 −4
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ package tradefed_modules
import (
	"android/soong/android"
	"android/soong/java"
	"strconv"
	"strings"
	"testing"
)
@@ -43,6 +44,7 @@ const bp = `
                        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 @@ func TestModuleConfigOptions(t *testing.T) {
}

// 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 @@ func TestModuleConfigBadBaseShouldFail(t *testing.T) {
                        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 @@ func TestModuleConfigNoFiltersOrAnnotationsShouldFail(t *testing.T) {
                test_module_config {
                        name: "derived_test",
                        base: "base",
                        test_suites: ["general-tests"],
                }`

	ctx := android.GroupFixturePreparers(
@@ -152,12 +199,14 @@ func TestModuleConfigMultipleDerivedTestsWriteDistinctMakeEntries(t *testing.T)
                        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 @@ 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]
// and we want to compare the RHS of the pairs, i.e. installedPath1, installedPath2
func assertEntryPairValues(t *testing.T, actual []string, expected []string) {