|  | // Copyright 2018 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 etc | 
|  |  | 
|  | import ( | 
|  | "fmt" | 
|  | "os" | 
|  | "path/filepath" | 
|  | "testing" | 
|  |  | 
|  | "github.com/google/blueprint/proptools" | 
|  |  | 
|  | "android/soong/android" | 
|  | "android/soong/bazel/cquery" | 
|  | "android/soong/snapshot" | 
|  | ) | 
|  |  | 
|  | func TestMain(m *testing.M) { | 
|  | os.Exit(m.Run()) | 
|  | } | 
|  |  | 
|  | var prepareForPrebuiltEtcTest = android.GroupFixturePreparers( | 
|  | android.PrepareForTestWithArchMutator, | 
|  | PrepareForTestWithPrebuiltEtc, | 
|  | android.FixtureMergeMockFs(android.MockFS{ | 
|  | "foo.conf": nil, | 
|  | "bar.conf": nil, | 
|  | "baz.conf": nil, | 
|  | }), | 
|  | ) | 
|  |  | 
|  | var prepareForPrebuiltEtcSnapshotTest = android.GroupFixturePreparers( | 
|  | prepareForPrebuiltEtcTest, | 
|  | android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) { | 
|  | snapshot.VendorSnapshotImageSingleton.Init(ctx) | 
|  | snapshot.RecoverySnapshotImageSingleton.Init(ctx) | 
|  | }), | 
|  | android.FixtureModifyConfig(func(config android.Config) { | 
|  | config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current") | 
|  | config.TestProductVariables.RecoverySnapshotVersion = proptools.StringPtr("current") | 
|  | }), | 
|  | ) | 
|  |  | 
|  | func TestPrebuiltEtcVariants(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcTest.RunTestWithBp(t, ` | 
|  | prebuilt_etc { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | } | 
|  | prebuilt_etc { | 
|  | name: "bar.conf", | 
|  | src: "bar.conf", | 
|  | recovery_available: true, | 
|  | } | 
|  | prebuilt_etc { | 
|  | name: "baz.conf", | 
|  | src: "baz.conf", | 
|  | recovery: true, | 
|  | } | 
|  | `) | 
|  |  | 
|  | foo_variants := result.ModuleVariantsForTests("foo.conf") | 
|  | if len(foo_variants) != 1 { | 
|  | t.Errorf("expected 1, got %#v", foo_variants) | 
|  | } | 
|  |  | 
|  | bar_variants := result.ModuleVariantsForTests("bar.conf") | 
|  | if len(bar_variants) != 2 { | 
|  | t.Errorf("expected 2, got %#v", bar_variants) | 
|  | } | 
|  |  | 
|  | baz_variants := result.ModuleVariantsForTests("baz.conf") | 
|  | if len(baz_variants) != 1 { | 
|  | t.Errorf("expected 1, got %#v", baz_variants) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestPrebuiltEtcOutputPath(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcTest.RunTestWithBp(t, ` | 
|  | prebuilt_etc { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | filename: "foo.installed.conf", | 
|  | } | 
|  | `) | 
|  |  | 
|  | p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc) | 
|  | android.AssertStringEquals(t, "output file path", "foo.installed.conf", p.outputFilePath.Base()) | 
|  | } | 
|  |  | 
|  | func TestPrebuiltEtcGlob(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcTest.RunTestWithBp(t, ` | 
|  | prebuilt_etc { | 
|  | name: "my_foo", | 
|  | src: "foo.*", | 
|  | } | 
|  | prebuilt_etc { | 
|  | name: "my_bar", | 
|  | src: "bar.*", | 
|  | filename_from_src: true, | 
|  | } | 
|  | `) | 
|  |  | 
|  | p := result.Module("my_foo", "android_arm64_armv8-a").(*PrebuiltEtc) | 
|  | android.AssertStringEquals(t, "my_foo output file path", "my_foo", p.outputFilePath.Base()) | 
|  |  | 
|  | p = result.Module("my_bar", "android_arm64_armv8-a").(*PrebuiltEtc) | 
|  | android.AssertStringEquals(t, "my_bar output file path", "bar.conf", p.outputFilePath.Base()) | 
|  | } | 
|  |  | 
|  | func TestPrebuiltEtcAndroidMk(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcTest.RunTestWithBp(t, ` | 
|  | prebuilt_etc { | 
|  | name: "foo", | 
|  | src: "foo.conf", | 
|  | owner: "abc", | 
|  | filename_from_src: true, | 
|  | required: ["modA", "moduleB"], | 
|  | host_required: ["hostModA", "hostModB"], | 
|  | target_required: ["targetModA"], | 
|  | } | 
|  | `) | 
|  |  | 
|  | expected := map[string][]string{ | 
|  | "LOCAL_MODULE":                  {"foo"}, | 
|  | "LOCAL_MODULE_CLASS":            {"ETC"}, | 
|  | "LOCAL_MODULE_OWNER":            {"abc"}, | 
|  | "LOCAL_INSTALLED_MODULE_STEM":   {"foo.conf"}, | 
|  | "LOCAL_REQUIRED_MODULES":        {"modA", "moduleB"}, | 
|  | "LOCAL_HOST_REQUIRED_MODULES":   {"hostModA", "hostModB"}, | 
|  | "LOCAL_TARGET_REQUIRED_MODULES": {"targetModA"}, | 
|  | "LOCAL_SOONG_MODULE_TYPE":       {"prebuilt_etc"}, | 
|  | } | 
|  |  | 
|  | mod := result.Module("foo", "android_arm64_armv8-a").(*PrebuiltEtc) | 
|  | entries := android.AndroidMkEntriesForTest(t, result.TestContext, mod)[0] | 
|  | for k, expectedValue := range expected { | 
|  | if value, ok := entries.EntryMap[k]; ok { | 
|  | android.AssertDeepEquals(t, k, expectedValue, value) | 
|  | } else { | 
|  | t.Errorf("No %s defined, saw %q", k, entries.EntryMap) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestPrebuiltEtcRelativeInstallPathInstallDirPath(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcTest.RunTestWithBp(t, ` | 
|  | prebuilt_etc { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | relative_install_path: "bar", | 
|  | } | 
|  | `) | 
|  |  | 
|  | p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc) | 
|  | expected := "out/soong/target/product/test_device/system/etc/bar" | 
|  | android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath) | 
|  | } | 
|  |  | 
|  | func TestPrebuiltEtcCannotSetRelativeInstallPathAndSubDir(t *testing.T) { | 
|  | prepareForPrebuiltEtcTest. | 
|  | ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("relative_install_path is set. Cannot set sub_dir")). | 
|  | RunTestWithBp(t, ` | 
|  | prebuilt_etc { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | sub_dir: "bar", | 
|  | relative_install_path: "bar", | 
|  | } | 
|  | `) | 
|  | } | 
|  |  | 
|  | func TestPrebuiltEtcHost(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcTest.RunTestWithBp(t, ` | 
|  | prebuilt_etc_host { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | } | 
|  | `) | 
|  |  | 
|  | buildOS := result.Config.BuildOS.String() | 
|  | p := result.Module("foo.conf", buildOS+"_common").(*PrebuiltEtc) | 
|  | if !p.Host() { | 
|  | t.Errorf("host bit is not set for a prebuilt_etc_host module.") | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestPrebuiltEtcAllowMissingDependencies(t *testing.T) { | 
|  | result := android.GroupFixturePreparers( | 
|  | prepareForPrebuiltEtcTest, | 
|  | android.PrepareForTestDisallowNonExistentPaths, | 
|  | android.FixtureModifyConfig( | 
|  | func(config android.Config) { | 
|  | config.TestProductVariables.Allow_missing_dependencies = proptools.BoolPtr(true) | 
|  | }), | 
|  | ).RunTestWithBp(t, ` | 
|  | prebuilt_etc { | 
|  | name: "foo.conf", | 
|  | filename_from_src: true, | 
|  | arch: { | 
|  | x86: { | 
|  | src: "x86.conf", | 
|  | }, | 
|  | }, | 
|  | } | 
|  | `) | 
|  |  | 
|  | android.AssertStringEquals(t, "expected error rule", "android/soong/android.Error", | 
|  | result.ModuleForTests("foo.conf", "android_arm64_armv8-a").Output("foo.conf").Rule.String()) | 
|  | } | 
|  |  | 
|  | func TestPrebuiltRootInstallDirPath(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcTest.RunTestWithBp(t, ` | 
|  | prebuilt_root { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | filename: "foo.conf", | 
|  | } | 
|  | `) | 
|  |  | 
|  | p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc) | 
|  | expected := "out/soong/target/product/test_device/system" | 
|  | android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath) | 
|  | } | 
|  |  | 
|  | func TestPrebuiltRootInstallDirPathValidate(t *testing.T) { | 
|  | prepareForPrebuiltEtcTest.ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern("filename cannot contain separator")).RunTestWithBp(t, ` | 
|  | prebuilt_root { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | filename: "foo/bar.conf", | 
|  | } | 
|  | `) | 
|  | } | 
|  |  | 
|  | func TestPrebuiltUserShareInstallDirPath(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcTest.RunTestWithBp(t, ` | 
|  | prebuilt_usr_share { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | sub_dir: "bar", | 
|  | } | 
|  | `) | 
|  |  | 
|  | p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc) | 
|  | expected := "out/soong/target/product/test_device/system/usr/share/bar" | 
|  | android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath) | 
|  | } | 
|  |  | 
|  | func TestPrebuiltUserShareHostInstallDirPath(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcTest.RunTestWithBp(t, ` | 
|  | prebuilt_usr_share_host { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | sub_dir: "bar", | 
|  | } | 
|  | `) | 
|  |  | 
|  | buildOS := result.Config.BuildOS.String() | 
|  | p := result.Module("foo.conf", buildOS+"_common").(*PrebuiltEtc) | 
|  | expected := filepath.Join("out/soong/host", result.Config.PrebuiltOS(), "usr", "share", "bar") | 
|  | android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath) | 
|  | } | 
|  |  | 
|  | func TestPrebuiltFontInstallDirPath(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcTest.RunTestWithBp(t, ` | 
|  | prebuilt_font { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | } | 
|  | `) | 
|  |  | 
|  | p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc) | 
|  | expected := "out/soong/target/product/test_device/system/fonts" | 
|  | android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath) | 
|  | } | 
|  |  | 
|  | func TestPrebuiltFirmwareDirPath(t *testing.T) { | 
|  | targetPath := "out/soong/target/product/test_device" | 
|  | tests := []struct { | 
|  | description  string | 
|  | config       string | 
|  | expectedPath string | 
|  | }{{ | 
|  | description: "prebuilt: system firmware", | 
|  | config: ` | 
|  | prebuilt_firmware { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | }`, | 
|  | expectedPath: filepath.Join(targetPath, "system/etc/firmware"), | 
|  | }, { | 
|  | description: "prebuilt: vendor firmware", | 
|  | config: ` | 
|  | prebuilt_firmware { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | soc_specific: true, | 
|  | sub_dir: "sub_dir", | 
|  | }`, | 
|  | expectedPath: filepath.Join(targetPath, "vendor/firmware/sub_dir"), | 
|  | }} | 
|  | for _, tt := range tests { | 
|  | t.Run(tt.description, func(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config) | 
|  | p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc) | 
|  | android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath) | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestPrebuiltDSPDirPath(t *testing.T) { | 
|  | targetPath := "out/soong/target/product/test_device" | 
|  | tests := []struct { | 
|  | description  string | 
|  | config       string | 
|  | expectedPath string | 
|  | }{{ | 
|  | description: "prebuilt: system dsp", | 
|  | config: ` | 
|  | prebuilt_dsp { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | }`, | 
|  | expectedPath: filepath.Join(targetPath, "system/etc/dsp"), | 
|  | }, { | 
|  | description: "prebuilt: vendor dsp", | 
|  | config: ` | 
|  | prebuilt_dsp { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | soc_specific: true, | 
|  | sub_dir: "sub_dir", | 
|  | }`, | 
|  | expectedPath: filepath.Join(targetPath, "vendor/dsp/sub_dir"), | 
|  | }} | 
|  | for _, tt := range tests { | 
|  | t.Run(tt.description, func(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config) | 
|  | p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc) | 
|  | android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath) | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestPrebuiltRFSADirPath(t *testing.T) { | 
|  | targetPath := "out/soong/target/product/test_device" | 
|  | tests := []struct { | 
|  | description  string | 
|  | config       string | 
|  | expectedPath string | 
|  | }{{ | 
|  | description: "prebuilt: system rfsa", | 
|  | config: ` | 
|  | prebuilt_rfsa { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | }`, | 
|  | expectedPath: filepath.Join(targetPath, "system/lib/rfsa"), | 
|  | }, { | 
|  | description: "prebuilt: vendor rfsa", | 
|  | config: ` | 
|  | prebuilt_rfsa { | 
|  | name: "foo.conf", | 
|  | src: "foo.conf", | 
|  | soc_specific: true, | 
|  | sub_dir: "sub_dir", | 
|  | }`, | 
|  | expectedPath: filepath.Join(targetPath, "vendor/lib/rfsa/sub_dir"), | 
|  | }} | 
|  | for _, tt := range tests { | 
|  | t.Run(tt.description, func(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcTest.RunTestWithBp(t, tt.config) | 
|  | p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc) | 
|  | android.AssertPathRelativeToTopEquals(t, "install dir", tt.expectedPath, p.installDirPath) | 
|  | }) | 
|  | } | 
|  | } | 
|  |  | 
|  | func checkIfSnapshotTaken(t *testing.T, result *android.TestResult, image string, moduleName string) { | 
|  | checkIfSnapshotExistAsExpected(t, result, image, moduleName, true) | 
|  | } | 
|  |  | 
|  | func checkIfSnapshotNotTaken(t *testing.T, result *android.TestResult, image string, moduleName string) { | 
|  | checkIfSnapshotExistAsExpected(t, result, image, moduleName, false) | 
|  | } | 
|  |  | 
|  | func checkIfSnapshotExistAsExpected(t *testing.T, result *android.TestResult, image string, moduleName string, expectToExist bool) { | 
|  | snapshotSingleton := result.SingletonForTests(image + "-snapshot") | 
|  | archType := "arm64" | 
|  | archVariant := "armv8-a" | 
|  | archDir := fmt.Sprintf("arch-%s", archType) | 
|  |  | 
|  | snapshotDir := fmt.Sprintf("%s-snapshot", image) | 
|  | snapshotVariantPath := filepath.Join(snapshotDir, archType) | 
|  | outputDir := filepath.Join(snapshotVariantPath, archDir, "etc") | 
|  | imageVariant := "" | 
|  | if image == "recovery" { | 
|  | imageVariant = "recovery_" | 
|  | } | 
|  | mod := result.ModuleForTests(moduleName, fmt.Sprintf("android_%s%s_%s", imageVariant, archType, archVariant)) | 
|  | outputFiles := mod.OutputFiles(t, "") | 
|  | if len(outputFiles) != 1 { | 
|  | t.Errorf("%q must have single output\n", moduleName) | 
|  | return | 
|  | } | 
|  | snapshotPath := filepath.Join(outputDir, moduleName) | 
|  |  | 
|  | if expectToExist { | 
|  | out := snapshotSingleton.Output(snapshotPath) | 
|  |  | 
|  | if out.Input.String() != outputFiles[0].String() { | 
|  | t.Errorf("The input of snapshot %q must be %q, but %q", "prebuilt_vendor", out.Input.String(), outputFiles[0]) | 
|  | } | 
|  |  | 
|  | snapshotJsonPath := snapshotPath + ".json" | 
|  |  | 
|  | if snapshotSingleton.MaybeOutput(snapshotJsonPath).Rule == nil { | 
|  | t.Errorf("%q expected but not found", snapshotJsonPath) | 
|  | } | 
|  | } else { | 
|  | out := snapshotSingleton.MaybeOutput(snapshotPath) | 
|  | if out.Rule != nil { | 
|  | t.Errorf("There must be no rule for module %q output file %q", moduleName, outputFiles[0]) | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | func TestPrebuiltTakeSnapshot(t *testing.T) { | 
|  | var testBp = ` | 
|  | prebuilt_etc { | 
|  | name: "prebuilt_vendor", | 
|  | src: "foo.conf", | 
|  | vendor: true, | 
|  | } | 
|  |  | 
|  | prebuilt_etc { | 
|  | name: "prebuilt_vendor_indirect", | 
|  | src: "foo.conf", | 
|  | vendor: true, | 
|  | } | 
|  |  | 
|  | prebuilt_etc { | 
|  | name: "prebuilt_recovery", | 
|  | src: "bar.conf", | 
|  | recovery: true, | 
|  | } | 
|  |  | 
|  | prebuilt_etc { | 
|  | name: "prebuilt_recovery_indirect", | 
|  | src: "bar.conf", | 
|  | recovery: true, | 
|  | } | 
|  | ` | 
|  |  | 
|  | t.Run("prebuilt: vendor and recovery snapshot", func(t *testing.T) { | 
|  | result := prepareForPrebuiltEtcSnapshotTest.RunTestWithBp(t, testBp) | 
|  |  | 
|  | checkIfSnapshotTaken(t, result, "vendor", "prebuilt_vendor") | 
|  | checkIfSnapshotTaken(t, result, "vendor", "prebuilt_vendor_indirect") | 
|  | checkIfSnapshotTaken(t, result, "recovery", "prebuilt_recovery") | 
|  | checkIfSnapshotTaken(t, result, "recovery", "prebuilt_recovery_indirect") | 
|  | }) | 
|  |  | 
|  | t.Run("prebuilt: directed snapshot", func(t *testing.T) { | 
|  | prepareForPrebuiltEtcDirectedSnapshotTest := android.GroupFixturePreparers( | 
|  | prepareForPrebuiltEtcSnapshotTest, | 
|  | android.FixtureModifyConfig(func(config android.Config) { | 
|  | config.TestProductVariables.DirectedVendorSnapshot = true | 
|  | config.TestProductVariables.VendorSnapshotModules = make(map[string]bool) | 
|  | config.TestProductVariables.VendorSnapshotModules["prebuilt_vendor"] = true | 
|  | config.TestProductVariables.DirectedRecoverySnapshot = true | 
|  | config.TestProductVariables.RecoverySnapshotModules = make(map[string]bool) | 
|  | config.TestProductVariables.RecoverySnapshotModules["prebuilt_recovery"] = true | 
|  | }), | 
|  | ) | 
|  |  | 
|  | result := prepareForPrebuiltEtcDirectedSnapshotTest.RunTestWithBp(t, testBp) | 
|  |  | 
|  | checkIfSnapshotTaken(t, result, "vendor", "prebuilt_vendor") | 
|  | checkIfSnapshotNotTaken(t, result, "vendor", "prebuilt_vendor_indirect") | 
|  | checkIfSnapshotTaken(t, result, "recovery", "prebuilt_recovery") | 
|  | checkIfSnapshotNotTaken(t, result, "recovery", "prebuilt_recovery_indirect") | 
|  | }) | 
|  | } | 
|  |  | 
|  | func TestPrebuiltEtcAndroidMkEntriesWithBazel(t *testing.T) { | 
|  | t.Parallel() | 
|  | bp := ` | 
|  | prebuilt_etc { | 
|  | name: "myetc", | 
|  | src: "prebuilt_etc.rc", // filename in src tree | 
|  | filename: "init.rc", // target filename on device | 
|  | sub_dir: "subdir", // relative subdir for installation | 
|  | bazel_module: { label: "//foo/bar:myetc" }, | 
|  | } | 
|  | ` | 
|  | res := android.GroupFixturePreparers( | 
|  | prepareForPrebuiltEtcTest, | 
|  | android.FixtureModifyConfig(func(cfg android.Config) { | 
|  | cfg.BazelContext = android.MockBazelContext{ | 
|  | LabelToPrebuiltFileInfo: map[string]cquery.PrebuiltFileInfo{ | 
|  | "//foo/bar:myetc": cquery.PrebuiltFileInfo{ | 
|  | Src:         "foo/bar/prebuilt_etc.rc", | 
|  | Dir:         "etc/subdir", | 
|  | Filename:    "init.rc", | 
|  | Installable: true, | 
|  | }, | 
|  | }, | 
|  | } | 
|  | }), | 
|  | ).RunTestWithBp(t, bp) | 
|  | ctx := res.ModuleForTests("myetc", "android_arm64_armv8-a") | 
|  | mod := ctx.Module() | 
|  | entries := android.AndroidMkEntriesForTest(t, res.TestContext, mod)[0] | 
|  | // verify androidmk entries | 
|  | android.AssertStringDoesContain(t, "LOCAL_MODULE_PATH should contain", entries.EntryMap["LOCAL_MODULE_PATH"][0], "etc/subdir") | 
|  | android.AssertStringEquals(t, "LOCAL_INSTALLED_MODULE_STEM is incorrect", "init.rc", entries.EntryMap["LOCAL_INSTALLED_MODULE_STEM"][0]) | 
|  | // verify installation rules | 
|  | install := ctx.Description("install") | 
|  | android.AssertStringEquals(t, "Source location of prebuilt_etc installation", "out/soong/.intermediates/myetc/android_arm64_armv8-a/init.rc", install.Input.String()) | 
|  | android.AssertStringEquals(t, "Target location of prebuilt_etc installation", "out/soong/target/product/test_device/system/etc/subdir/init.rc", install.Output.String()) | 
|  | } |