123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 |
- // Copyright 2019 Google Inc. All rights reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package sdk
- import (
- "fmt"
- "path/filepath"
- "strings"
- "testing"
- "android/soong/android"
- "android/soong/apex"
- "android/soong/cc"
- "android/soong/genrule"
- "android/soong/java"
- "github.com/google/blueprint/proptools"
- )
- // Prepare for running an sdk test with an apex.
- var prepareForSdkTestWithApex = android.GroupFixturePreparers(
- apex.PrepareForTestWithApexBuildComponents,
- android.FixtureAddTextFile("sdk/tests/Android.bp", `
- apex_key {
- name: "myapex.key",
- public_key: "myapex.avbpubkey",
- private_key: "myapex.pem",
- }
- android_app_certificate {
- name: "myapex.cert",
- certificate: "myapex",
- }
- `),
- android.FixtureMergeMockFs(map[string][]byte{
- "apex_manifest.json": nil,
- "system/sepolicy/apex/myapex-file_contexts": nil,
- "system/sepolicy/apex/myapex2-file_contexts": nil,
- "system/sepolicy/apex/mysdkapex-file_contexts": nil,
- "sdk/tests/myapex.avbpubkey": nil,
- "sdk/tests/myapex.pem": nil,
- "sdk/tests/myapex.x509.pem": nil,
- "sdk/tests/myapex.pk8": nil,
- }),
- )
- // Legacy preparer used for running tests within the sdk package.
- //
- // This includes everything that was needed to run any test in the sdk package prior to the
- // introduction of the test fixtures. Tests that are being converted to use fixtures directly
- // rather than through the testSdkError() and testSdkWithFs() methods should avoid using this and
- // instead should use the various preparers directly using android.GroupFixturePreparers(...) to
- // group them when necessary.
- //
- // deprecated
- var prepareForSdkTest = android.GroupFixturePreparers(
- cc.PrepareForTestWithCcDefaultModules,
- genrule.PrepareForTestWithGenRuleBuildComponents,
- java.PrepareForTestWithJavaBuildComponents,
- PrepareForTestWithSdkBuildComponents,
- prepareForSdkTestWithApex,
- cc.PrepareForTestOnWindows,
- android.FixtureModifyConfig(func(config android.Config) {
- // Add windows as a default disable OS to test behavior when some OS variants
- // are disabled.
- config.Targets[android.Windows] = []android.Target{
- {android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", true},
- }
- }),
- // Add a build number file.
- android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
- variables.BuildNumberFile = proptools.StringPtr(BUILD_NUMBER_FILE)
- }),
- // Make sure that every test provides all the source files.
- android.PrepareForTestDisallowNonExistentPaths,
- android.MockFS{
- "Test.java": nil,
- }.AddToFixture(),
- )
- var PrepareForTestWithSdkBuildComponents = android.GroupFixturePreparers(
- android.FixtureRegisterWithContext(registerModuleExportsBuildComponents),
- android.FixtureRegisterWithContext(registerSdkBuildComponents),
- )
- func testSdkWithFs(t *testing.T, bp string, fs android.MockFS) *android.TestResult {
- t.Helper()
- return android.GroupFixturePreparers(
- prepareForSdkTest,
- fs.AddToFixture(),
- ).RunTestWithBp(t, bp)
- }
- func testSdkError(t *testing.T, pattern, bp string) {
- t.Helper()
- prepareForSdkTest.
- ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
- RunTestWithBp(t, bp)
- }
- func ensureListContains(t *testing.T, result []string, expected string) {
- t.Helper()
- if !android.InList(expected, result) {
- t.Errorf("%q is not found in %v", expected, result)
- }
- }
- // Analyse the sdk build rules to extract information about what it is doing.
- //
- // e.g. find the src/dest pairs from each cp command, the various zip files
- // generated, etc.
- func getSdkSnapshotBuildInfo(t *testing.T, result *android.TestResult, sdk *sdk) *snapshotBuildInfo {
- info := &snapshotBuildInfo{
- t: t,
- r: result,
- androidBpContents: sdk.GetAndroidBpContentsForTests(),
- infoContents: sdk.GetInfoContentsForTests(),
- snapshotTestCustomizations: map[snapshotTest]*snapshotTestCustomization{},
- targetBuildRelease: sdk.builderForTests.targetBuildRelease,
- }
- buildParams := sdk.BuildParamsForTests()
- copyRules := &strings.Builder{}
- otherCopyRules := &strings.Builder{}
- snapshotDirPrefix := sdk.builderForTests.snapshotDir.String() + "/"
- seenBuildNumberFile := false
- for _, bp := range buildParams {
- switch bp.Rule.String() {
- case android.Cp.String():
- output := bp.Output
- // Get destination relative to the snapshot root
- dest := output.Rel()
- src := android.NormalizePathForTesting(bp.Input)
- // We differentiate between copy rules for the snapshot, and copy rules for the install file.
- if strings.HasPrefix(output.String(), snapshotDirPrefix) {
- // Don't include the build-number.txt file in the copy rules as that would break lots of
- // tests, just verify that it is copied here as it should appear in every snapshot.
- if output.Base() == BUILD_NUMBER_FILE {
- seenBuildNumberFile = true
- } else {
- // Get source relative to build directory.
- _, _ = fmt.Fprintf(copyRules, "%s -> %s\n", src, dest)
- }
- info.snapshotContents = append(info.snapshotContents, dest)
- } else {
- _, _ = fmt.Fprintf(otherCopyRules, "%s -> %s\n", src, dest)
- }
- case repackageZip.String():
- // Add the destdir to the snapshot contents as that is effectively where
- // the content of the repackaged zip is copied.
- dest := bp.Args["destdir"]
- info.snapshotContents = append(info.snapshotContents, dest)
- case zipFiles.String():
- // This could be an intermediate zip file and not the actual output zip.
- // In that case this will be overridden when the rule to merge the zips
- // is processed.
- info.outputZip = android.NormalizePathForTesting(bp.Output)
- case mergeZips.String():
- // Copy the current outputZip to the intermediateZip.
- info.intermediateZip = info.outputZip
- mergeInput := android.NormalizePathForTesting(bp.Input)
- if info.intermediateZip != mergeInput {
- t.Errorf("Expected intermediate zip %s to be an input to merge zips but found %s instead",
- info.intermediateZip, mergeInput)
- }
- // Override output zip (which was actually the intermediate zip file) with the actual
- // output zip.
- info.outputZip = android.NormalizePathForTesting(bp.Output)
- // Save the zips to be merged into the intermediate zip.
- info.mergeZips = android.NormalizePathsForTesting(bp.Inputs)
- }
- }
- if !seenBuildNumberFile {
- panic(fmt.Sprintf("Every snapshot must include the %s file", BUILD_NUMBER_FILE))
- }
- info.copyRules = copyRules.String()
- info.otherCopyRules = otherCopyRules.String()
- return info
- }
- // The enum of different sdk snapshot tests performed by CheckSnapshot.
- type snapshotTest int
- const (
- // The enumeration of the different test configurations.
- // A test with the snapshot/Android.bp file but without the original Android.bp file.
- checkSnapshotWithoutSource snapshotTest = iota
- // A test with both the original source and the snapshot, with the source preferred.
- checkSnapshotWithSourcePreferred
- // A test with both the original source and the snapshot, with the snapshot preferred.
- checkSnapshotPreferredWithSource
- // The directory into which the snapshot will be 'unpacked'.
- snapshotSubDir = "snapshot"
- )
- // Check the snapshot build rules.
- //
- // Takes a list of functions which check different facets of the snapshot build rules.
- // Allows each test to customize what is checked without duplicating lots of code
- // or proliferating check methods of different flavors.
- func CheckSnapshot(t *testing.T, result *android.TestResult, name string, dir string, checkers ...snapshotBuildInfoChecker) {
- t.Helper()
- // The sdk CommonOS variant is always responsible for generating the snapshot.
- variant := android.CommonOS.Name
- sdk := result.Module(name, variant).(*sdk)
- snapshotBuildInfo := getSdkSnapshotBuildInfo(t, result, sdk)
- // Check state of the snapshot build.
- for _, checker := range checkers {
- checker(snapshotBuildInfo)
- }
- // Make sure that the generated zip file is in the correct place.
- actual := snapshotBuildInfo.outputZip
- if dir != "" {
- dir = filepath.Clean(dir) + "/"
- }
- suffix := "-" + soongSdkSnapshotVersionCurrent
- expectedZipPath := fmt.Sprintf(".intermediates/%s%s/%s/%s%s.zip", dir, name, variant, name, suffix)
- android.AssertStringEquals(t, "Snapshot zip file in wrong place", expectedZipPath, actual)
- // Populate a mock filesystem with the files that would have been copied by
- // the rules.
- fs := android.MockFS{}
- for _, dest := range snapshotBuildInfo.snapshotContents {
- fs[filepath.Join(snapshotSubDir, dest)] = nil
- }
- fs[filepath.Join(snapshotSubDir, "Android.bp")] = []byte(snapshotBuildInfo.androidBpContents)
- // If the generated snapshot builders not for the current release then it cannot be loaded by
- // the current release.
- if snapshotBuildInfo.targetBuildRelease != buildReleaseCurrent {
- return
- }
- // The preparers from the original source fixture.
- sourcePreparers := result.Preparer()
- // Preparer to combine the snapshot and the source.
- snapshotPreparer := android.GroupFixturePreparers(sourcePreparers, fs.AddToFixture())
- var runSnapshotTestWithCheckers = func(t *testing.T, testConfig snapshotTest, extraPreparer android.FixturePreparer) {
- t.Helper()
- customization := snapshotBuildInfo.snapshotTestCustomization(testConfig)
- customizedPreparers := android.GroupFixturePreparers(customization.preparers...)
- // TODO(b/183184375): Set Config.TestAllowNonExistentPaths = false to verify that all the
- // files the snapshot needs are actually copied into the snapshot.
- // Run the snapshot with the snapshot preparer and the extra preparer, which must come after as
- // it may need to modify parts of the MockFS populated by the snapshot preparer.
- result := android.GroupFixturePreparers(snapshotPreparer, extraPreparer, customizedPreparers).
- ExtendWithErrorHandler(customization.errorHandler).
- RunTest(t)
- // Perform any additional checks the test need on the result of processing the snapshot.
- for _, checker := range customization.checkers {
- checker(t, result)
- }
- }
- t.Run("snapshot without source", func(t *testing.T) {
- // Remove the source Android.bp file to make sure it works without.
- removeSourceAndroidBp := android.FixtureModifyMockFS(func(fs android.MockFS) {
- delete(fs, "Android.bp")
- })
- runSnapshotTestWithCheckers(t, checkSnapshotWithoutSource, removeSourceAndroidBp)
- })
- t.Run("snapshot with source preferred", func(t *testing.T) {
- runSnapshotTestWithCheckers(t, checkSnapshotWithSourcePreferred, android.NullFixturePreparer)
- })
- t.Run("snapshot preferred with source", func(t *testing.T) {
- // Replace the snapshot/Android.bp file with one where "prefer: false," has been replaced with
- // "prefer: true,"
- preferPrebuilts := android.FixtureModifyMockFS(func(fs android.MockFS) {
- snapshotBpFile := filepath.Join(snapshotSubDir, "Android.bp")
- unpreferred := string(fs[snapshotBpFile])
- fs[snapshotBpFile] = []byte(strings.ReplaceAll(unpreferred, "prefer: false,", "prefer: true,"))
- })
- runSnapshotTestWithCheckers(t, checkSnapshotPreferredWithSource, preferPrebuilts)
- })
- }
- type snapshotBuildInfoChecker func(info *snapshotBuildInfo)
- // Check that the snapshot's generated Android.bp is correct.
- //
- // Both the expected and actual string are both trimmed before comparing.
- func checkAndroidBpContents(expected string) snapshotBuildInfoChecker {
- return func(info *snapshotBuildInfo) {
- info.t.Helper()
- android.AssertTrimmedStringEquals(info.t, "Android.bp contents do not match", expected, info.androidBpContents)
- }
- }
- // Check that the snapshot's copy rules are correct.
- //
- // The copy rules are formatted as <src> -> <dest>, one per line and then compared
- // to the supplied expected string. Both the expected and actual string are trimmed
- // before comparing.
- func checkAllCopyRules(expected string) snapshotBuildInfoChecker {
- return func(info *snapshotBuildInfo) {
- info.t.Helper()
- android.AssertTrimmedStringEquals(info.t, "Incorrect copy rules", expected, info.copyRules)
- }
- }
- func checkAllOtherCopyRules(expected string) snapshotBuildInfoChecker {
- return func(info *snapshotBuildInfo) {
- info.t.Helper()
- android.AssertTrimmedStringEquals(info.t, "Incorrect copy rules", expected, info.otherCopyRules)
- }
- }
- // Check that the specified paths match the list of zips to merge with the intermediate zip.
- func checkMergeZips(expected ...string) snapshotBuildInfoChecker {
- return func(info *snapshotBuildInfo) {
- info.t.Helper()
- if info.intermediateZip == "" {
- info.t.Errorf("No intermediate zip file was created")
- }
- android.AssertDeepEquals(info.t, "mismatching merge zip files", expected, info.mergeZips)
- }
- }
- // Check that the snapshot's info contents are ciorrect.
- //
- // Both the expected and actual string are both trimmed before comparing.
- func checkInfoContents(config android.Config, expected string) snapshotBuildInfoChecker {
- return func(info *snapshotBuildInfo) {
- info.t.Helper()
- android.AssertTrimmedStringEquals(info.t, "info contents do not match",
- expected, android.StringRelativeToTop(config, info.infoContents))
- }
- }
- type resultChecker func(t *testing.T, result *android.TestResult)
- // snapshotTestPreparer registers a preparer that will be used to customize the specified
- // snapshotTest.
- func snapshotTestPreparer(snapshotTest snapshotTest, preparer android.FixturePreparer) snapshotBuildInfoChecker {
- return func(info *snapshotBuildInfo) {
- customization := info.snapshotTestCustomization(snapshotTest)
- customization.preparers = append(customization.preparers, preparer)
- }
- }
- // snapshotTestChecker registers a checker that will be run against the result of processing the
- // generated snapshot for the specified snapshotTest.
- func snapshotTestChecker(snapshotTest snapshotTest, checker resultChecker) snapshotBuildInfoChecker {
- return func(info *snapshotBuildInfo) {
- customization := info.snapshotTestCustomization(snapshotTest)
- customization.checkers = append(customization.checkers, checker)
- }
- }
- // snapshotTestErrorHandler registers an error handler to use when processing the snapshot
- // in the specific test case.
- //
- // Generally, the snapshot should work with all the test cases but some do not and just in case
- // there are a lot of issues to resolve, or it will take a lot of time this is a
- // get-out-of-jail-free card that allows progress to be made.
- //
- // deprecated: should only be used as a temporary workaround with an attached to do and bug.
- func snapshotTestErrorHandler(snapshotTest snapshotTest, handler android.FixtureErrorHandler) snapshotBuildInfoChecker {
- return func(info *snapshotBuildInfo) {
- customization := info.snapshotTestCustomization(snapshotTest)
- customization.errorHandler = handler
- }
- }
- // Encapsulates information provided by each test to customize a specific snapshotTest.
- type snapshotTestCustomization struct {
- // Preparers that are used to customize the test fixture before running the test.
- preparers []android.FixturePreparer
- // Checkers that are run on the result of processing the preferred snapshot in a specific test
- // case.
- checkers []resultChecker
- // Specify an error handler for when processing a specific test case.
- //
- // In some cases the generated snapshot cannot be used in a test configuration. Those cases are
- // invariably bugs that need to be resolved but sometimes that can take a while. This provides a
- // mechanism to temporarily ignore that error.
- errorHandler android.FixtureErrorHandler
- }
- // Encapsulates information about the snapshot build structure in order to insulate tests from
- // knowing too much about internal structures.
- //
- // All source/input paths are relative either the build directory. All dest/output paths are
- // relative to the snapshot root directory.
- type snapshotBuildInfo struct {
- t *testing.T
- // The result from RunTest()
- r *android.TestResult
- // The contents of the generated Android.bp file
- androidBpContents string
- // The contents of the info file.
- infoContents string
- // The paths, relative to the snapshot root, of all files and directories copied into the
- // snapshot.
- snapshotContents []string
- // A formatted representation of the src/dest pairs for a snapshot, one pair per line,
- // of the format src -> dest
- copyRules string
- // A formatted representation of the src/dest pairs for files not in a snapshot, one pair
- // per line, of the format src -> dest
- otherCopyRules string
- // The path to the intermediate zip, which is a zip created from the source files copied
- // into the snapshot directory and which will be merged with other zips to form the final output.
- // Is am empty string if there is no intermediate zip because there are no zips to merge in.
- intermediateZip string
- // The paths to the zips to merge into the output zip, does not include the intermediate
- // zip.
- mergeZips []string
- // The final output zip.
- outputZip string
- // The target build release.
- targetBuildRelease *buildRelease
- // The test specific customizations for each snapshot test.
- snapshotTestCustomizations map[snapshotTest]*snapshotTestCustomization
- }
- // snapshotTestCustomization gets the test specific customization for the specified snapshotTest.
- //
- // If no customization was created previously then it creates a default customization.
- func (i *snapshotBuildInfo) snapshotTestCustomization(snapshotTest snapshotTest) *snapshotTestCustomization {
- customization := i.snapshotTestCustomizations[snapshotTest]
- if customization == nil {
- customization = &snapshotTestCustomization{
- errorHandler: android.FixtureExpectsNoErrors,
- }
- i.snapshotTestCustomizations[snapshotTest] = customization
- }
- return customization
- }
|