Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/build/build/soong/java/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 163 kB image not shown  

Quelle  app_test.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

// Copyright 2017 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 java

import (
 "encoding/json"
 "errors"
 "fmt"
 "path/filepath"
 "reflect"
 "regexp"
 "sort"
 "strings"
 "testing"

 "github.com/google/blueprint/proptools"

 "android/soong/android"
 "android/soong/cc"
 "android/soong/dexpreopt"
)

// testApp runs tests using the prepareForJavaTest
//
// See testJava for an explanation as to how to stop using this deprecated method.
//
// deprecated
func testApp(t *testing.T, bp string) *android.TestContext {
 t.Helper()
 result := prepareForJavaTest.RunTestWithBp(t, bp)
 return result.TestContext
}

func TestApp(t *testing.T) {
 t.Parallel()
 resourceFiles := []string{
  "res/layout/layout.xml",
  "res/values/strings.xml",
  "res/values-en-rUS/strings.xml",
 }

 compiledResourceFiles := []string{
  "aapt2/res/layout_layout.xml.flat",
  "aapt2/res/values_strings.arsc.flat",
  "aapt2/res/values-en-rUS_strings.arsc.flat",
 }

 for _, moduleType := range []string{"android_app", "android_library"} {
  t.Run(moduleType, func(t *testing.T) {
   t.Parallel()
   result := android.GroupFixturePreparers(
    prepareForJavaTest,
    android.FixtureModifyMockFS(func(fs android.MockFS) {
     for _, file := range resourceFiles {
      fs[file] = nil
     }
    }),
   ).RunTestWithBp(t, moduleType+` {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current"
    }
   `)

   foo := result.ModuleForTests(t, "foo", "android_common")

   var expectedLinkImplicits []string

   manifestFixer := foo.Output("manifest_fixer/AndroidManifest.xml")
   expectedLinkImplicits = append(expectedLinkImplicits, manifestFixer.Output.String())

   frameworkRes := result.ModuleForTests(t, "framework-res", "android_common")
   expectedLinkImplicits = append(expectedLinkImplicits,
    frameworkRes.Output("package-res.apk").Output.String())

   // Test the mapping from input files to compiled output file names
   compile := foo.Output(compiledResourceFiles[0])
   android.AssertDeepEquals(t, "aapt2 compile inputs", resourceFiles, compile.Inputs.Strings())

   compiledResourceOutputs := compile.Outputs.Strings()
   sort.Strings(compiledResourceOutputs)

   expectedLinkImplicits = append(expectedLinkImplicits, compiledResourceOutputs...)

   list := foo.Output("aapt2/res.list")
   expectedLinkImplicits = append(expectedLinkImplicits, list.Output.String())

   // Check that the link rule uses
   res := result.ModuleForTests(t, "foo", "android_common").Output("package-res.apk")
   android.AssertDeepEquals(t, "aapt2 link implicits", expectedLinkImplicits, res.Implicits.Strings())
  })
 }
}

func TestAppSplits(t *testing.T) {
 t.Parallel()
 ctx := testApp(t, `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     package_splits: ["v4", "v7,hdpi"],
     sdk_version: "current"
    }`)

 foo := ctx.ModuleForTests(t, "foo", "android_common")

 expectedOutputs := []string{
  "out/soong/.intermediates/foo/android_common/foo.apk",
  "out/soong/.intermediates/foo/android_common/foo_v4.apk",
  "out/soong/.intermediates/foo/android_common/foo_v7_hdpi.apk",
 }
 for _, expectedOutput := range expectedOutputs {
  foo.Output(expectedOutput)
 }

 outputFiles := foo.OutputFiles(ctx, t, "")
 android.AssertPathsRelativeToTopEquals(t, `OutputFiles("")`, expectedOutputs, outputFiles)
}

func TestPlatformAPIs(t *testing.T) {
 t.Parallel()
 testJava(t, `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   platform_apis: true,
  }
 `)

 testJava(t, `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   sdk_version: "current",
  }
 `)

 testJavaError(t, "This module has conflicting settings. sdk_version is empty, which means that this module is build against platform APIs. However platform_apis is not set to true", `
  android_app {
   name: "bar",
   srcs: ["b.java"],
  }
 `)

 testJavaError(t, "This module has conflicting settings. sdk_version is not empty, which means this module cannot use platform APIs. However platform_apis is set to true.", `
  android_app {
   name: "bar",
   srcs: ["b.java"],
   sdk_version: "system_current",
   platform_apis: true,
  }
 `)
}

func TestAndroidAppLinkType(t *testing.T) {
 t.Parallel()
 testJava(t, `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   libs: ["bar"],
   static_libs: ["baz"],
   platform_apis: true,
  }

  java_library {
   name: "bar",
   sdk_version: "current",
   srcs: ["b.java"],
  }

  android_library {
   name: "baz",
   sdk_version: "system_current",
   srcs: ["c.java"],
  }
 `)

 testJavaError(t, "consider adjusting sdk_version: OR platform_apis:", `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   libs: ["bar"],
   sdk_version: "current",
   static_libs: ["baz"],
  }

  java_library {
   name: "bar",
   sdk_version: "current",
   srcs: ["b.java"],
  }

  android_library {
   name: "baz",
   sdk_version: "system_current",
   srcs: ["c.java"],
  }
 `)

 testJava(t, `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   libs: ["bar"],
   sdk_version: "system_current",
   static_libs: ["baz"],
  }

  java_library {
   name: "bar",
   sdk_version: "current",
   srcs: ["b.java"],
  }

  android_library {
   name: "baz",
   sdk_version: "system_current",
   srcs: ["c.java"],
  }
 `)

 testJavaError(t, "consider adjusting sdk_version: OR platform_apis:", `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   libs: ["bar"],
   sdk_version: "system_current",
   static_libs: ["baz"],
  }

  java_library {
   name: "bar",
   sdk_version: "current",
   srcs: ["b.java"],
  }

  android_library {
   name: "baz",
   srcs: ["c.java"],
  }
 `)
}

func TestUpdatableApps(t *testing.T) {
 t.Parallel()
 testCases := []struct {
  name          string
  bp            string
  expectedError string
 }{
  {
   name: "Stable public SDK",
   bp: `android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "29",
     min_sdk_version: "29",
     updatable: true,
    }`,
  },
  {
   name: "Stable system SDK",
   bp: `android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "system_29",
     min_sdk_version: "29",
     updatable: true,
    }`,
  },
  {
   name: "Current public SDK",
   bp: `android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current",
     min_sdk_version: "29",
     updatable: true,
    }`,
  },
  {
   name: "Current system SDK",
   bp: `android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "system_current",
     min_sdk_version: "29",
     updatable: true,
    }`,
  },
  {
   name: "Current module SDK",
   bp: `android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "module_current",
     min_sdk_version: "29",
     updatable: true,
    }`,
  },
  {
   name: "Current core SDK",
   bp: `android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "core_current",
     min_sdk_version: "29",
     updatable: true,
    }`,
  },
  {
   name: "No Platform APIs",
   bp: `android_app {
     name: "foo",
     srcs: ["a.java"],
     platform_apis: true,
     min_sdk_version: "29",
     updatable: true,
    }`,
   expectedError: "Updatable apps must use stable SDKs",
  },
  {
   name: "No Core Platform APIs",
   bp: `android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "core_platform",
     min_sdk_version: "29",
     updatable: true,
    }`,
   expectedError: "Updatable apps must use stable SDKs",
  },
  {
   name: "No unspecified APIs",
   bp: `android_app {
     name: "foo",
     srcs: ["a.java"],
     updatable: true,
     min_sdk_version: "29",
    }`,
   expectedError: "Updatable apps must use stable SDK",
  },
  {
   name: "Must specify min_sdk_version",
   bp: `android_app {
     name: "app_without_min_sdk_version",
     srcs: ["a.java"],
     sdk_version: "29",
     updatable: true,
    }`,
   expectedError: "updatable apps must set min_sdk_version.",
  },
 }

 for _, test := range testCases {
  t.Run(test.name, func(t *testing.T) {
   t.Parallel()
   errorHandler := android.FixtureExpectsNoErrors
   if test.expectedError != "" {
    errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(test.expectedError)
   }
   android.GroupFixturePreparers(
    prepareForJavaTest, FixtureWithPrebuiltApis(map[string][]string{
     "29": {"foo"},
    })).
    ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, test.bp)
  })
 }
}

func TestUpdatableApps_TransitiveDepsShouldSetMinSdkVersion(t *testing.T) {
 t.Parallel()
 testJavaError(t, `module "bar".*: should support min_sdk_version\(29\)`, cc.GatherRequiredDepsForTest(android.Android)+`
  android_app {
   name: "foo",
   srcs: ["a.java"],
   updatable: true,
   sdk_version: "current",
   min_sdk_version: "29",
   static_libs: ["bar"],
  }

  java_library {
   name: "bar",
   sdk_version: "current",
  }
 `)
}

func TestUpdatableApps_JniLibsShouldShouldSupportMinSdkVersion(t *testing.T) {
 t.Parallel()
 testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
  android_app {
   name: "foo",
   srcs: ["a.java"],
   updatable: true,
   sdk_version: "current",
   min_sdk_version: "current",
   jni_libs: ["libjni"],
  }

  cc_library {
   name: "libjni",
   stl: "none",
   system_shared_libs: [],
   sdk_version: "current",
  }
 `)
}

func TestUpdatableApps_JniLibShouldBeBuiltAgainstMinSdkVersion(t *testing.T) {
 t.Parallel()
 bp := cc.GatherRequiredDepsForTest(android.Android) + `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   updatable: true,
   sdk_version: "current",
   min_sdk_version: "29",
   jni_libs: ["libjni"],
  }

  cc_library {
   name: "libjni",
   stl: "none",
   system_shared_libs: [],
   sdk_version: "current",
   min_sdk_version: "29",
  }
 `
 fs := map[string][]byte{
  "prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtbegin_so.o": nil,
  "prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtend_so.o":   nil,
  "prebuilts/ndk/current/platforms/android-29/arch-arm/usr/lib/crtbegin_so.o":   nil,
  "prebuilts/ndk/current/platforms/android-29/arch-arm/usr/lib/crtend_so.o":     nil,
 }

 ctx, _ := testJavaWithFS(t, bp, fs)

 inputs := ctx.ModuleForTests(t, "libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
 var crtbeginFound, crtendFound bool
 expectedCrtBegin := ctx.ModuleForTests(t, "crtbegin_so",
  "android_arm64_armv8-a_sdk_29").Rule("noAddrSig").Output
 expectedCrtEnd := ctx.ModuleForTests(t, "crtend_so",
  "android_arm64_armv8-a_sdk_29").Rule("noAddrSig").Output
 implicits := []string{}
 for _, input := range inputs {
  implicits = append(implicits, input.String())
  if strings.HasSuffix(input.String(), expectedCrtBegin.String()) {
   crtbeginFound = true
  } else if strings.HasSuffix(input.String(), expectedCrtEnd.String()) {
   crtendFound = true
  }
 }
 if !crtbeginFound {
  t.Error(fmt.Sprintf(
   "expected implicit with suffix %q, have the following implicits:\n%s",
   expectedCrtBegin, strings.Join(implicits, "\n")))
 }
 if !crtendFound {
  t.Error(fmt.Sprintf(
   "expected implicit with suffix %q, have the following implicits:\n%s",
   expectedCrtEnd, strings.Join(implicits, "\n")))
 }
}

func TestUpdatableApps_ErrorIfJniLibDoesntSupportMinSdkVersion(t *testing.T) {
 t.Parallel()
 bp := cc.GatherRequiredDepsForTest(android.Android) + `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   updatable: true,
   sdk_version: "current",
   min_sdk_version: "29",  // this APK should support 29
   jni_libs: ["libjni"],
  }

  cc_library {
   name: "libjni",
   stl: "none",
   sdk_version: "current",
   min_sdk_version: "current",
  }
 `
 testJavaError(t, `"libjni" .*: min_sdk_version\(current\) is higher than min_sdk_version\(29\)`, bp)
}

func TestUpdatableApps_ErrorIfDepMinSdkVersionIsHigher(t *testing.T) {
 t.Parallel()
 bp := cc.GatherRequiredDepsForTest(android.Android) + `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   updatable: true,
   sdk_version: "current",
   min_sdk_version: "29",  // this APK should support 29
   jni_libs: ["libjni"],
  }

  cc_library {
   name: "libjni",
   stl: "none",
   shared_libs: ["libbar"],
   system_shared_libs: [],
   sdk_version: "27",
   min_sdk_version: "27",
  }

  cc_library {
   name: "libbar",
   stl: "none",
   system_shared_libs: [],
   sdk_version: "current",
   min_sdk_version: "current",
  }
 `
 testJavaError(t, `"libjni" .*: links "libbar" built against newer API version "current"`, bp)
}

func TestUpdatableApps_ApplyDefaultUpdatableModuleVersion(t *testing.T) {
 t.Parallel()
 result := android.GroupFixturePreparers(
  PrepareForTestWithJavaDefaultModules,
 ).RunTestWithBp(t, `
  android_app {
   name: "com.android.foo",
   srcs: ["a.java"],
   sdk_version: "current",
   min_sdk_version: "31",
   updatable: true,
  }
 `)
 foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
 android.AssertStringDoesContain(t,
  "com.android.foo: expected manifest fixer to set override-placeholder-version to RELEASE_DEFAULT_UPDATABLE_MODULE_VERSION",
  foo.BuildParams.Args["args"],
  fmt.Sprintf("--override-placeholder-version %s", testDefaultUpdatableModuleVersion),
 )
}

func TestUpdatableApps_ApplyOverrideApexManifestDefaultVersion(t *testing.T) {
 t.Parallel()
 result := android.GroupFixturePreparers(
  PrepareForTestWithJavaDefaultModules,
  android.FixtureMergeEnv(map[string]string{
   "OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION": "1234",
  }),
 ).RunTestWithBp(t, `
  android_app {
   name: "com.android.foo",
   srcs: ["a.java"],
   sdk_version: "current",
   min_sdk_version: "31",
   updatable: true,
  }
 `)
 foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
 android.AssertStringDoesContain(t,
  "com.android.foo: expected manifest fixer to set override-placeholder-version to 1234",
  foo.BuildParams.Args["args"],
  "--override-placeholder-version 1234",
 )
}

func TestResourceDirs(t *testing.T) {
 t.Parallel()
 testCases := []struct {
  name      string
  prop      string
  resources []string
 }{
  {
   name:      "no resource_dirs",
   prop:      "",
   resources: []string{"res/res/values/strings.xml"},
  },
  {
   name:      "resource_dirs",
   prop:      `resource_dirs: ["res"]`,
   resources: []string{"res/res/values/strings.xml"},
  },
  {
   name:      "empty resource_dirs",
   prop:      `resource_dirs: []`,
   resources: nil,
  },
 }

 fs := android.MockFS{
  "res/res/values/strings.xml": nil,
 }

 bp := `
   android_app {
    name: "foo",
    sdk_version: "current",
    %s
   }
  `

 for _, testCase := range testCases {
  t.Run(testCase.name, func(t *testing.T) {
   t.Parallel()
   result := android.GroupFixturePreparers(
    PrepareForTestWithJavaDefaultModules,
    fs.AddToFixture(),
   ).RunTestWithBp(t, fmt.Sprintf(bp, testCase.prop))

   module := result.ModuleForTests(t, "foo", "android_common")
   resourceList := module.MaybeOutput("aapt2/res.list")

   var resources []string
   if resourceList.Rule != nil {
    for _, compiledResource := range resourceList.Inputs.Strings() {
     resources = append(resources, module.Output(compiledResource).Inputs.Strings()...)
    }
   }

   android.AssertDeepEquals(t, "resource files", testCase.resources, resources)
  })
 }
}

func TestLibraryAssets(t *testing.T) {
 t.Parallel()
 bp := `
   android_app {
    name: "foo",
    sdk_version: "current",
    static_libs: ["lib1", "lib2", "lib3"],
   }

   android_library {
    name: "lib1",
    sdk_version: "current",
    asset_dirs: ["assets_a"],
   }

   android_library {
    name: "lib2",
    sdk_version: "current",
   }

   android_library {
    name: "lib3",
    sdk_version: "current",
    static_libs: ["lib4", "import"],
   }

   android_library {
    name: "lib4",
    sdk_version: "current",
    asset_dirs: ["assets_b"],
   }

   android_library {
    name: "lib5",
    sdk_version: "current",
    assets: [
     "path/to/asset_file_1",
     "path/to/asset_file_2",
    ],
   }

   android_library_import {
    name: "import",
    sdk_version: "current",
    aars: ["import.aar"],
   }
  `

 testCases := []struct {
  name               string
  assetFlag          string
  assetPackages      []string
  tmpAssetDirInputs  []string
  tmpAssetDirOutputs []string
 }{
  {
   name: "foo",
   // lib1 has its own assets. lib3 doesn't have any, but lib4 and import have assets.
   assetPackages: []string{
    "out/soong/.intermediates/foo/android_common/aapt2/package-res.apk",
    "out/soong/.intermediates/lib1/android_common/assets.zip",
    "out/soong/.intermediates/lib4/android_common/assets.zip",
    "out/soong/.intermediates/import/android_common/assets.zip",
   },
  },
  {
   name:      "lib1",
   assetFlag: "-A assets_a",
  },
  {
   name: "lib2",
  },
  {
   name: "lib3",
  },
  {
   name:      "lib4",
   assetFlag: "-A assets_b",
  },
  {
   name:      "lib5",
   assetFlag: "-A out/soong/.intermediates/lib5/android_common/tmp_asset_dir",
   tmpAssetDirInputs: []string{
    "path/to/asset_file_1",
    "path/to/asset_file_2",
   },
   tmpAssetDirOutputs: []string{
    "out/soong/.intermediates/lib5/android_common/tmp_asset_dir/path/to/asset_file_1",
    "out/soong/.intermediates/lib5/android_common/tmp_asset_dir/path/to/asset_file_2",
   },
  },
 }
 ctx := testApp(t, bp)

 for _, test := range testCases {
  t.Run(test.name, func(t *testing.T) {
   t.Parallel()
   m := ctx.ModuleForTests(t, test.name, "android_common")

   // Check asset flag in aapt2 link flags
   var aapt2link android.TestingBuildParams
   if len(test.assetPackages) > 0 {
    aapt2link = m.Output("aapt2/package-res.apk")
   } else {
    aapt2link = m.Output("package-res.apk")
   }
   aapt2Flags := aapt2link.Args["flags"]
   if test.assetFlag != "" {
    android.AssertStringDoesContain(t, "asset flag", aapt2Flags, test.assetFlag)
   } else {
    android.AssertStringDoesNotContain(t, "aapt2 link flags", aapt2Flags, " -A ")
   }

   // Check asset merge rule.
   if len(test.assetPackages) > 0 {
    mergeAssets := m.Output("package-res.apk")
    android.AssertPathsRelativeToTopEquals(t, "mergeAssets inputs", test.assetPackages, mergeAssets.Inputs)
   }

   if len(test.tmpAssetDirInputs) > 0 {
    rule := m.Rule("tmp_asset_dir")
    inputs := rule.Implicits
    outputs := append(android.WritablePaths{rule.Output}, rule.ImplicitOutputs...).Paths()
    android.AssertPathsRelativeToTopEquals(t, "tmp_asset_dir inputs", test.tmpAssetDirInputs, inputs)
    android.AssertPathsRelativeToTopEquals(t, "tmp_asset_dir outputs", test.tmpAssetDirOutputs, outputs)
   }
  })
 }
}

func TestAppJavaResources(t *testing.T) {
 t.Parallel()
 bp := `
   android_app {
    name: "foo",
    sdk_version: "current",
    java_resources: ["resources/a"],
    srcs: ["a.java"],
   }

   android_app {
    name: "bar",
    sdk_version: "current",
    java_resources: ["resources/a"],
   }
  `

 ctx := testApp(t, bp)

 foo := ctx.ModuleForTests(t, "foo", "android_common")
 fooResources := foo.Output("res/foo.jar")
 fooDexJar := foo.Output("dex-withres/foo.jar")
 fooDexJarAligned := foo.Output("dex-withres-aligned/foo.jar")
 fooApk := foo.Rule("combineApk")

 if g, w := fooDexJar.Inputs.Strings(), fooResources.Output.String(); !android.InList(w, g) {
  t.Errorf("expected resource jar %q in foo dex jar inputs %q", w, g)
 }

 if g, w := fooDexJarAligned.Input.String(), fooDexJar.Output.String(); g != w {
  t.Errorf("expected dex jar %q in foo aligned dex jar inputs %q", w, g)
 }

 if g, w := fooApk.Inputs.Strings(), fooDexJarAligned.Output.String(); !android.InList(w, g) {
  t.Errorf("expected aligned dex jar %q in foo apk inputs %q", w, g)
 }

 bar := ctx.ModuleForTests(t, "bar", "android_common")
 barResources := bar.Output("res/bar.jar")
 barApk := bar.Rule("combineApk")

 if g, w := barApk.Inputs.Strings(), barResources.Output.String(); !android.InList(w, g) {
  t.Errorf("expected resources jar %q in bar apk inputs %q", w, g)
 }
}

func TestAndroidResourceProcessor(t *testing.T) {
 t.Parallel()
 testCases := []struct {
  name                            string
  appUsesRP                       bool
  directLibUsesRP                 bool
  transitiveLibUsesRP             bool
  sharedLibUsesRP                 bool
  sharedTransitiveStaticLibUsesRP bool
  sharedTransitiveSharedLibUsesRP bool

  dontVerifyApp bool
  appResources  []string
  appOverlays   []string
  appImports    []string
  appSrcJars    []string
  appClasspath  []string
  appCombined   []string

  dontVerifyDirect bool
  directResources  []string
  directOverlays   []string
  directImports    []string
  directSrcJars    []string
  directClasspath  []string
  directCombined   []string

  dontVerifyTransitive bool
  transitiveResources  []string
  transitiveOverlays   []string
  transitiveImports    []string
  transitiveSrcJars    []string
  transitiveClasspath  []string
  transitiveCombined   []string

  dontVerifyDirectImport bool
  directImportResources  []string
  directImportOverlays   []string
  directImportImports    []string

  dontVerifyTransitiveImport bool
  transitiveImportResources  []string
  transitiveImportOverlays   []string
  transitiveImportImports    []string

  dontVerifyShared bool
  sharedResources  []string
  sharedOverlays   []string
  sharedImports    []string
  sharedSrcJars    []string
  sharedClasspath  []string
  sharedCombined   []string
 }{
  {
   // Test with all modules set to use_resource_processor: false (except android_library_import modules,
   // which always use resource processor).
   name:                "legacy",
   appUsesRP:           false,
   directLibUsesRP:     false,
   transitiveLibUsesRP: false,

   appResources: nil,
   appOverlays: []string{
    "out/soong/.intermediates/transitive/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import/android_common/package-res.apk",
    "out/soong/.intermediates/direct/android_common/package-res.apk",
    "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/direct_import/android_common/package-res.apk",
    "out/soong/.intermediates/app/android_common/aapt2/app/res/values_strings.arsc.flat",
   },
   appImports: []string{
    "out/soong/.intermediates/shared/android_common/package-res.apk",
    "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
   },
   appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"},
   appClasspath: []string{
    "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
    "out/soong/.intermediates/shared/android_common/turbine/shared.jar",
    "out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
    "out/soong/.intermediates/direct/android_common/turbine/direct.jar",
    "out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
    "out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
    "out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
   },
   appCombined: []string{
    "out/soong/.intermediates/app/android_common/javac/app.jar",
    "out/soong/.intermediates/direct/android_common/javac/direct.jar",
    "out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
    "out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
    "out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
   },

   directResources: nil,
   directOverlays: []string{
    "out/soong/.intermediates/transitive/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import/android_common/package-res.apk",
    "out/soong/.intermediates/direct/android_common/aapt2/direct/res/values_strings.arsc.flat",
   },
   directImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
   directSrcJars: []string{"out/soong/.intermediates/direct/android_common/gen/android/R.srcjar"},
   directClasspath: []string{
    "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
    "out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
   },
   directCombined: []string{
    "out/soong/.intermediates/direct/android_common/javac/direct.jar",
    "out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
   },

   transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
   transitiveOverlays:  nil,
   transitiveImports:   []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
   transitiveSrcJars:   []string{"out/soong/.intermediates/transitive/android_common/gen/android/R.srcjar"},
   transitiveClasspath: []string{"out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar"},
   transitiveCombined:  nil,

   sharedResources: nil,
   sharedOverlays: []string{
    "out/soong/.intermediates/shared_transitive_static/android_common/package-res.apk",
    "out/soong/.intermediates/shared/android_common/aapt2/shared/res/values_strings.arsc.flat",
   },
   sharedImports: []string{
    "out/soong/.intermediates/shared_transitive_shared/android_common/package-res.apk",
    "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
   },
   sharedSrcJars: []string{"out/soong/.intermediates/shared/android_common/gen/android/R.srcjar"},
   sharedClasspath: []string{
    "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
    "out/soong/.intermediates/shared_transitive_shared/android_common/turbine/shared_transitive_shared.jar",
    "out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
   },
   sharedCombined: []string{
    "out/soong/.intermediates/shared/android_common/javac/shared.jar",
    "out/soong/.intermediates/shared_transitive_static/android_common/javac/shared_transitive_static.jar",
   },

   directImportResources: nil,
   directImportOverlays:  []string{"out/soong/.intermediates/direct_import/android_common/flat-res/gen_res.flata"},
   directImportImports: []string{
    "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
    "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk",
   },

   transitiveImportResources: nil,
   transitiveImportOverlays:  []string{"out/soong/.intermediates/transitive_import/android_common/flat-res/gen_res.flata"},
   transitiveImportImports: []string{
    "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
   },
  },
  {
   // Test with all modules set to use_resource_processor: true.
   name:                            "resource_processor",
   appUsesRP:                       true,
   directLibUsesRP:                 true,
   transitiveLibUsesRP:             true,
   sharedLibUsesRP:                 true,
   sharedTransitiveSharedLibUsesRP: true,
   sharedTransitiveStaticLibUsesRP: true,

   appResources: nil,
   appOverlays: []string{
    "out/soong/.intermediates/transitive/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import/android_common/package-res.apk",
    "out/soong/.intermediates/direct/android_common/package-res.apk",
    "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/direct_import/android_common/package-res.apk",
    "out/soong/.intermediates/app/android_common/aapt2/app/res/values_strings.arsc.flat",
   },
   appImports: []string{
    "out/soong/.intermediates/shared/android_common/package-res.apk",
    "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
   },
   appSrcJars: nil,
   appClasspath: []string{
    "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
    "out/soong/.intermediates/app/android_common/busybox/R.jar",
    "out/soong/.intermediates/shared/android_common/turbine/shared.jar",
    "out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
    "out/soong/.intermediates/direct/android_common/turbine/direct.jar",
    "out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
    "out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
    "out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
   },
   appCombined: []string{
    "out/soong/.intermediates/app/android_common/javac/app.jar",
    "out/soong/.intermediates/app/android_common/busybox/R.jar",
    "out/soong/.intermediates/direct/android_common/javac/direct.jar",
    "out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
    "out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
    "out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
   },

   directResources: nil,
   directOverlays:  []string{"out/soong/.intermediates/direct/android_common/aapt2/direct/res/values_strings.arsc.flat"},
   directImports: []string{
    "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/transitive/android_common/package-res.apk",
   },
   directSrcJars: nil,
   directClasspath: []string{
    "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
    "out/soong/.intermediates/direct/android_common/busybox/R.jar",
    "out/soong/.intermediates/transitive/android_common/busybox/R.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar",
    "out/soong/.intermediates/transitive_import/android_common/busybox/R.jar",
    "out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
   },
   directCombined: []string{
    "out/soong/.intermediates/direct/android_common/javac/direct.jar",
    "out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
   },

   transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
   transitiveOverlays:  nil,
   transitiveImports:   []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
   transitiveSrcJars:   nil,
   transitiveClasspath: []string{
    "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
    "out/soong/.intermediates/transitive/android_common/busybox/R.jar",
   },
   transitiveCombined: nil,

   sharedResources: nil,
   sharedOverlays:  []string{"out/soong/.intermediates/shared/android_common/aapt2/shared/res/values_strings.arsc.flat"},
   sharedImports: []string{
    "out/soong/.intermediates/shared_transitive_shared/android_common/package-res.apk",
    "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
    "out/soong/.intermediates/shared_transitive_static/android_common/package-res.apk",
   },
   sharedSrcJars: nil,
   sharedClasspath: []string{
    "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
    "out/soong/.intermediates/shared/android_common/busybox/R.jar",
    "out/soong/.intermediates/shared_transitive_static/android_common/busybox/R.jar",
    "out/soong/.intermediates/shared_transitive_shared/android_common/busybox/R.jar",
    "out/soong/.intermediates/shared_transitive_shared/android_common/turbine/shared_transitive_shared.jar",
    "out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
   },
   sharedCombined: []string{
    "out/soong/.intermediates/shared/android_common/javac/shared.jar",
    "out/soong/.intermediates/shared_transitive_static/android_common/javac/shared_transitive_static.jar",
   },

   directImportResources: nil,
   directImportOverlays:  []string{"out/soong/.intermediates/direct_import/android_common/flat-res/gen_res.flata"},
   directImportImports: []string{
    "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
    "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk",
   },

   transitiveImportResources: nil,
   transitiveImportOverlays:  []string{"out/soong/.intermediates/transitive_import/android_common/flat-res/gen_res.flata"},
   transitiveImportImports: []string{
    "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
   },
  }, {
   // Test an app building with resource processor enabled but with dependencies built without
   // resource processor.
   name:                "app_resource_processor",
   appUsesRP:           true,
   directLibUsesRP:     false,
   transitiveLibUsesRP: false,

   appResources: nil,
   appOverlays: []string{
    "out/soong/.intermediates/transitive/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import/android_common/package-res.apk",
    "out/soong/.intermediates/direct/android_common/package-res.apk",
    "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/direct_import/android_common/package-res.apk",
    "out/soong/.intermediates/app/android_common/aapt2/app/res/values_strings.arsc.flat",
   },
   appImports: []string{
    "out/soong/.intermediates/shared/android_common/package-res.apk",
    "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
   },
   appSrcJars: nil,
   appClasspath: []string{
    "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
    // R.jar has to come before direct.jar
    "out/soong/.intermediates/app/android_common/busybox/R.jar",
    "out/soong/.intermediates/shared/android_common/turbine/shared.jar",
    "out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
    "out/soong/.intermediates/direct/android_common/turbine/direct.jar",
    "out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
    "out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
    "out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
   },
   appCombined: []string{
    "out/soong/.intermediates/app/android_common/javac/app.jar",
    "out/soong/.intermediates/app/android_common/busybox/R.jar",
    "out/soong/.intermediates/direct/android_common/javac/direct.jar",
    "out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
    "out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
    "out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
   },

   dontVerifyDirect:           true,
   dontVerifyTransitive:       true,
   dontVerifyShared:           true,
   dontVerifyDirectImport:     true,
   dontVerifyTransitiveImport: true,
  },
  {
   // Test an app building without resource processor enabled but with a dependency built with
   // resource processor.
   name:                "app_dependency_lib_resource_processor",
   appUsesRP:           false,
   directLibUsesRP:     true,
   transitiveLibUsesRP: false,

   appOverlays: []string{
    "out/soong/.intermediates/transitive/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import/android_common/package-res.apk",
    "out/soong/.intermediates/direct/android_common/package-res.apk",
    "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/direct_import/android_common/package-res.apk",
    "out/soong/.intermediates/app/android_common/aapt2/app/res/values_strings.arsc.flat",
   },
   appImports: []string{
    "out/soong/.intermediates/shared/android_common/package-res.apk",
    "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
   },
   appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"},
   appClasspath: []string{
    "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
    "out/soong/.intermediates/shared/android_common/turbine/shared.jar",
    "out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
    "out/soong/.intermediates/direct/android_common/turbine/direct.jar",
    "out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
    "out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
    "out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
   },
   appCombined: []string{
    "out/soong/.intermediates/app/android_common/javac/app.jar",
    "out/soong/.intermediates/direct/android_common/javac/direct.jar",
    "out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
    "out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
    "out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
   },

   directResources: nil,
   directOverlays:  []string{"out/soong/.intermediates/direct/android_common/aapt2/direct/res/values_strings.arsc.flat"},
   directImports: []string{
    "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/transitive/android_common/package-res.apk",
   },
   directSrcJars: nil,
   directClasspath: []string{
    "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
    "out/soong/.intermediates/direct/android_common/busybox/R.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/busybox/R.jar",
    "out/soong/.intermediates/transitive_import/android_common/busybox/R.jar",
    "out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
   },
   directCombined: []string{
    "out/soong/.intermediates/direct/android_common/javac/direct.jar",
    "out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
   },

   dontVerifyTransitive:       true,
   dontVerifyShared:           true,
   dontVerifyDirectImport:     true,
   dontVerifyTransitiveImport: true,
  },
  {
   // Test a library building without resource processor enabled but with a dependency built with
   // resource processor.
   name:                "lib_dependency_lib_resource_processor",
   appUsesRP:           false,
   directLibUsesRP:     false,
   transitiveLibUsesRP: true,

   appOverlays: []string{
    "out/soong/.intermediates/transitive/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import/android_common/package-res.apk",
    "out/soong/.intermediates/direct/android_common/package-res.apk",
    "out/soong/.intermediates/direct_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/direct_import/android_common/package-res.apk",
    "out/soong/.intermediates/app/android_common/aapt2/app/res/values_strings.arsc.flat",
   },
   appImports: []string{
    "out/soong/.intermediates/shared/android_common/package-res.apk",
    "out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk",
   },
   appSrcJars: []string{"out/soong/.intermediates/app/android_common/gen/android/R.srcjar"},
   appClasspath: []string{
    "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
    "out/soong/.intermediates/shared/android_common/turbine/shared.jar",
    "out/soong/.intermediates/shared_transitive_static/android_common/turbine/shared_transitive_static.jar",
    "out/soong/.intermediates/direct/android_common/turbine/direct.jar",
    "out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
    "out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
    "out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
   },
   appCombined: []string{
    "out/soong/.intermediates/app/android_common/javac/app.jar",
    "out/soong/.intermediates/direct/android_common/javac/direct.jar",
    "out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
    "out/soong/.intermediates/direct_import/android_common/aar/direct_import.jar",
    "out/soong/.intermediates/direct_import_dep/android_common/aar/direct_import_dep.jar",
   },

   directResources: nil,
   directOverlays: []string{
    "out/soong/.intermediates/transitive/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import_dep/android_common/package-res.apk",
    "out/soong/.intermediates/transitive_import/android_common/package-res.apk",
    "out/soong/.intermediates/direct/android_common/aapt2/direct/res/values_strings.arsc.flat",
   },
   directImports: []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
   directSrcJars: []string{"out/soong/.intermediates/direct/android_common/gen/android/R.srcjar"},
   directClasspath: []string{
    "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
    "out/soong/.intermediates/transitive/android_common/turbine/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
   },
   directCombined: []string{
    "out/soong/.intermediates/direct/android_common/javac/direct.jar",
    "out/soong/.intermediates/transitive/android_common/javac/transitive.jar",
    "out/soong/.intermediates/transitive_import/android_common/aar/transitive_import.jar",
    "out/soong/.intermediates/transitive_import_dep/android_common/aar/transitive_import_dep.jar",
   },

   transitiveResources: []string{"out/soong/.intermediates/transitive/android_common/aapt2/transitive/res/values_strings.arsc.flat"},
   transitiveOverlays:  nil,
   transitiveImports:   []string{"out/soong/.intermediates/default/java/framework-res/android_common/package-res.apk"},
   transitiveSrcJars:   nil,
   transitiveClasspath: []string{
    "out/soong/.intermediates/default/java/android_stubs_current/android_common/turbine/android_stubs_current.jar",
    "out/soong/.intermediates/transitive/android_common/busybox/R.jar",
   },
   transitiveCombined: nil,

   dontVerifyShared:           true,
   dontVerifyDirectImport:     true,
   dontVerifyTransitiveImport: true,
  },
 }

 for _, testCase := range testCases {
  t.Run(testCase.name, func(t *testing.T) {
   t.Parallel()
   bp := fmt.Sprintf(`
    android_app {
     name: "app",
     sdk_version: "current",
     srcs: ["app/app.java"],
     resource_dirs: ["app/res"],
     manifest: "app/AndroidManifest.xml",
     libs: ["shared"],
     static_libs: ["direct", "direct_import"],
     use_resource_processor: %v,
    }

    android_library {
     name: "direct",
     sdk_version: "current",
     srcs: ["direct/direct.java"],
     resource_dirs: ["direct/res"],
     manifest: "direct/AndroidManifest.xml",
     static_libs: ["transitive", "transitive_import"],
     use_resource_processor: %v,
    }

    android_library {
     name: "transitive",
     sdk_version: "current",
     srcs: ["transitive/transitive.java"],
     resource_dirs: ["transitive/res"],
     manifest: "transitive/AndroidManifest.xml",
     use_resource_processor: %v,
    }

    android_library {
     name: "shared",
     sdk_version: "current",
     srcs: ["shared/shared.java"],
     resource_dirs: ["shared/res"],
     manifest: "shared/AndroidManifest.xml",
     use_resource_processor: %v,
     libs: ["shared_transitive_shared"],
     static_libs: ["shared_transitive_static"],
    }

    android_library {
     name: "shared_transitive_shared",
     sdk_version: "current",
     srcs: ["shared_transitive_shared/shared_transitive_shared.java"],
     resource_dirs: ["shared_transitive_shared/res"],
     manifest: "shared_transitive_shared/AndroidManifest.xml",
     use_resource_processor: %v,
    }

    android_library {
     name: "shared_transitive_static",
     sdk_version: "current",
     srcs: ["shared_transitive_static/shared.java"],
     resource_dirs: ["shared_transitive_static/res"],
     manifest: "shared_transitive_static/AndroidManifest.xml",
     use_resource_processor: %v,
    }

    android_library_import {
     name: "direct_import",
     sdk_version: "current",
     aars: ["direct_import.aar"],
     static_libs: ["direct_import_dep"],
    }

    android_library_import {
     name: "direct_import_dep",
     sdk_version: "current",
     aars: ["direct_import_dep.aar"],
    }

    android_library_import {
     name: "transitive_import",
     sdk_version: "current",
     aars: ["transitive_import.aar"],
     static_libs: ["transitive_import_dep"],
    }

    android_library_import {
     name: "transitive_import_dep",
     sdk_version: "current",
     aars: ["transitive_import_dep.aar"],
    }
   `, testCase.appUsesRP, testCase.directLibUsesRP, testCase.transitiveLibUsesRP,
    testCase.sharedLibUsesRP, testCase.sharedTransitiveSharedLibUsesRP, testCase.sharedTransitiveStaticLibUsesRP)

   fs := android.MockFS{
    "app/res/values/strings.xml":                      nil,
    "direct/res/values/strings.xml":                   nil,
    "transitive/res/values/strings.xml":               nil,
    "shared/res/values/strings.xml":                   nil,
    "shared_transitive_static/res/values/strings.xml": nil,
    "shared_transitive_shared/res/values/strings.xml": nil,
   }

   result := android.GroupFixturePreparers(
    PrepareForTestWithJavaDefaultModules,
    fs.AddToFixture(),
   ).RunTestWithBp(t, bp)

   type aaptInfo struct {
    resources, overlays, imports, srcJars, classpath, combined android.Paths
   }

   getAaptInfo := func(moduleName string) (aaptInfo aaptInfo) {
    mod := result.ModuleForTests(t, moduleName, "android_common")
    resourceListRule := mod.MaybeOutput("aapt2/res.list")
    overlayListRule := mod.MaybeOutput("aapt2/overlay.list")
    aaptRule := mod.Rule("aapt2Link")
    javacRule := mod.MaybeRule("javac")
    combinedRule := mod.MaybeOutput("combined/" + moduleName + ".jar")

    aaptInfo.resources = resourceListRule.Inputs
    aaptInfo.overlays = overlayListRule.Inputs

    aaptFlags := strings.Split(aaptRule.Args["flags"], " ")
    for i, flag := range aaptFlags {
     if flag == "-I" && i+1 < len(aaptFlags) {
      aaptInfo.imports = append(aaptInfo.imports, android.PathForTesting(aaptFlags[i+1]))
     }
    }

    if len(javacRule.Args["srcJars"]) > 0 {
     aaptInfo.srcJars = android.PathsForTesting(strings.Split(javacRule.Args["srcJars"], " ")...)
    }

    if len(javacRule.Args["classpath"]) > 0 {
     classpathArg := strings.TrimPrefix(javacRule.Args["classpath"], "-classpath ")
     aaptInfo.classpath = android.PathsForTesting(strings.Split(classpathArg, ":")...)
    }

    aaptInfo.combined = combinedRule.Inputs
    return
   }

   app := getAaptInfo("app")
   direct := getAaptInfo("direct")
   transitive := getAaptInfo("transitive")
   shared := getAaptInfo("shared")
   directImport := getAaptInfo("direct_import")
   transitiveImport := getAaptInfo("transitive_import")

   if !testCase.dontVerifyApp {
    android.AssertPathsRelativeToTopEquals(t, "app resources", testCase.appResources, app.resources)
    android.AssertPathsRelativeToTopEquals(t, "app overlays", testCase.appOverlays, app.overlays)
    android.AssertPathsRelativeToTopEquals(t, "app imports", testCase.appImports, app.imports)
    android.AssertPathsRelativeToTopEquals(t, "app srcjars", testCase.appSrcJars, app.srcJars)
    android.AssertPathsRelativeToTopEquals(t, "app classpath", testCase.appClasspath, app.classpath)
    android.AssertPathsRelativeToTopEquals(t, "app combined", testCase.appCombined, app.combined)
   }

   if !testCase.dontVerifyDirect {
    android.AssertPathsRelativeToTopEquals(t, "direct resources", testCase.directResources, direct.resources)
    android.AssertPathsRelativeToTopEquals(t, "direct overlays", testCase.directOverlays, direct.overlays)
    android.AssertPathsRelativeToTopEquals(t, "direct imports", testCase.directImports, direct.imports)
    android.AssertPathsRelativeToTopEquals(t, "direct srcjars", testCase.directSrcJars, direct.srcJars)
    android.AssertPathsRelativeToTopEquals(t, "direct classpath", testCase.directClasspath, direct.classpath)
    android.AssertPathsRelativeToTopEquals(t, "direct combined", testCase.directCombined, direct.combined)
   }

   if !testCase.dontVerifyTransitive {
    android.AssertPathsRelativeToTopEquals(t, "transitive resources", testCase.transitiveResources, transitive.resources)
    android.AssertPathsRelativeToTopEquals(t, "transitive overlays", testCase.transitiveOverlays, transitive.overlays)
    android.AssertPathsRelativeToTopEquals(t, "transitive imports", testCase.transitiveImports, transitive.imports)
    android.AssertPathsRelativeToTopEquals(t, "transitive srcjars", testCase.transitiveSrcJars, transitive.srcJars)
    android.AssertPathsRelativeToTopEquals(t, "transitive classpath", testCase.transitiveClasspath, transitive.classpath)
    android.AssertPathsRelativeToTopEquals(t, "transitive combined", testCase.transitiveCombined, transitive.combined)
   }

   if !testCase.dontVerifyShared {
    android.AssertPathsRelativeToTopEquals(t, "shared resources", testCase.sharedResources, shared.resources)
    android.AssertPathsRelativeToTopEquals(t, "shared overlays", testCase.sharedOverlays, shared.overlays)
    android.AssertPathsRelativeToTopEquals(t, "shared imports", testCase.sharedImports, shared.imports)
    android.AssertPathsRelativeToTopEquals(t, "shared srcjars", testCase.sharedSrcJars, shared.srcJars)
    android.AssertPathsRelativeToTopEquals(t, "shared classpath", testCase.sharedClasspath, shared.classpath)
    android.AssertPathsRelativeToTopEquals(t, "shared combined", testCase.sharedCombined, shared.combined)
   }

   if !testCase.dontVerifyDirectImport {
    android.AssertPathsRelativeToTopEquals(t, "direct_import resources", testCase.directImportResources, directImport.resources)
    android.AssertPathsRelativeToTopEquals(t, "direct_import overlays", testCase.directImportOverlays, directImport.overlays)
    android.AssertPathsRelativeToTopEquals(t, "direct_import imports", testCase.directImportImports, directImport.imports)
   }

   if !testCase.dontVerifyTransitiveImport {
    android.AssertPathsRelativeToTopEquals(t, "transitive_import resources", testCase.transitiveImportResources, transitiveImport.resources)
    android.AssertPathsRelativeToTopEquals(t, "transitive_import overlays", testCase.transitiveImportOverlays, transitiveImport.overlays)
    android.AssertPathsRelativeToTopEquals(t, "transitive_import imports", testCase.transitiveImportImports, transitiveImport.imports)
   }
  })
 }
}

func TestAndroidResourceOverlays(t *testing.T) {
 t.Parallel()
 type moduleAndVariant struct {
  module  string
  variant string
 }

 testCases := []struct {
  name                       string
  enforceRROTargets          []string
  enforceRROExcludedOverlays []string
  resourceFiles              map[moduleAndVariant][]string
  overlayFiles               map[moduleAndVariant][]string
  rroDirs                    map[moduleAndVariant][]string
 }{
  {
   name:                       "no RRO",
   enforceRROTargets:          nil,
   enforceRROExcludedOverlays: nil,
   resourceFiles: map[moduleAndVariant][]string{
    {"foo", "android_common"}:  nil,
    {"bar", "android_common"}:  {"bar/res/res/values/strings.xml"},
    {"lib", "android_common"}:  nil,
    {"lib2", "android_common"}: {"lib2/res/res/values/strings.xml"},
   },
   overlayFiles: map[moduleAndVariant][]string{
    {"foo", "android_common"}: {
     "out/soong/.intermediates/lib2/android_common/package-res.apk",
     "out/soong/.intermediates/lib/android_common/package-res.apk",
     "out/soong/.intermediates/lib3/android_common/package-res.apk",
     "foo/res/res/values/strings.xml",
     "device/vendor/blah/static_overlay/foo/res/values/strings.xml",
     "device/vendor/blah/overlay/foo/res/values/strings.xml",
     "product/vendor/blah/overlay/foo/res/values/strings.xml",
    },
    {"bar", "android_common"}: {
     "device/vendor/blah/static_overlay/bar/res/values/strings.xml",
     "device/vendor/blah/overlay/bar/res/values/strings.xml",
    },
    {"lib", "android_common"}: {
     "lib/res/res/values/strings.xml",
     "device/vendor/blah/overlay/lib/res/values/strings.xml",
    },
   },
   rroDirs: map[moduleAndVariant][]string{
    {"foo", "android_common"}: nil,
    {"bar", "android_common"}: nil,
   },
  },
  {
   name:                       "enforce RRO on foo",
   enforceRROTargets:          []string{"foo"},
   enforceRROExcludedOverlays: []string{"device/vendor/blah/static_overlay"},
   resourceFiles: map[moduleAndVariant][]string{
    {"foo", "android_common"}:      nil,
    {"bar", "android_common"}:      {"bar/res/res/values/strings.xml"},
    {"lib", "android_common"}:      nil,
    {"lib", "android_common_rro"}:  nil,
    {"lib2", "android_common"}:     {"lib2/res/res/values/strings.xml"},
    {"lib2", "android_common_rro"}: {"lib2/res/res/values/strings.xml"},
   },
   overlayFiles: map[moduleAndVariant][]string{
    {"foo", "android_common"}: {
     "out/soong/.intermediates/lib2/android_common_rro/package-res.apk",
     "out/soong/.intermediates/lib/android_common_rro/package-res.apk",
     "out/soong/.intermediates/lib3/android_common_rro/package-res.apk",
     "foo/res/res/values/strings.xml",
     "device/vendor/blah/static_overlay/foo/res/values/strings.xml",
    },
    {"bar", "android_common"}: {
     "device/vendor/blah/static_overlay/bar/res/values/strings.xml",
     "device/vendor/blah/overlay/bar/res/values/strings.xml",
    },
    {"lib", "android_common"}: {
     "lib/res/res/values/strings.xml",
     "device/vendor/blah/overlay/lib/res/values/strings.xml",
    },
    {"lib", "android_common_rro"}: {
     "lib/res/res/values/strings.xml",
    },
   },

   rroDirs: map[moduleAndVariant][]string{
    {"foo", "android_common"}: {
     "device:device/vendor/blah/overlay/foo/res",
     "product:product/vendor/blah/overlay/foo/res",
     "device:device/vendor/blah/overlay/lib/res",
    },
    {"bar", "android_common"}:     nil,
    {"lib", "android_common"}:     nil,
    {"lib", "android_common_rro"}: {"device:device/vendor/blah/overlay/lib/res"},
   },
  },
  {
   name:              "enforce RRO on all",
   enforceRROTargets: []string{"*"},
   enforceRROExcludedOverlays: []string{
    // Excluding specific apps/res directories also allowed.
    "device/vendor/blah/static_overlay/foo",
    "device/vendor/blah/static_overlay/bar/res",
   },
   resourceFiles: map[moduleAndVariant][]string{
    {"foo", "android_common"}:  nil,
    {"bar", "android_common"}:  {"bar/res/res/values/strings.xml"},
    {"lib", "android_common"}:  nil,
    {"lib2", "android_common"}: {"lib2/res/res/values/strings.xml"},
   },
   overlayFiles: map[moduleAndVariant][]string{
    {"foo", "android_common"}: {
     "out/soong/.intermediates/lib2/android_common/package-res.apk",
     "out/soong/.intermediates/lib/android_common/package-res.apk",
     "out/soong/.intermediates/lib3/android_common/package-res.apk",
     "foo/res/res/values/strings.xml",
     "device/vendor/blah/static_overlay/foo/res/values/strings.xml",
    },
    {"bar", "android_common"}: {"device/vendor/blah/static_overlay/bar/res/values/strings.xml"},
    {"lib", "android_common"}: {
     "lib/res/res/values/strings.xml",
    },
   },
   rroDirs: map[moduleAndVariant][]string{
    {"foo", "android_common"}: {
     "device:device/vendor/blah/overlay/foo/res",
     "product:product/vendor/blah/overlay/foo/res",
     // Lib dep comes after the direct deps
     "device:device/vendor/blah/overlay/lib/res",
    },
    {"bar", "android_common"}: {"device:device/vendor/blah/overlay/bar/res"},
    {"lib", "android_common"}: {"device:device/vendor/blah/overlay/lib/res"},
   },
  },
 }

 deviceResourceOverlays := []string{
  "device/vendor/blah/overlay",
  "device/vendor/blah/overlay2",
  "device/vendor/blah/static_overlay",
 }

 productResourceOverlays := []string{
  "product/vendor/blah/overlay",
 }

 fs := android.MockFS{
  "foo/res/res/values/strings.xml":                               nil,
  "bar/res/res/values/strings.xml":                               nil,
  "lib/res/res/values/strings.xml":                               nil,
  "lib2/res/res/values/strings.xml":                              nil,
  "device/vendor/blah/overlay/foo/res/values/strings.xml":        nil,
  "device/vendor/blah/overlay/bar/res/values/strings.xml":        nil,
  "device/vendor/blah/overlay/lib/res/values/strings.xml":        nil,
  "device/vendor/blah/static_overlay/foo/res/values/strings.xml": nil,
  "device/vendor/blah/static_overlay/bar/res/values/strings.xml": nil,
  "device/vendor/blah/overlay2/res/values/strings.xml":           nil,
  "product/vendor/blah/overlay/foo/res/values/strings.xml":       nil,
 }

 bp := `
   android_app {
    name: "foo",
    sdk_version: "current",
    resource_dirs: ["foo/res"],
    static_libs: ["lib", "lib3"],
   }

   android_app {
    name: "bar",
    sdk_version: "current",
    resource_dirs: ["bar/res"],
   }

   android_library {
    name: "lib",
    sdk_version: "current",
    resource_dirs: ["lib/res"],
    static_libs: ["lib2"],
   }

   android_library {
    name: "lib2",
    sdk_version: "current",
    resource_dirs: ["lib2/res"],
   }

   // This library has the same resources as lib (should not lead to dupe RROs)
   android_library {
    name: "lib3",
    sdk_version: "current",
    resource_dirs: ["lib/res"]
   }
  `

 for _, testCase := range testCases {
  t.Run(testCase.name, func(t *testing.T) {
   t.Parallel()
   result := android.GroupFixturePreparers(
    PrepareForTestWithJavaDefaultModules,
    fs.AddToFixture(),
    android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
     variables.DeviceResourceOverlays = deviceResourceOverlays
     variables.ProductResourceOverlays = productResourceOverlays
     if testCase.enforceRROTargets != nil {
      variables.EnforceRROTargets = testCase.enforceRROTargets
     }
     if testCase.enforceRROExcludedOverlays != nil {
      variables.EnforceRROExcludedOverlays = testCase.enforceRROExcludedOverlays
     }
    }),
   ).RunTestWithBp(t, bp)

   resourceListToFiles := func(module android.TestingModule, list []string) (files []string) {
    for _, o := range list {
     res := module.MaybeOutput(o)
     if res.Rule != nil {
      // If the overlay is compiled as part of this moduleAndVariant (i.e. a .arsc.flat file),
      // verify the inputs to the .arsc.flat rule.
      files = append(files, res.Inputs.Strings()...)
     } else {
      // Otherwise, verify the full path to the output of the other moduleAndVariant
      files = append(files, o)
     }
    }
    return files
   }

   getResources := func(moduleName, variantName string) (resourceFiles, overlayFiles, rroDirs []string) {
    module := result.ModuleForTests(t, moduleName, variantName)
    resourceList := module.MaybeOutput("aapt2/res.list")
    if resourceList.Rule != nil {
     resourceFiles = resourceListToFiles(module, android.PathsRelativeToTop(resourceList.Inputs))
    }
    overlayList := module.MaybeOutput("aapt2/overlay.list")
    if overlayList.Rule != nil {
     overlayFiles = resourceListToFiles(module, android.PathsRelativeToTop(overlayList.Inputs))
    }

    for _, d := range module.Module().(AndroidLibraryDependency).RRODirsDepSet().ToList() {
     var prefix string
     if d.overlayType == device {
      prefix = "device:"
     } else if d.overlayType == product {
      prefix = "product:"
     } else {
      t.Fatalf("Unexpected overlayType %d", d.overlayType)
     }
     rroDirs = append(rroDirs, prefix+android.PathRelativeToTop(d.path))
    }

    return resourceFiles, overlayFiles, rroDirs
   }

   modules := []moduleAndVariant{
    {"foo", "android_common"},
    {"foo", "android_common_rro"},
    {"bar", "android_common"},
    {"bar", "android_common_rro"},
    {"lib", "android_common"},
    {"lib", "android_common_rro"},
    {"lib2", "android_common"},
    {"lib2", "android_common_rro"},
   }
   for _, moduleAndVariant := range modules {
    if _, exists := testCase.resourceFiles[moduleAndVariant]; !exists {
     continue
    }
    resourceFiles, overlayFiles, rroDirs := getResources(moduleAndVariant.module, moduleAndVariant.variant)

    if !reflect.DeepEqual(resourceFiles, testCase.resourceFiles[moduleAndVariant]) {
     t.Errorf("expected %s resource files:\n  %#v\n got:\n  %#v",
      moduleAndVariant, testCase.resourceFiles[moduleAndVariant], resourceFiles)
    }
    if !reflect.DeepEqual(overlayFiles, testCase.overlayFiles[moduleAndVariant]) {
     t.Errorf("expected %s overlay files:\n  %#v\n got:\n  %#v",
      moduleAndVariant, testCase.overlayFiles[moduleAndVariant], overlayFiles)
    }
    if !reflect.DeepEqual(rroDirs, testCase.rroDirs[moduleAndVariant]) {
     t.Errorf("expected %s rroDirs:  %#v\n got:\n  %#v",
      moduleAndVariant, testCase.rroDirs[moduleAndVariant], rroDirs)
    }
   }
  })
 }
}

func checkSdkVersion(t *testing.T, result *android.TestResult, expectedSdkVersion string) {
 foo := result.ModuleForTests(t, "foo", "android_common")
 link := foo.Output("package-res.apk")
 linkFlags := strings.Split(link.Args["flags"], " ")
 min := android.IndexList("--min-sdk-version", linkFlags)
 target := android.IndexList("--target-sdk-version", linkFlags)

 if min == -1 || target == -1 || min == len(linkFlags)-1 || target == len(linkFlags)-1 {
  t.Fatalf("missing --min-sdk-version or --target-sdk-version in link flags: %q", linkFlags)
 }

 gotMinSdkVersion := linkFlags[min+1]
 gotTargetSdkVersion := linkFlags[target+1]

 android.AssertStringEquals(t, "incorrect --min-sdk-version", expectedSdkVersion, gotMinSdkVersion)

 android.AssertStringEquals(t, "incorrect --target-sdk-version", expectedSdkVersion, gotTargetSdkVersion)
}

func TestAppSdkVersion(t *testing.T) {
 t.Parallel()
 testCases := []struct {
  name                  string
  sdkVersion            string
  platformSdkInt        int
  platformSdkCodename   string
  platformSdkFinal      bool
  minSdkVersionBp       string
  expectedMinSdkVersion string
  platformApis          bool
  activeCodenames       []string
 }{
  {
   name:                  "current final SDK",
   sdkVersion:            "current",
   platformSdkInt:        27,
   platformSdkCodename:   "REL",
   platformSdkFinal:      true,
   expectedMinSdkVersion: "27",
  },
  {
   name:                  "current non-final SDK",
   sdkVersion:            "current",
   platformSdkInt:        27,
   platformSdkCodename:   "OMR1",
   platformSdkFinal:      false,
   expectedMinSdkVersion: "OMR1",
   activeCodenames:       []string{"OMR1"},
  },
  {
   name:                  "default final SDK",
   sdkVersion:            "",
   platformApis:          true,
   platformSdkInt:        27,
   platformSdkCodename:   "REL",
   platformSdkFinal:      true,
   expectedMinSdkVersion: "27",
  },
  {
   name:                  "default non-final SDK",
   sdkVersion:            "",
   platformApis:          true,
   platformSdkInt:        27,
   platformSdkCodename:   "OMR1",
   platformSdkFinal:      false,
   expectedMinSdkVersion: "OMR1",
   activeCodenames:       []string{"OMR1"},
  },
  {
   name:                  "14",
   sdkVersion:            "14",
   expectedMinSdkVersion: "14",
   platformSdkCodename:   "S",
   activeCodenames:       []string{"S"},
  },
  {
   name:                  "two active SDKs",
   sdkVersion:            "module_current",
   minSdkVersionBp:       "UpsideDownCake",
   expectedMinSdkVersion: "UpsideDownCake", // And not VanillaIceCream
   platformSdkCodename:   "VanillaIceCream",
   activeCodenames:       []string{"UpsideDownCake", "VanillaIceCream"},
  },
 }

 for _, moduleType := range []string{"android_app", "android_library"} {
  for _, test := range testCases {
   t.Run(moduleType+" "+test.name, func(t *testing.T) {
    t.Parallel()
    platformApiProp := ""
    if test.platformApis {
     platformApiProp = "platform_apis: true,"
    }
    minSdkVersionProp := ""
    if test.minSdkVersionBp != "" {
     minSdkVersionProp = fmt.Sprintf(` min_sdk_version: "%s",`, test.minSdkVersionBp)
    }
    bp := fmt.Sprintf(`%s {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "%s",
     %s
     %s
    }`, moduleType, test.sdkVersion, platformApiProp, minSdkVersionProp)

    result := android.GroupFixturePreparers(
     prepareForJavaTest,
     android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
      variables.Platform_sdk_version = &test.platformSdkInt
      variables.Platform_sdk_codename = &test.platformSdkCodename
      variables.Platform_version_active_codenames = test.activeCodenames
      variables.Platform_sdk_final = &test.platformSdkFinal
     }),
     FixtureWithPrebuiltApis(map[string][]string{
      "14": {"foo"},
     }),
    ).RunTestWithBp(t, bp)

    checkSdkVersion(t, result, test.expectedMinSdkVersion)
   })
  }
 }
}

func TestVendorAppSdkVersion(t *testing.T) {
 t.Parallel()
 testCases := []struct {
  name                                  string
  sdkVersion                            string
  platformSdkInt                        int
  platformSdkCodename                   string
  platformSdkFinal                      bool
  deviceCurrentApiLevelForVendorModules string
  expectedMinSdkVersion                 string
 }{
  {
   name:                                  "current final SDK",
   sdkVersion:                            "current",
   platformSdkInt:                        29,
   platformSdkCodename:                   "REL",
   platformSdkFinal:                      true,
   deviceCurrentApiLevelForVendorModules: "29",
   expectedMinSdkVersion:                 "29",
  },
  {
   name:                                  "current final SDK",
   sdkVersion:                            "current",
   platformSdkInt:                        29,
   platformSdkCodename:                   "REL",
   platformSdkFinal:                      true,
   deviceCurrentApiLevelForVendorModules: "28",
   expectedMinSdkVersion:                 "28",
  },
  {
   name:                                  "current final SDK",
   sdkVersion:                            "current",
   platformSdkInt:                        29,
   platformSdkCodename:                   "Q",
   platformSdkFinal:                      false,
   deviceCurrentApiLevelForVendorModules: "28",
   expectedMinSdkVersion:                 "28",
  },
 }

 for _, moduleType := range []string{"android_app", "android_library"} {
  for _, sdkKind := range []string{"", "system_"} {
   for _, test := range testCases {
    t.Run(moduleType+" "+test.name, func(t *testing.T) {
     t.Parallel()
     bp := fmt.Sprintf(`%s {
      name: "foo",
      srcs: ["a.java"],
      sdk_version: "%s%s",
      vendor: true,
     }`, moduleType, sdkKind, test.sdkVersion)

     result := android.GroupFixturePreparers(
      prepareForJavaTest,
      android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
       variables.Platform_sdk_version = &test.platformSdkInt
       variables.Platform_sdk_codename = &test.platformSdkCodename
       variables.Platform_sdk_final = &test.platformSdkFinal
       variables.DeviceCurrentApiLevelForVendorModules = &test.deviceCurrentApiLevelForVendorModules
       variables.DeviceSystemSdkVersions = []string{"28", "29"}
      }),
      FixtureWithPrebuiltApis(map[string][]string{
       "28":      {"foo"},
       "29":      {"foo"},
       "current": {"foo"},
      }),
     ).RunTestWithBp(t, bp)

     checkSdkVersion(t, result, test.expectedMinSdkVersion)
    })
   }
  }
 }
}

func TestJNIABI(t *testing.T) {
 t.Parallel()
 ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
  cc_library {
   name: "libjni",
   system_shared_libs: [],
   sdk_version: "current",
   stl: "none",
  }

  android_test {
   name: "test",
   sdk_version: "core_platform",
   jni_libs: ["libjni"],
  }

  android_test {
   name: "test_first",
   sdk_version: "core_platform",
   compile_multilib: "first",
   jni_libs: ["libjni"],
  }

  android_test {
   name: "test_both",
   sdk_version: "core_platform",
   compile_multilib: "both",
   jni_libs: ["libjni"],
  }

  android_test {
   name: "test_32",
   sdk_version: "core_platform",
   compile_multilib: "32",
   jni_libs: ["libjni"],
  }

  android_test {
   name: "test_64",
   sdk_version: "core_platform",
   compile_multilib: "64",
   jni_libs: ["libjni"],
  }
  `)

 testCases := []struct {
  name string
  abis []string
 }{
  {"test", []string{"arm64-v8a"}},
  {"test_first", []string{"arm64-v8a"}},
  {"test_both", []string{"arm64-v8a", "armeabi-v7a"}},
  {"test_32", []string{"armeabi-v7a"}},
  {"test_64", []string{"arm64-v8a"}},
 }

 for _, test := range testCases {
  t.Run(test.name, func(t *testing.T) {
   t.Parallel()
   app := ctx.ModuleForTests(t, test.name, "android_common")
   jniLibZip := app.Output("jnilibs.zip")
   var abis []string
   args := strings.Fields(jniLibZip.Args["jarArgs"])
   for i := 0; i < len(args); i++ {
    if args[i] == "-P" {
     abis = append(abis, filepath.Base(args[i+1]))
     i++
    }
   }
   if !reflect.DeepEqual(abis, test.abis) {
    t.Errorf("want abis %v, got %v", test.abis, abis)
   }
  })
 }
}

func TestAppSdkVersionByPartition(t *testing.T) {
 t.Parallel()
 testJavaError(t, "sdk_version must have a value when the module is located at vendor or product", `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   vendor: true,
   platform_apis: true,
  }
 `)

 testJava(t, `
  android_app {
   name: "bar",
   srcs: ["b.java"],
   platform_apis: true,
  }
 `)

 for _, enforce := range []bool{true, false} {
  bp := `
   android_app {
    name: "foo",
    srcs: ["a.java"],
    product_specific: true,
    platform_apis: true,
   }

   java_library {
    name: "framework-minus-internal-res",
    srcs: ["b.java"],
   }
  `

  errorHandler := android.FixtureExpectsNoErrors
  if enforce {
   errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern("sdk_version must have a value when the module is located at vendor or product")
  }

  android.GroupFixturePreparers(
   PrepareForTestWithJavaDefaultModules,
   android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
    variables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
   }),
  ).
   ExtendWithErrorHandler(errorHandler).
   RunTestWithBp(t, bp)
 }
}

func TestJNIPackaging(t *testing.T) {
 t.Parallel()
 ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
  cc_library {
   name: "libjni",
   system_shared_libs: [],
   stl: "none",
   sdk_version: "current",
  }

  android_app {
   name: "app",
   jni_libs: ["libjni"],
   sdk_version: "current",
  }

  android_app {
   name: "app_noembed",
   jni_libs: ["libjni"],
   use_embedded_native_libs: false,
   sdk_version: "current",
  }

  android_app {
   name: "app_embed",
   jni_libs: ["libjni"],
   use_embedded_native_libs: true,
   sdk_version: "current",
  }

  android_test {
   name: "test",
   sdk_version: "current",
   jni_libs: ["libjni"],
  }

  android_test {
   name: "test_noembed",
   sdk_version: "current",
   jni_libs: ["libjni"],
   use_embedded_native_libs: false,
  }

  android_test_helper_app {
   name: "test_helper",
   sdk_version: "current",
   jni_libs: ["libjni"],
  }

  android_test_helper_app {
   name: "test_helper_noembed",
   sdk_version: "current",
   jni_libs: ["libjni"],
   use_embedded_native_libs: false,
  }
  `)

 testCases := []struct {
  name       string
  packaged   bool
  compressed bool
 }{
  {"app", false, false},
  {"app_noembed", false, false},
  {"app_embed", true, false},
  {"test", true, false},
  {"test_noembed", true, true},
  {"test_helper", true, false},
  {"test_helper_noembed", true, true},
 }

 for _, test := range testCases {
  t.Run(test.name, func(t *testing.T) {
   t.Parallel()
   app := ctx.ModuleForTests(t, test.name, "android_common")
   jniLibZip := app.MaybeOutput("jnilibs.zip")
   if g, w := (jniLibZip.Rule != nil), test.packaged; g != w {
    t.Errorf("expected jni packaged %v, got %v", w, g)
   }

   if jniLibZip.Rule != nil {
    if g, w := !strings.Contains(jniLibZip.Args["jarArgs"], "-L 0"), test.compressed; g != w {
     t.Errorf("expected jni compressed %v, got %v", w, g)
    }

    if !strings.Contains(jniLibZip.Implicits[0].String(), "_sdk_") {
     t.Errorf("expected input %q to use sdk variant", jniLibZip.Implicits[0].String())
    }
   }
  })
 }
}

func TestJNISDK(t *testing.T) {
 t.Parallel()
 ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
  cc_library {
   name: "libjni",
   system_shared_libs: [],
   stl: "none",
   sdk_version: "current",
  }

  android_test {
   name: "app_platform",
   jni_libs: ["libjni"],
   platform_apis: true,
  }

  android_test {
   name: "app_sdk",
   jni_libs: ["libjni"],
   sdk_version: "current",
  }

  android_test {
   name: "app_force_platform",
   jni_libs: ["libjni"],
   sdk_version: "current",
   jni_uses_platform_apis: true,
  }

  android_test {
   name: "app_force_sdk",
   jni_libs: ["libjni"],
   platform_apis: true,
   jni_uses_sdk_apis: true,
  }

  cc_library {
   name: "libvendorjni",
   system_shared_libs: [],
   stl: "none",
   vendor: true,
  }

  android_test {
   name: "app_vendor",
   jni_libs: ["libvendorjni"],
   sdk_version: "current",
   vendor: true,
  }
 `)

 testCases := []struct {
  name      string
  sdkJNI    bool
  vendorJNI bool
 }{
  {name: "app_platform"},
  {name: "app_sdk", sdkJNI: true},
  {name: "app_force_platform"},
  {name: "app_force_sdk", sdkJNI: true},
  {name: "app_vendor", vendorJNI: true},
 }

 platformJNI := ctx.ModuleForTests(t, "libjni", "android_arm64_armv8-a_shared").
  Output("libjni.so").Output.String()
 sdkJNI := ctx.ModuleForTests(t, "libjni", "android_arm64_armv8-a_sdk_shared").
  Output("libjni.so").Output.String()
 vendorJNI := ctx.ModuleForTests(t, "libvendorjni", "android_vendor_arm64_armv8-a_shared").
  Output("libvendorjni.so").Output.String()

 for _, test := range testCases {
  t.Run(test.name, func(t *testing.T) {
   t.Parallel()
   app := ctx.ModuleForTests(t, test.name, "android_common")

   jniLibZip := app.MaybeOutput("jnilibs.zip")
   if len(jniLibZip.Implicits) != 1 {
    t.Fatalf("expected exactly one jni library, got %q", jniLibZip.Implicits.Strings())
   }
   gotJNI := jniLibZip.Implicits[0].String()

   if test.sdkJNI {
    if gotJNI != sdkJNI {
     t.Errorf("expected SDK JNI library %q, got %q", sdkJNI, gotJNI)
    }
   } else if test.vendorJNI {
    if gotJNI != vendorJNI {
     t.Errorf("expected platform JNI library %q, got %q", vendorJNI, gotJNI)
    }
   } else {
    if gotJNI != platformJNI {
     t.Errorf("expected platform JNI library %q, got %q", platformJNI, gotJNI)
    }
   }
  })
 }

 t.Run("jni_uses_platform_apis_error", func(t *testing.T) {
  t.Parallel()
  testJavaError(t, `jni_uses_platform_apis: can only be set for modules that set sdk_version`, `
   android_test {
    name: "app_platform",
    platform_apis: true,
    jni_uses_platform_apis: true,
   }
  `)
 })

 t.Run("jni_uses_sdk_apis_error", func(t *testing.T) {
  t.Parallel()
  testJavaError(t, `jni_uses_sdk_apis: can only be set for modules that do not set sdk_version`, `
   android_test {
    name: "app_sdk",
    sdk_version: "current",
    jni_uses_sdk_apis: true,
   }
  `)
 })

}

func TestCertificates(t *testing.T) {
 t.Parallel()
 testCases := []struct {
  name                     string
  bp                       string
  allowMissingDependencies bool
  certificateOverride      string
  expectedCertSigningFlags string
  expectedCertificate      string
 }{
  {
   name: "default",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current",
    }
   `,
   certificateOverride:      "",
   expectedCertSigningFlags: "--disable-v1",
   expectedCertificate:      "build/make/target/product/security/testkey",
  },
  {
   name: "module certificate property",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     certificate: ":new_certificate",
     sdk_version: "current",
    }

    android_app_certificate {
     name: "new_certificate",
     certificate: "cert/new_cert",
    }
   `,
   certificateOverride:      "",
   expectedCertSigningFlags: "--disable-v1",
   expectedCertificate:      "cert/new_cert",
  },
  {
   name: "path certificate property",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     certificate: "expiredkey",
     sdk_version: "current",
    }
   `,
   certificateOverride:      "",
   expectedCertSigningFlags: "--disable-v1",
   expectedCertificate:      "build/make/target/product/security/expiredkey",
  },
  {
   name: "certificate overrides",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     certificate: "expiredkey",
     sdk_version: "current",
    }

    android_app_certificate {
     name: "new_certificate",
     certificate: "cert/new_cert",
    }
   `,
   certificateOverride:      "foo:new_certificate",
   expectedCertSigningFlags: "--disable-v1",
   expectedCertificate:      "cert/new_cert",
  },
  {
   name: "certificate signing flags",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     certificate: ":new_certificate",
     lineage: "lineage.bin",
     rotationMinSdkVersion: "32",
     sdk_version: "current",
    }

    android_app_certificate {
     name: "new_certificate",
     certificate: "cert/new_cert",
    }
   `,
   certificateOverride:      "",
   expectedCertSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32 --disable-v1",
   expectedCertificate:      "cert/new_cert",
  },
  {
   name: "cert signing flags from filegroup",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     certificate: ":new_certificate",
     lineage: ":lineage_bin",
     rotationMinSdkVersion: "32",
     sdk_version: "current",
    }

    android_app_certificate {
     name: "new_certificate",
     certificate: "cert/new_cert",
    }

    filegroup {
     name: "lineage_bin",
     srcs: ["lineage.bin"],
    }
   `,
   certificateOverride:      "",
   expectedCertSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32 --disable-v1",
   expectedCertificate:      "cert/new_cert",
  },
  {
   name: "missing with AllowMissingDependencies",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     certificate: ":new_certificate",
     sdk_version: "current",
    }
   `,
   expectedCertificate:      "out/soong/.intermediates/foo/android_common/missing",
   allowMissingDependencies: true,
  },
  {
   name: "old minSdkVersion",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current",
     min_sdk_version: "23",
    }
   `,
   certificateOverride:      "",
   expectedCertSigningFlags: "",
   expectedCertificate:      "build/make/target/product/security/testkey",
  },
 }

 for _, test := range testCases {
  t.Run(test.name, func(t *testing.T) {
   t.Parallel()
   result := android.GroupFixturePreparers(
    PrepareForTestWithJavaDefaultModules,
    android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
     if test.certificateOverride != "" {
      variables.CertificateOverrides = []string{test.certificateOverride}
     }
     if test.allowMissingDependencies {
      variables.Allow_missing_dependencies = proptools.BoolPtr(true)
     }
    }),
    android.FixtureModifyContext(func(ctx *android.TestContext) {
     ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
    }),
   ).RunTestWithBp(t, test.bp)

   foo := result.ModuleForTests(t, "foo", "android_common")

   certificate := foo.Module().(*AndroidApp).certificate
   android.AssertPathRelativeToTopEquals(t, "certificates key", test.expectedCertificate+".pk8", certificate.Key)
   // The sign_target_files_apks and check_target_files_signatures
   // tools require that certificates have a .x509.pem extension.
   android.AssertPathRelativeToTopEquals(t, "certificates pem", test.expectedCertificate+".x509.pem", certificate.Pem)

   signapk := foo.Output("foo.apk")
   if !android.IsErrorRule(signapk.Rule) {
    signCertificateFlags := signapk.Args["certificates"]
    expectedFlags := certificate.Pem.String() + " " + certificate.Key.String()
    android.AssertStringEquals(t, "certificates flags", expectedFlags, signCertificateFlags)

    certSigningFlags := signapk.Args["flags"]
    android.AssertStringEquals(t, "cert signing flags", test.expectedCertSigningFlags, certSigningFlags)
   }
  })
 }
}

func TestRequestV4SigningFlag(t *testing.T) {
 t.Parallel()
 testCases := []struct {
  name     string
  bp       string
  expected string
 }{
  {
   name: "default",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current",
    }
   `,
   expected: "--disable-v1",
  },
  {
   name: "default",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current",
     v4_signature: false,
    }
   `,
   expected: "--disable-v1",
  },
  {
   name: "module certificate property",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current",
     v4_signature: true,
    }
   `,
   expected: "--enable-v4 --disable-v1",
  },
 }

 for _, test := range testCases {
  t.Run(test.name, func(t *testing.T) {
   t.Parallel()
   result := android.GroupFixturePreparers(
    PrepareForTestWithJavaDefaultModules,
   ).RunTestWithBp(t, test.bp)

   foo := result.ModuleForTests(t, "foo", "android_common")

   signapk := foo.Output("foo.apk")
   signFlags := signapk.Args["flags"]
   android.AssertStringEquals(t, "signing flags", test.expected, signFlags)
  })
 }
}

func TestPackageNameOverride(t *testing.T) {
 t.Parallel()
 testCases := []struct {
  name                string
  bp                  string
  packageNameOverride string
  expected            []string
 }{
  {
   name: "default",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current",
    }
   `,
   packageNameOverride: "",
   expected: []string{
    "out/soong/.intermediates/foo/android_common/foo.apk",
    "out/target/product/test_device/system/app/foo/foo.apk",
   },
  },
  {
   name: "overridden via PRODUCT_PACKAGE_NAME_OVERRIDES",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current",
    }
   `,
   packageNameOverride: "foo:bar",
   expected: []string{
    // The package apk should be still be the original name for test dependencies.
    "out/soong/.intermediates/foo/android_common/bar.apk",
    "out/target/product/test_device/system/app/bar/bar.apk",
   },
  },
  {
   name: "overridden via stem",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current",
     stem: "bar",
    }
   `,
   packageNameOverride: "",
   expected: []string{
    "out/soong/.intermediates/foo/android_common/bar.apk",
    "out/target/product/test_device/system/app/bar/bar.apk",
   },
  },
 }

 for _, test := range testCases {
  t.Run(test.name, func(t *testing.T) {
   t.Parallel()
   result := android.GroupFixturePreparers(
    PrepareForTestWithJavaDefaultModules,
    android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
     if test.packageNameOverride != "" {
      variables.PackageNameOverrides = []string{test.packageNameOverride}
     }
    }),
   ).RunTestWithBp(t, test.bp)

   foo := result.ModuleForTests(t, "foo", "android_common")

   outSoongDir := result.Config.SoongOutDir()

   outputs := foo.AllOutputs()
   outputMap := make(map[string]bool)
   for _, o := range outputs {
    outputMap[android.StringPathRelativeToTop(outSoongDir, o)] = true
   }
   for _, e := range test.expected {
    if _, exist := outputMap[e]; !exist {
     t.Errorf("Can't find %q in output files.\nAll outputs:%v", e, outputs)
    }
   }
  })
 }
}

func TestInstrumentationTargetOverridden(t *testing.T) {
 t.Parallel()
 bp := `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   sdk_version: "current",
  }

  android_test {
   name: "bar",
   instrumentation_for: "foo",
   sdk_version: "current",
  }
  `

 result := android.GroupFixturePreparers(
  PrepareForTestWithJavaDefaultModules,
  android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
   variables.ManifestPackageNameOverrides = []string{"foo:org.dandroid.bp"}
  }),
 ).RunTestWithBp(t, bp)

 bar := result.ModuleForTests(t, "bar", "android_common")
 res := bar.Output("package-res.apk")
 aapt2Flags := res.Args["flags"]
 e := "--rename-instrumentation-target-package org.dandroid.bp"
 if !strings.Contains(aapt2Flags, e) {
  t.Errorf("target package renaming flag, %q is missing in aapt2 link flags, %q", e, aapt2Flags)
 }
}

func TestOverrideAndroidApp(t *testing.T) {
 t.Parallel()
 result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
  t, `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   certificate: "expiredkey",
   overrides: ["qux"],
   sdk_version: "current",
  }

  override_android_app {
   name: "bar",
   base: "foo",
   certificate: ":new_certificate",
   lineage: "lineage.bin",
   rotationMinSdkVersion: "32",
   logging_parent: "bah",
  }

  android_app_certificate {
   name: "new_certificate",
   certificate: "cert/new_cert",
  }

  override_android_app {
   name: "baz",
   base: "foo",
   package_name: "org.dandroid.bp",
  }

  override_android_app {
   name: "baz_no_rename_resources",
   base: "foo",
   package_name: "org.dandroid.bp",
   rename_resources_package: false,
  }

  android_app {
   name: "foo_no_rename_resources",
   srcs: ["a.java"],
   certificate: "expiredkey",
   overrides: ["qux"],
   rename_resources_package: false,
   sdk_version: "current",
  }

  override_android_app {
   name: "baz_base_no_rename_resources",
   base: "foo_no_rename_resources",
   package_name: "org.dandroid.bp",
  }

  override_android_app {
   name: "baz_override_base_rename_resources",
   base: "foo_no_rename_resources",
   package_name: "org.dandroid.bp",
   rename_resources_package: true,
  }
  `)

 expectedVariants := []struct {
  name             string
  moduleName       string
  variantName      string
  apkName          string
  apkPath          string
  certFlag         string
  certSigningFlags string
  overrides        []string
  packageFlag      string
  renameResources  bool
  logging_parent   string
 }{
  {
   name:             "foo",
   moduleName:       "foo",
   variantName:      "android_common",
   apkPath:          "out/target/product/test_device/system/app/foo/foo.apk",
   certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
   certSigningFlags: "--disable-v1",
   overrides:        []string{"qux"},
   packageFlag:      "",
   renameResources:  false,
   logging_parent:   "",
  },
  {
   name:             "foo",
   moduleName:       "bar",
   variantName:      "android_common_bar",
   apkPath:          "out/target/product/test_device/system/app/bar/bar.apk",
   certFlag:         "cert/new_cert.x509.pem cert/new_cert.pk8",
   certSigningFlags: "--lineage lineage.bin --rotation-min-sdk-version 32 --disable-v1",
   overrides:        []string{"qux", "foo"},
   packageFlag:      "",
   renameResources:  false,
   logging_parent:   "bah",
  },
  {
   name:             "foo",
   moduleName:       "baz",
   variantName:      "android_common_baz",
   apkPath:          "out/target/product/test_device/system/app/baz/baz.apk",
   certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
   certSigningFlags: "--disable-v1",
   overrides:        []string{"qux", "foo"},
   packageFlag:      "org.dandroid.bp",
   renameResources:  true,
   logging_parent:   "",
  },
  {
   name:             "foo",
   moduleName:       "baz_no_rename_resources",
   variantName:      "android_common_baz_no_rename_resources",
   apkPath:          "out/target/product/test_device/system/app/baz_no_rename_resources/baz_no_rename_resources.apk",
   certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
   certSigningFlags: "--disable-v1",
   overrides:        []string{"qux", "foo"},
   packageFlag:      "org.dandroid.bp",
   renameResources:  false,
   logging_parent:   "",
  },
  {
   name:             "foo_no_rename_resources",
   moduleName:       "baz_base_no_rename_resources",
   variantName:      "android_common_baz_base_no_rename_resources",
   apkPath:          "out/target/product/test_device/system/app/baz_base_no_rename_resources/baz_base_no_rename_resources.apk",
   certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
   certSigningFlags: "--disable-v1",
   overrides:        []string{"qux", "foo_no_rename_resources"},
   packageFlag:      "org.dandroid.bp",
   renameResources:  false,
   logging_parent:   "",
  },
  {
   name:             "foo_no_rename_resources",
   moduleName:       "baz_override_base_rename_resources",
   variantName:      "android_common_baz_override_base_rename_resources",
   apkPath:          "out/target/product/test_device/system/app/baz_override_base_rename_resources/baz_override_base_rename_resources.apk",
   certFlag:         "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
   certSigningFlags: "--disable-v1",
   overrides:        []string{"qux", "foo_no_rename_resources"},
   packageFlag:      "org.dandroid.bp",
   renameResources:  true,
   logging_parent:   "",
  },
 }
 for _, expected := range expectedVariants {
  variant := result.ModuleForTests(t, expected.name, expected.variantName)

  // Check the final apk name
  variant.Output(expected.apkPath)

  // Check the certificate paths
  signapk := variant.Output(expected.moduleName + ".apk")
  certFlag := signapk.Args["certificates"]
  android.AssertStringEquals(t, "certificates flags", expected.certFlag, certFlag)

  // Check the cert signing flags
  certSigningFlags := signapk.Args["flags"]
  android.AssertStringEquals(t, "cert signing flags", expected.certSigningFlags, certSigningFlags)

  // Check if the overrides field values are correctly aggregated.
  mod := variant.Module().(*AndroidApp)
  android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.overridableAppProperties.Overrides)

  // Test Overridable property: Logging_parent
  logging_parent := mod.aapt.LoggingParent
  android.AssertStringEquals(t, "overrides property value for logging parent", expected.logging_parent, logging_parent)

  // Check the package renaming flag, if exists.
  res := variant.Output("package-res.apk")
  aapt2Flags := res.Args["flags"]
  checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
  expectedPackage := expected.packageFlag
  if !expected.renameResources {
   expectedPackage = ""
  }
  checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", expectedPackage)
 }
}

func TestOverrideAndroidAppOverrides(t *testing.T) {
 t.Parallel()
 ctx, _ := testJava(
  t, `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   sdk_version: "current",
   overrides: ["qux"]
  }

  android_app {
   name: "bar",
   srcs: ["b.java"],
   sdk_version: "current",
   overrides: ["foo"]
  }

  override_android_app {
   name: "foo_override",
   base: "foo",
   overrides: ["bar"]
  }
  `)

 expectedVariants := []struct {
  name        string
  moduleName  string
  variantName string
  overrides   []string
 }{
  {
   name:        "foo",
   moduleName:  "foo",
   variantName: "android_common",
   overrides:   []string{"qux"},
  },
  {
   name:        "bar",
   moduleName:  "bar",
   variantName: "android_common",
   overrides:   []string{"foo"},
  },
  {
   name:        "foo",
   moduleName:  "foo_override",
   variantName: "android_common_foo_override",
   overrides:   []string{"bar", "foo"},
  },
 }
 for _, expected := range expectedVariants {
  variant := ctx.ModuleForTests(t, expected.name, expected.variantName)

  // Check if the overrides field values are correctly aggregated.
  mod := variant.Module().(*AndroidApp)
  android.AssertDeepEquals(t, "overrides property", expected.overrides, mod.overridableAppProperties.Overrides)
 }
}

func TestOverrideAndroidAppWithPrebuilt(t *testing.T) {
 t.Parallel()
 result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
  t, `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   sdk_version: "current",
  }

  override_android_app {
   name: "bar",
   base: "foo",
  }

  android_app_import {
   name: "bar",
   prefer: true,
   apk: "bar.apk",
   presigned: true,
  }
  `)

 // An app that has an override that also has a prebuilt should not be hidden.
 foo := result.ModuleForTests(t, "foo", "android_common")
 if foo.Module().IsHideFromMake() {
  t.Errorf("expected foo to have HideFromMake false")
 }

 // An override that also has a prebuilt should be hidden.
 barOverride := result.ModuleForTests(t, "foo", "android_common_bar")
 if !barOverride.Module().IsHideFromMake() {
  t.Errorf("expected bar override variant of foo to have HideFromMake true")
 }
}

func TestOverrideAndroidAppStem(t *testing.T) {
 t.Parallel()
 ctx, _ := testJava(t, `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   sdk_version: "current",
  }
  override_android_app {
   name: "bar",
   base: "foo",
  }
  override_android_app {
   name: "baz",
   base: "foo",
   stem: "baz_stem",
  }
  android_app {
   name: "foo2",
   srcs: ["a.java"],
   sdk_version: "current",
   stem: "foo2_stem",
  }
  override_android_app {
   name: "bar2",
   base: "foo2",
  }
  override_android_app {
   name: "baz2",
   base: "foo2",
   stem: "baz2_stem",
  }
 `)
 for _, expected := range []struct {
  moduleName  string
  variantName string
  apkPath     string
 }{
  {
   moduleName:  "foo",
   variantName: "android_common",
   apkPath:     "out/target/product/test_device/system/app/foo/foo.apk",
  },
  {
   moduleName:  "foo",
   variantName: "android_common_bar",
   apkPath:     "out/target/product/test_device/system/app/bar/bar.apk",
  },
  {
   moduleName:  "foo",
   variantName: "android_common_baz",
   apkPath:     "out/target/product/test_device/system/app/baz_stem/baz_stem.apk",
  },
  {
   moduleName:  "foo2",
   variantName: "android_common",
   apkPath:     "out/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
  },
  {
   moduleName:  "foo2",
   variantName: "android_common_bar2",
   // Note that this may cause the duplicate output error.
   apkPath: "out/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
  },
  {
   moduleName:  "foo2",
   variantName: "android_common_baz2",
   apkPath:     "out/target/product/test_device/system/app/baz2_stem/baz2_stem.apk",
  },
 } {
  variant := ctx.ModuleForTests(t, expected.moduleName, expected.variantName)
  variant.Output(expected.apkPath)
 }
}

func TestOverrideAndroidAppDependency(t *testing.T) {
 t.Parallel()
 ctx, _ := testJava(t, `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   sdk_version: "current",
  }

  override_android_app {
   name: "bar",
   base: "foo",
   package_name: "org.dandroid.bp",
  }

  android_test {
   name: "baz",
   srcs: ["b.java"],
   instrumentation_for: "foo",
  }

  android_test {
   name: "qux",
   srcs: ["b.java"],
   instrumentation_for: "bar",
  }
  `)

 // Verify baz, which depends on the overridden module foo, has the correct classpath javac arg.
 javac := ctx.ModuleForTests(t, "baz", "android_common").Rule("javac")
 fooTurbine := "out/soong/.intermediates/foo/android_common/turbine/foo.jar"
 if !strings.Contains(javac.Args["classpath"], fooTurbine) {
  t.Errorf("baz classpath %v does not contain %q", javac.Args["classpath"], fooTurbine)
 }

 // Verify qux, which depends on the overriding module bar, has the correct classpath javac arg.
 javac = ctx.ModuleForTests(t, "qux", "android_common").Rule("javac")
 barTurbine := "out/soong/.intermediates/foo/android_common_bar/turbine/foo.jar"
 if !strings.Contains(javac.Args["classpath"], barTurbine) {
  t.Errorf("qux classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
 }
}

func TestOverrideAndroidTest(t *testing.T) {
 ctx, _ := testJava(t, `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   package_name: "com.android.foo",
   sdk_version: "current",
  }

  override_android_app {
   name: "bar",
   base: "foo",
   package_name: "com.android.bar",
  }

  android_test {
   name: "foo_test",
   srcs: ["b.java"],
   instrumentation_for: "foo",
  }

  override_android_test {
   name: "bar_test",
   base: "foo_test",
   package_name: "com.android.bar.test",
   instrumentation_for: "bar",
   instrumentation_target_package: "com.android.bar",
  }
  `)

 expectedVariants := []struct {
  moduleName        string
  variantName       string
  apkPath           string
  overrides         []string
  targetVariant     string
  packageFlag       string
  targetPackageFlag string
 }{
  {
   variantName:       "android_common",
   apkPath:           "/target/product/test_device/testcases/foo_test/arm64/foo_test.apk",
   overrides:         nil,
   targetVariant:     "android_common",
   packageFlag:       "",
   targetPackageFlag: "",
  },
  {
   variantName:       "android_common_bar_test",
   apkPath:           "/target/product/test_device/testcases/bar_test/arm64/bar_test.apk",
   overrides:         []string{"foo_test"},
   targetVariant:     "android_common_bar",
   packageFlag:       "com.android.bar.test",
   targetPackageFlag: "com.android.bar",
  },
 }
 for _, expected := range expectedVariants {
  variant := ctx.ModuleForTests(t, "foo_test", expected.variantName)

  // Check the final apk name
  variant.Output("out" + expected.apkPath)

  // Check if the overrides field values are correctly aggregated.
  mod := variant.Module().(*AndroidTest)
  if !reflect.DeepEqual(expected.overrides, mod.overridableAppProperties.Overrides) {
   t.Errorf("Incorrect overrides property value, expected: %q, got: %q",
    expected.overrides, mod.overridableAppProperties.Overrides)
  }

  // Check if javac classpath has the correct jar file path. This checks instrumentation_for overrides.
  javac := variant.Rule("javac")
  turbine := filepath.Join("out", "soong", ".intermediates", "foo", expected.targetVariant, "turbine", "foo.jar")
  if !strings.Contains(javac.Args["classpath"], turbine) {
   t.Errorf("classpath %q does not contain %q", javac.Args["classpath"], turbine)
  }

  // Check aapt2 flags.
  res := variant.Output("package-res.apk")
  aapt2Flags := res.Args["flags"]
  checkAapt2LinkFlag(t, aapt2Flags, "rename-manifest-package", expected.packageFlag)
  checkAapt2LinkFlag(t, aapt2Flags, "rename-resources-package", expected.packageFlag)
  checkAapt2LinkFlag(t, aapt2Flags, "rename-instrumentation-target-package", expected.targetPackageFlag)
 }
}

func TestAndroidTest_FixTestConfig(t *testing.T) {
 ctx, _ := testJava(t, `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   package_name: "com.android.foo",
   sdk_version: "current",
  }

  android_test {
   name: "foo_test",
   srcs: ["b.java"],
   instrumentation_for: "foo",
  }

  android_test {
   name: "bar_test",
   srcs: ["b.java"],
   package_name: "com.android.bar.test",
   instrumentation_for: "foo",
   mainline_package_name: "com.android.bar",
  }

  override_android_test {
   name: "baz_test",
   base: "foo_test",
   package_name: "com.android.baz.test",
   mainline_package_name: "com.android.baz",
  }
  `)

 testCases := []struct {
  moduleName    string
  variantName   string
  expectedFlags []string
 }{
  {
   moduleName:  "foo_test",
   variantName: "android_common",
  },
  {
   moduleName:  "bar_test",
   variantName: "android_common",
   expectedFlags: []string{
    "--manifest out/soong/.intermediates/bar_test/android_common/manifest_fixer/AndroidManifest.xml",
    "--package-name com.android.bar.test",
    "--mainline-package-name com.android.bar",
   },
  },
  {
   moduleName:  "foo_test",
   variantName: "android_common_baz_test",
   expectedFlags: []string{
    "--manifest out/soong/.intermediates/foo_test/android_common_baz_test/manifest_fixer/AndroidManifest.xml",
    "--package-name com.android.baz.test",
    "--test-file-name baz_test.apk",
    "out/soong/.intermediates/foo_test/android_common_baz_test/test_config_fixer/AndroidTest.xml",
    "--mainline-package-name com.android.baz",
   },
  },
 }

 for _, test := range testCases {
  variant := ctx.ModuleForTests(t, test.moduleName, test.variantName)
  params := variant.MaybeOutput("test_config_fixer/AndroidTest.xml")

  if len(test.expectedFlags) > 0 {
   if params.Rule == nil {
    t.Errorf("test_config_fixer was expected to run, but didn't")
   } else {
    for _, flag := range test.expectedFlags {
     if !strings.Contains(params.RuleParams.Command, flag) {
      t.Errorf("Flag %q was not found in command: %q", flag, params.RuleParams.Command)
     }
    }
   }
  } else {
   if params.Rule != nil {
    t.Errorf("test_config_fixer was not expected to run, but did: %q", params.RuleParams.Command)
   }
  }
 }
}

func TestInstrumentationTargetPrebuilt(t *testing.T) {
 bp := `
  android_app_import {
   name: "foo",
   apk: "foo.apk",
   presigned: true,
  }

  android_test {
   name: "bar",
   srcs: ["a.java"],
   instrumentation_for: "foo",
   sdk_version: "current",
  }
  `

 android.GroupFixturePreparers(
  PrepareForTestWithJavaDefaultModules,
 ).ExtendWithErrorHandler(
  android.FixtureExpectsAtLeastOneErrorMatchingPattern(
   "instrumentation_for: dependency \"foo\" of type \"android_app_import\" does not provide JavaInfo so is unsuitable for use with this property")).
  RunTestWithBp(t, bp)
}

func TestStl(t *testing.T) {
 t.Parallel()
 ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
  cc_library {
   name: "libjni",
   sdk_version: "current",
   stl: "c++_shared",
  }

  android_test {
   name: "stl",
   jni_libs: ["libjni"],
   compile_multilib: "both",
   sdk_version: "current",
   stl: "c++_shared",
  }

  android_test {
   name: "system",
   jni_libs: ["libjni"],
   compile_multilib: "both",
   sdk_version: "current",
  }
  `)

 testCases := []struct {
  name string
  jnis []string
 }{
  {"stl",
   []string{
    "libjni.so",
    "libc++_shared.so",
   },
  },
  {"system",
   []string{
    "libjni.so",
   },
  },
 }

 for _, test := range testCases {
  t.Run(test.name, func(t *testing.T) {
   t.Parallel()
   app := ctx.ModuleForTests(t, test.name, "android_common")
   jniLibZip := app.Output("jnilibs.zip")
   var jnis []string
   args := strings.Fields(jniLibZip.Args["jarArgs"])
   for i := 0; i < len(args); i++ {
    if args[i] == "-f" {
     jnis = append(jnis, args[i+1])
     i += 1
    }
   }
   jnisJoined := strings.Join(jnis, " ")
   for _, jni := range test.jnis {
    if !strings.Contains(jnisJoined, jni) {
     t.Errorf("missing jni %q in %q", jni, jnis)
    }
   }
  })
 }
}

func TestUsesLibraries(t *testing.T) {
 bp := `
  java_sdk_library {
   name: "foo",
   srcs: ["a.java"],
   api_packages: ["foo"],
   sdk_version: "current",
  }

  java_sdk_library {
   name: "qux",
   srcs: ["a.java"],
   api_packages: ["qux"],
   sdk_version: "current",
  }

  java_sdk_library {
   name: "quuz",
   srcs: ["a.java"],
   api_packages: ["quuz"],
   sdk_version: "current",
  }

  java_sdk_library {
   name: "fred",
   srcs: ["a.java"],
   api_packages: ["fred"],
   sdk_version: "current",
  }

  java_sdk_library {
   name: "bar",
   srcs: ["a.java"],
   api_packages: ["bar"],
   sdk_version: "current",
  }

  java_sdk_library {
   name: "runtime-library",
   srcs: ["a.java"],
   sdk_version: "current",
  }

  java_library {
   name: "static-runtime-helper",
   srcs: ["a.java"],
   libs: ["runtime-library.impl"],
   sdk_version: "current",
  }

  java_library {
   name: "runtime-required-x",
   srcs: ["a.java"],
   installable: true,
   sdk_version: "current",
  }

  java_library {
   name: "runtime-optional-x",
   srcs: ["a.java"],
   installable: true,
   sdk_version: "current",
  }

  android_library {
   name: "static-x",
   uses_libs: ["runtime-required-x"],
   optional_uses_libs: ["runtime-optional-x"],
   sdk_version: "current",
  }

  java_library {
   name: "runtime-required-y",
   srcs: ["a.java"],
   installable: true,
   sdk_version: "current",
  }

  java_library {
   name: "runtime-optional-y",
   srcs: ["a.java"],
   installable: true,
   sdk_version: "current",
  }

  java_library {
   name: "static-y",
   srcs: ["a.java"],
   uses_libs: ["runtime-required-y"],
   optional_uses_libs: [
    "runtime-optional-y",
    "missing-lib-a",
   ],
   sdk_version: "current",
  }

  // A library that has to use "provides_uses_lib", because:
  //    - it is not an SDK library
  //    - its library name is different from its module name
  java_library {
   name: "non-sdk-lib",
   provides_uses_lib: "com.non.sdk.lib",
   installable: true,
   srcs: ["a.java"],
  }

  android_app {
   name: "app",
   srcs: ["a.java"],
   libs: [
    "qux.impl",
    "quuz.stubs"
   ],
   static_libs: [
    "static-runtime-helper",
    // statically linked component libraries should not pull their SDK libraries,
    // so "fred" should not be added to class loader context
    "fred.stubs",
    "static-x",
    "static-y",
   ],
   uses_libs: [
    "foo",
    "non-sdk-lib"
   ],
   sdk_version: "current",
   optional_uses_libs: [
    "bar",
    "missing-lib-b",
   ],
  }

  android_app_import {
   name: "prebuilt",
   apk: "prebuilts/apk/app.apk",
   certificate: "platform",
   uses_libs: [
    "foo",
    "non-sdk-lib",
    "android.test.runner"
   ],
   optional_uses_libs: [
    "bar",
    "missing-lib-b",
   ],
  }
 `

 result := android.GroupFixturePreparers(
  prepareForJavaTest,
  PrepareForTestWithJavaSdkLibraryFiles,
  FixtureWithLastReleaseApis("runtime-library", "foo", "quuz", "qux", "bar", "fred"),
 ).RunTestWithBp(t, bp)

 app := result.ModuleForTests(t, "app", "android_common")
 prebuilt := result.ModuleForTests(t, "prebuilt", "android_common")

 // Test that implicit dependencies on java_sdk_library instances are passed to the manifest.
 // These also include explicit `uses_libs`/`optional_uses_libs` entries, as they may be
 // propagated from dependencies.
 actualManifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
 expectManifestFixerArgs := `--extract-native-libs=true ` +
  `--uses-library foo ` +
  `--uses-library com.non.sdk.lib ` +
  `--uses-library qux ` +
  `--uses-library quuz ` +
  `--uses-library runtime-library ` +
  `--uses-library runtime-required-x ` +
  `--uses-library runtime-required-y ` +
  `--optional-uses-library bar ` +
  `--optional-uses-library runtime-optional-x ` +
  `--optional-uses-library runtime-optional-y`
 android.AssertStringDoesContain(t, "manifest_fixer args", actualManifestFixerArgs, expectManifestFixerArgs)

 // Test that all libraries are verified (library order matters).
 verifyCmd := app.Rule("verify_uses_libraries").RuleParams.Command
 verifyArgs := `--uses-library foo ` +
  `--uses-library com.non.sdk.lib ` +
  `--uses-library qux ` +
  `--uses-library quuz ` +
  `--uses-library runtime-library ` +
  `--uses-library runtime-required-x ` +
  `--uses-library runtime-required-y ` +
  `--optional-uses-library bar ` +
  `--optional-uses-library runtime-optional-x ` +
  `--optional-uses-library runtime-optional-y ` +
  `--missing-optional-uses-library missing-lib-b ` +
  `--missing-optional-uses-library missing-lib-a`
 android.AssertStringDoesContain(t, "verify cmd args", verifyCmd, verifyArgs)

 // Test that all libraries are verified for an APK (library order matters).
 verifyApkCmd := prebuilt.Rule("verify_uses_libraries").RuleParams.Command
 verifyApkArgs := `--uses-library foo ` +
  `--uses-library com.non.sdk.lib ` +
  `--uses-library android.test.runner ` +
  `--optional-uses-library bar ` +
  `--missing-optional-uses-library missing-lib-b `
 android.AssertStringDoesContain(t, "verify apk cmd args", verifyApkCmd, verifyApkArgs)

 // Test that necessary args are passed for constructing CLC in Ninja phase.
 cmd := app.Rule("dexpreopt").RuleParams.Command
 android.AssertStringDoesContain(t, "dexpreopt app cmd context", cmd, "--context-json=")
 android.AssertStringDoesContain(t, "dexpreopt app cmd product_packages", cmd,
  "--product-packages=out/soong/.intermediates/app/android_common/dexpreopt/app/product_packages.txt")
}

func extractContextJson(cmd string) (map[string]interface{}, error) {
 var clc map[string]interface{}
 for _, flag := range strings.Split(cmd, " ") {
  if value, match := strings.CutPrefix(flag, "--context-json='"); match {
   value = strings.TrimSuffix(value, "'")
   if err := json.Unmarshal([]byte(value), &clc); err != nil {
    return nil, err
   }
   return clc, nil
  }
 }
 return nil, errors.New("--context-json not found")
}

func TestClassLoaderContext_SdkLibrary(t *testing.T) {
 bp := `
  java_sdk_library {
   name: "foo",
   srcs: ["a.java"],
   api_packages: ["foo"],
   sdk_version: "current",
   uses_libs: ["bar"],
  }

  java_sdk_library {
   name: "bar",
   srcs: ["b.java"],
   api_packages: ["bar"],
   sdk_version: "current",
  }

  android_app {
   name: "app",
   srcs: ["app.java"],
   libs: ["foo.stubs"],
   uses_libs: ["foo"],
   sdk_version: "current",
  }

  android_app {
   name: "app2",
   srcs: ["app.java"],
   libs: ["foo.impl"],
   uses_libs: ["foo"],
   sdk_version: "current",
  }
 `

 result := android.GroupFixturePreparers(
  prepareForJavaTest,
  PrepareForTestWithJavaSdkLibraryFiles,
  FixtureWithLastReleaseApis("foo", "bar"),
 ).RunTestWithBp(t, bp)

 {
  fooXml := result.ModuleForTests(t, "foo.xml", "android_common").Output("foo.xml")
  fooXmlContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooXml)
  android.AssertStringDoesContain(t, "", fooXmlContents, `dependency="bar"`)
 }

 {
  fooImpl := result.ModuleForTests(t, "foo.impl", "android_common")
  cmd := fooImpl.Rule("dexpreopt").RuleParams.Command

  clc, err := extractContextJson(cmd)
  if err != nil {
   t.Error(err)
   return
  }

  deps := clc["any"].([]interface{})
  android.AssertIntEquals(t, "", 1, len(deps))
  bar := deps[0].(map[string]interface{})
  android.AssertStringEquals(t, "", "bar", bar["Name"].(string))
 }

 for _, name := range []string{"app", "app2"} {
  app := result.ModuleForTests(t, name, "android_common")
  cmd := app.Rule("dexpreopt").RuleParams.Command

  clc, err := extractContextJson(cmd)
  if err != nil {
   t.Error(err)
   return
  }

  deps := clc["any"].([]interface{})
  android.AssertIntEquals(t, "", 1, len(deps))
  foo := deps[0].(map[string]interface{})
  android.AssertStringEquals(t, "", "foo", foo["Name"].(string))
  fooDeps := foo["Subcontexts"].([]interface{})
  android.AssertIntEquals(t, "", 1, len(fooDeps))
  bar := fooDeps[0].(map[string]interface{})
  android.AssertStringEquals(t, "", "bar", bar["Name"].(string))
 }
}

func TestClassLoaderContext_SdkLibrary2(t *testing.T) {
 bp := `
  java_sdk_library {
   name: "foo",
   srcs: ["a.java"],
   api_packages: ["foo"],
   sdk_version: "current",
   libs: ["bar.impl"],
  }

  java_sdk_library {
   name: "bar",
   srcs: ["b.java"],
   api_packages: ["bar"],
   sdk_version: "current",
  }

  android_app {
   name: "app",
   srcs: ["app.java"],
   libs: ["foo.stubs"],
   uses_libs: ["foo"],
   sdk_version: "current",
  }
 `

 result := android.GroupFixturePreparers(
  prepareForJavaTest,
  PrepareForTestWithJavaSdkLibraryFiles,
  FixtureWithLastReleaseApis("foo", "bar"),
 ).RunTestWithBp(t, bp)

 {
  fooXml := result.ModuleForTests(t, "foo.xml", "android_common").Output("foo.xml")
  fooXmlContents := android.ContentFromFileRuleForTests(t, result.TestContext, fooXml)
  android.AssertStringDoesContain(t, "", fooXmlContents, `dependency="bar"`)
 }

 {
  fooImpl := result.ModuleForTests(t, "foo.impl", "android_common")
  cmd := fooImpl.Rule("dexpreopt").RuleParams.Command

  clc, err := extractContextJson(cmd)
  if err != nil {
   t.Error(err)
   return
  }

  deps := clc["any"].([]interface{})
  android.AssertIntEquals(t, "", 1, len(deps))
  bar := deps[0].(map[string]interface{})
  android.AssertStringEquals(t, "", "bar", bar["Name"].(string))
 }

 {
  app := result.ModuleForTests(t, "app", "android_common")
  cmd := app.Rule("dexpreopt").RuleParams.Command

  clc, err := extractContextJson(cmd)
  if err != nil {
   t.Error(err)
   return
  }

  deps := clc["any"].([]interface{})
  android.AssertIntEquals(t, "", 1, len(deps))
  foo := deps[0].(map[string]interface{})
  android.AssertStringEquals(t, "", "foo", foo["Name"].(string))
  fooDeps := foo["Subcontexts"].([]interface{})
  android.AssertIntEquals(t, "", 1, len(fooDeps))
  bar := fooDeps[0].(map[string]interface{})
  android.AssertStringEquals(t, "", "bar", bar["Name"].(string))
 }
}

func TestDexpreoptBcp(t *testing.T) {
 t.Parallel()
 bp := `
  java_sdk_library {
   name: "foo",
   srcs: ["a.java"],
   api_packages: ["foo"],
   sdk_version: "current",
  }

  java_sdk_library {
   name: "bar",
   srcs: ["a.java"],
   api_packages: ["bar"],
   permitted_packages: ["bar"],
   sdk_version: "current",
  }

  android_app {
   name: "app",
   srcs: ["a.java"],
   sdk_version: "current",
  }
 `

 testCases := []struct {
  name   string
  with   bool
  expect string
 }{
  {
   name:   "with updatable bcp",
   with:   true,
   expect: "/system/framework/foo.jar:/system/framework/bar.jar",
  },
  {
   name:   "without updatable bcp",
   with:   false,
   expect: "/system/framework/foo.jar",
  },
 }

 for _, test := range testCases {
  t.Run(test.name, func(t *testing.T) {
   t.Parallel()
   result := android.GroupFixturePreparers(
    prepareForJavaTest,
    PrepareForTestWithJavaSdkLibraryFiles,
    FixtureWithLastReleaseApis("runtime-library", "foo", "bar"),
    dexpreopt.FixtureSetBootJars("platform:foo"),
    dexpreopt.FixtureSetApexBootJars("platform:bar"),
    dexpreopt.FixtureSetPreoptWithUpdatableBcp(test.with),
   ).RunTestWithBp(t, bp)

   app := result.ModuleForTests(t, "app", "android_common")
   cmd := app.Rule("dexpreopt").RuleParams.Command
   bcp := " -Xbootclasspath-locations:" + test.expect + " " // space at the end matters
   android.AssertStringDoesContain(t, "dexpreopt app bcp", cmd, bcp)
  })
 }
}

func TestCodelessApp(t *testing.T) {
 t.Parallel()
 testCases := []struct {
  name   string
  bp     string
  noCode bool
 }{
  {
   name: "normal",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current",
    }
   `,
   noCode: false,
  },
  {
   name: "app without sources",
   bp: `
    android_app {
     name: "foo",
     sdk_version: "current",
    }
   `,
   noCode: true,
  },
  {
   name: "app with libraries",
   bp: `
    android_app {
     name: "foo",
     static_libs: ["lib"],
     sdk_version: "current",
    }

    java_library {
     name: "lib",
     srcs: ["a.java"],
     sdk_version: "current",
    }
   `,
   noCode: false,
  },
  {
   name: "app with sourceless libraries",
   bp: `
    android_app {
     name: "foo",
     static_libs: ["lib"],
     sdk_version: "current",
    }

    java_library {
     name: "lib",
     sdk_version: "current",
    }
   `,
   // TODO(jungjw): this should probably be true
   noCode: false,
  },
 }

 for _, test := range testCases {
  t.Run(test.name, func(t *testing.T) {
   t.Parallel()
   ctx := testApp(t, test.bp)

   foo := ctx.ModuleForTests(t, "foo", "android_common")
   manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
   if strings.Contains(manifestFixerArgs, "--has-no-code") != test.noCode {
    t.Errorf("unexpected manifest_fixer args: %q", manifestFixerArgs)
   }
  })
 }
}

func TestUncompressDex(t *testing.T) {
 t.Parallel()
 testCases := []struct {
  name string
  bp   string

  uncompressedPlatform  bool
  uncompressedUnbundled bool
 }{
  {
   name: "normal",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current",
    }
   `,
   uncompressedPlatform:  true,
   uncompressedUnbundled: false,
  },
  {
   name: "use_embedded_dex",
   bp: `
    android_app {
     name: "foo",
     use_embedded_dex: true,
     srcs: ["a.java"],
     sdk_version: "current",
    }
   `,
   uncompressedPlatform:  true,
   uncompressedUnbundled: true,
  },
  {
   name: "privileged",
   bp: `
    android_app {
     name: "foo",
     privileged: true,
     srcs: ["a.java"],
     sdk_version: "current",
    }
   `,
   uncompressedPlatform:  true,
   uncompressedUnbundled: true,
  },
  {
   name: "normal_uncompress_dex_true",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current",
     uncompress_dex: true,
    }
   `,
   uncompressedPlatform:  true,
   uncompressedUnbundled: true,
  },
  {
   name: "normal_uncompress_dex_false",
   bp: `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     sdk_version: "current",
     uncompress_dex: false,
    }
   `,
   uncompressedPlatform:  false,
   uncompressedUnbundled: false,
  },
 }

 test := func(t *testing.T, bp string, want bool, unbundled bool) {
  t.Helper()

  result := android.GroupFixturePreparers(
   prepareForJavaTest,
   PrepareForTestWithPrebuiltsOfCurrentApi,
   android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
    if unbundled {
     variables.Unbundled_build = proptools.BoolPtr(true)
     variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
    }
   }),
  ).RunTestWithBp(t, bp)

  foo := result.ModuleForTests(t, "foo", "android_common")
  dex := foo.Rule("r8")
  uncompressedInDexJar := strings.Contains(dex.Args["zipFlags"], "-L 0")
  aligned := foo.MaybeRule("zipalign").Rule != nil

  android.AssertBoolEquals(t, "uncompressed in dex", want, uncompressedInDexJar)

  android.AssertBoolEquals(t, "aligne", want, aligned)
 }

 for _, tt := range testCases {
  t.Run(tt.name, func(t *testing.T) {
   t.Parallel()
   t.Run("platform", func(t *testing.T) {
    t.Parallel()
    test(t, tt.bp, tt.uncompressedPlatform, false)
   })
   t.Run("unbundled", func(t *testing.T) {
    t.Parallel()
    test(t, tt.bp, tt.uncompressedUnbundled, true)
   })
  })
 }
}

func checkAapt2LinkFlag(t *testing.T, aapt2Flags, flagName, expectedValue string) {
 if expectedValue != "" {
  expectedFlag := "--" + flagName + " " + expectedValue
  if !strings.Contains(aapt2Flags, expectedFlag) {
   t.Errorf("%q is missing in aapt2 link flags, %q", expectedFlag, aapt2Flags)
  }
 } else {
  unexpectedFlag := "--" + flagName
  if strings.Contains(aapt2Flags, unexpectedFlag) {
   t.Errorf("unexpected flag, %q is found in aapt2 link flags, %q", unexpectedFlag, aapt2Flags)
  }
 }
}

func TestExportedProguardFlagFiles(t *testing.T) {
 t.Parallel()
 ctx, _ := testJava(t, `
  android_app {
   name: "foo",
   sdk_version: "current",
   static_libs: [
    "lib1",
    "lib3",
   ],
  }

  android_library {
   name: "lib1",
   sdk_version: "current",
   optimize: {
    proguard_flags_files: ["lib1proguard.cfg"],
   },
   static_libs: ["lib2"],
  }

  android_library {
   name: "lib2",
   sdk_version: "current",
   optimize: {
    proguard_flags_files: ["lib2proguard.cfg"],
   }
  }

  android_library_import {
   name: "lib3",
   sdk_version: "current",
   aars: ["lib3.aar"],
   static_libs: ["lib4"],
  }

  android_library {
   name: "lib4",
   sdk_version: "current",
   optimize: {
    proguard_flags_files: ["lib4proguard.cfg"],
   }
  }


 `)

 m := ctx.ModuleForTests(t, "foo", "android_common")
 r8 := m.Rule("java.r8")
 implicits := r8.Implicits.RelativeToTop().Strings()
 android.AssertStringListContains(t, "r8 implicits", implicits, "lib1proguard.cfg")
 android.AssertStringListContains(t, "r8 implicits", implicits, "lib2proguard.cfg")
 android.AssertStringListContains(t, "r8 implicits", implicits, "lib4proguard.cfg")
 android.AssertStringListContains(t, "r8 implicits", implicits, "out/soong/.intermediates/lib3/android_common/aar/proguard.txt")

 flags := r8.Args["r8Flags"]
 android.AssertStringDoesContain(t, "r8 flags", flags, "-include lib1proguard.cfg")
 android.AssertStringDoesContain(t, "r8 flags", flags, "-include lib2proguard.cfg")
 android.AssertStringDoesContain(t, "r8 flags", flags, "-include lib4proguard.cfg")
 android.AssertStringDoesContain(t, "r8 flags", flags, "-include out/soong/.intermediates/lib3/android_common/aar/proguard.txt")
}

func TestTargetSdkVersionManifestFixer(t *testing.T) {
 t.Parallel()
 platform_sdk_codename := "Tiramisu"
 platform_sdk_version := 33
 testCases := []struct {
  name                     string
  targetSdkVersionInBp     string
  targetSdkVersionExpected string
  unbundledBuild           bool
  platformSdkFinal         bool
 }{
  {
   name:                     "Non-Unbundled build: Android.bp has targetSdkVersion",
   targetSdkVersionInBp:     "30",
   targetSdkVersionExpected: "30",
   unbundledBuild:           false,
  },
  {
   name:                     "Unbundled build: Android.bp has targetSdkVersion",
   targetSdkVersionInBp:     "30",
   targetSdkVersionExpected: "30",
   unbundledBuild:           true,
  },
  {
   name:                     "Non-Unbundled build: Android.bp has targetSdkVersion equal to platform_sdk_codename",
   targetSdkVersionInBp:     platform_sdk_codename,
   targetSdkVersionExpected: platform_sdk_codename,
   unbundledBuild:           false,
  },
  {
   name:                     "Unbundled build: Android.bp has targetSdkVersion equal to platform_sdk_codename",
   targetSdkVersionInBp:     platform_sdk_codename,
   targetSdkVersionExpected: "10000",
   unbundledBuild:           true,
  },

  {
   name:                     "Non-Unbundled build: Android.bp has no targetSdkVersion",
   targetSdkVersionExpected: platform_sdk_codename,
   unbundledBuild:           false,
  },
  {
   name:                     "Unbundled build: Android.bp has no targetSdkVersion",
   targetSdkVersionExpected: "10000",
   unbundledBuild:           true,
  },
  {
   name:                     "Bundled build in REL branches",
   targetSdkVersionExpected: "33",
   unbundledBuild:           false,
   platformSdkFinal:         true,
  },
 }
 for _, testCase := range testCases {
  t.Run(testCase.name, func(t *testing.T) {
   t.Parallel()
   targetSdkVersionTemplate := ""
   if testCase.targetSdkVersionInBp != "" {
    targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, testCase.targetSdkVersionInBp)
   }
   bp := fmt.Sprintf(`
   android_app {
    name: "foo",
    sdk_version: "current",
    %s
   }
   `, targetSdkVersionTemplate)
   fixture := android.GroupFixturePreparers(
    prepareForJavaTest,
    android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
     if testCase.platformSdkFinal {
      variables.Platform_sdk_final = proptools.BoolPtr(true)
     }
     // explicitly set platform_sdk_codename to make the test deterministic
     variables.Platform_sdk_codename = &platform_sdk_codename
     variables.Platform_sdk_version = &platform_sdk_version
     variables.Platform_version_active_codenames = []string{platform_sdk_codename}
     // create a non-empty list if unbundledBuild==true
     if testCase.unbundledBuild {
      variables.Unbundled_build_apps = []string{"apex_a", "apex_b"}
     }
    }),
   )

   result := fixture.RunTestWithBp(t, bp)
   foo := result.ModuleForTests(t, "foo", "android_common")

   manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
   android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
  })
 }
}

func TestDefaultAppTargetSdkVersionForUpdatableModules(t *testing.T) {
 t.Parallel()
 platform_sdk_codename := "Tiramisu"
 platform_sdk_version := 33
 testCases := []struct {
  name                     string
  platform_sdk_final       bool
  targetSdkVersionInBp     *string
  targetSdkVersionExpected *string
  updatable                bool
 }{
  {
   name:                     "Non-Updatable Module: Android.bp has older targetSdkVersion",
   targetSdkVersionInBp:     proptools.StringPtr("29"),
   targetSdkVersionExpected: proptools.StringPtr("29"),
   updatable:                false,
  },
  {
   name:                     "Updatable Module: Android.bp has older targetSdkVersion",
   targetSdkVersionInBp:     proptools.StringPtr("30"),
   targetSdkVersionExpected: proptools.StringPtr("30"),
   updatable:                true,
  },
  {
   name:                     "Updatable Module: Android.bp has no targetSdkVersion",
   targetSdkVersionExpected: proptools.StringPtr("10000"),
   updatable:                true,
  },
  {
   name:                     "[SDK finalised] Non-Updatable Module: Android.bp has older targetSdkVersion",
   platform_sdk_final:       true,
   targetSdkVersionInBp:     proptools.StringPtr("30"),
   targetSdkVersionExpected: proptools.StringPtr("30"),
   updatable:                false,
  },
  {
   name:                     "[SDK finalised] Updatable Module: Android.bp has older targetSdkVersion",
   platform_sdk_final:       true,
   targetSdkVersionInBp:     proptools.StringPtr("30"),
   targetSdkVersionExpected: proptools.StringPtr("30"),
   updatable:                true,
  },
  {
   name:                     "[SDK finalised] Updatable Module: Android.bp has targetSdkVersion as platform sdk codename",
   platform_sdk_final:       true,
   targetSdkVersionInBp:     proptools.StringPtr(platform_sdk_codename),
   targetSdkVersionExpected: proptools.StringPtr("33"),
   updatable:                true,
  },
  {
   name:                     "[SDK finalised] Updatable Module: Android.bp has no targetSdkVersion",
   platform_sdk_final:       true,
   targetSdkVersionExpected: proptools.StringPtr("33"),
   updatable:                true,
  },
 }
 for _, testCase := range testCases {
  t.Run(testCase.name, func(t *testing.T) {
   t.Parallel()
   targetSdkVersionTemplate := ""
   if testCase.targetSdkVersionInBp != nil {
    targetSdkVersionTemplate = fmt.Sprintf(`target_sdk_version: "%s",`, *testCase.targetSdkVersionInBp)
   }
   bp := fmt.Sprintf(`
   android_app {
    name: "foo",
    sdk_version: "current",
    min_sdk_version: "29",
    %s
    updatable: %t,
    enforce_default_target_sdk_version: %t
   }
   `, targetSdkVersionTemplate, testCase.updatable, testCase.updatable) // enforce default target sdk version if app is updatable

   fixture := android.GroupFixturePreparers(
    PrepareForTestWithJavaDefaultModules,
    android.PrepareForTestWithAllowMissingDependencies,
    android.PrepareForTestWithAndroidMk,
    android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
     // explicitly set following platform variables to make the test deterministic
     variables.Platform_sdk_final = &testCase.platform_sdk_final
     variables.Platform_sdk_version = &platform_sdk_version
     variables.Platform_sdk_codename = &platform_sdk_codename
     variables.Platform_version_active_codenames = []string{platform_sdk_codename}
     variables.Unbundled_build = proptools.BoolPtr(true)
     variables.Unbundled_build_apps = []string{"sampleModule"}
    }),
   )

   result := fixture.RunTestWithBp(t, bp)
   foo := result.ModuleForTests(t, "foo", "android_common")

   manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
   android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+*testCase.targetSdkVersionExpected)
  })
 }
}

func TestEnforceDefaultAppTargetSdkVersionFlag(t *testing.T) {
 t.Parallel()
 platform_sdk_codename := "Tiramisu"
 platform_sdk_version := 33
 testCases := []struct {
  name                           string
  enforceDefaultTargetSdkVersion bool
  expectedError                  string
  platform_sdk_final             bool
  targetSdkVersionInBp           string
  targetSdkVersionExpected       string
  updatable                      bool
 }{
  {
   name:                           "Not enforcing Target SDK Version: Android.bp has older targetSdkVersion",
   enforceDefaultTargetSdkVersion: false,
   targetSdkVersionInBp:           "29",
   targetSdkVersionExpected:       "29",
   updatable:                      false,
  },
  {
   name:                           "[SDK finalised] Enforce Target SDK Version: Android.bp has current targetSdkVersion",
   enforceDefaultTargetSdkVersion: true,
   platform_sdk_final:             true,
   targetSdkVersionInBp:           "current",
   targetSdkVersionExpected:       "33",
   updatable:                      true,
  },
  {
   name:                           "Enforce Target SDK Version: Android.bp has current targetSdkVersion",
   enforceDefaultTargetSdkVersion: true,
   platform_sdk_final:             false,
   targetSdkVersionInBp:           "current",
   targetSdkVersionExpected:       "10000",
   updatable:                      false,
  },
  {
   name:                           "Not enforcing Target SDK Version for Updatable app",
   enforceDefaultTargetSdkVersion: false,
   expectedError:                  "Updatable apps must enforce default target sdk version",
   targetSdkVersionInBp:           "29",
   targetSdkVersionExpected:       "29",
   updatable:                      true,
  },
 }
 for _, testCase := range testCases {
  t.Run(testCase.name, func(t *testing.T) {
   t.Parallel()
   errExpected := testCase.expectedError != ""
   bp := fmt.Sprintf(`
   android_app {
    name: "foo",
    enforce_default_target_sdk_version: %t,
    sdk_version: "current",
    min_sdk_version: "29",
    target_sdk_version: "%v",
    updatable: %t
   }
   `, testCase.enforceDefaultTargetSdkVersion, testCase.targetSdkVersionInBp, testCase.updatable)

   fixture := android.GroupFixturePreparers(
    PrepareForTestWithJavaDefaultModules,
    android.PrepareForTestWithAllowMissingDependencies,
    android.PrepareForTestWithAndroidMk,
    android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
     // explicitly set following platform variables to make the test deterministic
     variables.Platform_sdk_final = &testCase.platform_sdk_final
     variables.Platform_sdk_version = &platform_sdk_version
     variables.Platform_sdk_codename = &platform_sdk_codename
     variables.Unbundled_build = proptools.BoolPtr(true)
     variables.Unbundled_build_apps = []string{"sampleModule"}
    }),
   )

   errorHandler := android.FixtureExpectsNoErrors
   if errExpected {
    errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.expectedError)
   }
   result := fixture.ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, bp)

   if !errExpected {
    foo := result.ModuleForTests(t, "foo", "android_common")
    manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
    android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
   }
  })
 }
}

func TestEnforceDefaultAppTargetSdkVersionFlagForTests(t *testing.T) {
 t.Parallel()
 platform_sdk_codename := "Tiramisu"
 platform_sdk_version := 33
 testCases := []struct {
  name                           string
  enforceDefaultTargetSdkVersion bool
  expectedError                  string
  platform_sdk_final             bool
  targetSdkVersionInBp           string
  targetSdkVersionExpected       string
 }{
  {
   name:                           "Not enforcing Target SDK Version: Android.bp has older targetSdkVersion",
   enforceDefaultTargetSdkVersion: false,
   targetSdkVersionInBp:           "29",
   targetSdkVersionExpected:       "29",
  },
  {
   name:                           "[SDK finalised] Enforce Target SDK Version: Android.bp has current targetSdkVersion",
   enforceDefaultTargetSdkVersion: true,
   platform_sdk_final:             true,
   targetSdkVersionInBp:           "current",
   targetSdkVersionExpected:       "33",
  },
  {
   name:                           "Enforce Target SDK Version: Android.bp has current targetSdkVersion",
   enforceDefaultTargetSdkVersion: true,
   platform_sdk_final:             false,
   targetSdkVersionInBp:           "current",
   targetSdkVersionExpected:       "10000",
  },
 }
 for _, testCase := range testCases {
  t.Run(testCase.name, func(t *testing.T) {
   t.Parallel()
   errExpected := testCase.expectedError != ""
   bp := fmt.Sprintf(`
   android_test {
    name: "foo",
    enforce_default_target_sdk_version: %t,
    min_sdk_version: "29",
    target_sdk_version: "%v",
   }
  `, testCase.enforceDefaultTargetSdkVersion, testCase.targetSdkVersionInBp)

   fixture := android.GroupFixturePreparers(
    PrepareForTestWithJavaDefaultModules,
    android.PrepareForTestWithAllowMissingDependencies,
    android.PrepareForTestWithAndroidMk,
    android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
     // explicitly set following platform variables to make the test deterministic
     variables.Platform_sdk_final = &testCase.platform_sdk_final
     variables.Platform_sdk_version = &platform_sdk_version
     variables.Platform_sdk_codename = &platform_sdk_codename
     variables.Unbundled_build = proptools.BoolPtr(true)
     variables.Unbundled_build_apps = []string{"sampleModule"}
    }),
   )

   errorHandler := android.FixtureExpectsNoErrors
   if errExpected {
    errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(testCase.expectedError)
   }
   result := fixture.ExtendWithErrorHandler(errorHandler).RunTestWithBp(t, bp)

   if !errExpected {
    foo := result.ModuleForTests(t, "foo", "android_common")
    manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
    android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
   }
  })
 }
}

func TestAppMissingCertificateAllowMissingDependencies(t *testing.T) {
 t.Parallel()
 result := android.GroupFixturePreparers(
  PrepareForTestWithJavaDefaultModules,
  android.PrepareForTestWithAllowMissingDependencies,
  android.PrepareForTestWithAndroidMk,
 ).RunTestWithBp(t, `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   certificate: ":missing_certificate",
   sdk_version: "current",
  }

  android_app {
   name: "bar",
   srcs: ["a.java"],
   certificate: ":missing_certificate",
   product_specific: true,
   sdk_version: "current",
  }`)

 foo := result.ModuleForTests(t, "foo", "android_common")
 fooApk := foo.Output("foo.apk")
 if !android.IsErrorRule(fooApk.Rule) {
  t.Fatalf("expected ErrorRule for foo.apk, got %s", fooApk.Rule.String())
 }
 android.AssertStringDoesContain(t, "expected error rule message", fooApk.Args["error"], "missing dependencies: missing_certificate\n")
}

func TestAppIncludesJniPackages(t *testing.T) {
 t.Parallel()
 ctx := android.GroupFixturePreparers(
  PrepareForTestWithJavaDefaultModules,
 ).RunTestWithBp(t, `
  android_library_import {
   name: "aary-nodeps",
   aars: ["aary.aar"],
   extract_jni: true,
  }

  android_library {
   name: "aary-lib",
   sdk_version: "current",
   min_sdk_version: "21",
   static_libs: ["aary-nodeps"],
  }

  android_app {
   name: "aary-lib-dep",
   sdk_version: "current",
   min_sdk_version: "21",
   manifest: "AndroidManifest.xml",
   static_libs: ["aary-lib"],
   use_embedded_native_libs: true,
  }

  android_app {
   name: "aary-import-dep",
   sdk_version: "current",
   min_sdk_version: "21",
   manifest: "AndroidManifest.xml",
   static_libs: ["aary-nodeps"],
   use_embedded_native_libs: true,
  }

  android_app {
   name: "aary-no-use-embedded",
   sdk_version: "current",
   min_sdk_version: "21",
   manifest: "AndroidManifest.xml",
   static_libs: ["aary-nodeps"],
  }`)

 testCases := []struct {
  name       string
  hasPackage bool
 }{
  {
   name:       "aary-import-dep",
   hasPackage: true,
  },
  {
   name:       "aary-lib-dep",
   hasPackage: true,
  },
  {
   name:       "aary-no-use-embedded",
   hasPackage: false,
  },
 }

 for _, tc := range testCases {
  t.Run(tc.name, func(t *testing.T) {
   t.Parallel()
   app := ctx.ModuleForTests(t, tc.name, "android_common")

   outputFile := "jnilibs.zip"
   jniOutputLibZip := app.MaybeOutput(outputFile)
   if jniOutputLibZip.Rule == nil && !tc.hasPackage {
    return
   }

   jniPackage := "arm64-v8a_jni.zip"
   inputs := jniOutputLibZip.Inputs
   foundPackage := false
   for i := 0; i < len(inputs); i++ {
    if strings.Contains(inputs[i].String(), jniPackage) {
     foundPackage = true
    }
   }
   if foundPackage != tc.hasPackage {
    t.Errorf("expected to find %v in %v inputs; inputs = %v", jniPackage, outputFile, inputs)
   }
  })
 }
}

func TestTargetSdkVersionMtsTests(t *testing.T) {
 t.Parallel()
 platformSdkCodename := "Tiramisu"
 android_test := "android_test"
 android_test_helper_app := "android_test_helper_app"
 bpTemplate := `
 %v {
  name: "mytest",
  min_sdk_version: "34",
  target_sdk_version: "%v",
  test_suites: ["othersuite", "%v"],
 }
 `
 testCases := []struct {
  desc                     string
  moduleType               string
  targetSdkVersionInBp     string
  targetSdkVersionExpected string
  testSuites               string
 }{
  {
   desc:                     "Non-MTS android_test_apps targeting current should not be upgraded to 10000",
   moduleType:               android_test,
   targetSdkVersionInBp:     "current",
   targetSdkVersionExpected: platformSdkCodename,
   testSuites:               "non-mts-suite",
  },
  {
   desc:                     "MTS android_test_apps targeting released sdks should not be upgraded to 10000",
   moduleType:               android_test,
   targetSdkVersionInBp:     "29",
   targetSdkVersionExpected: "29",
   testSuites:               "mts-suite",
  },
  {
   desc:                     "MTS android_test_apps targeting current should be upgraded to 10000",
   moduleType:               android_test,
   targetSdkVersionInBp:     "current",
   targetSdkVersionExpected: "10000",
   testSuites:               "mts-suite",
  },
  {
   desc:                     "MTS android_test_helper_apps targeting current should be upgraded to 10000",
   moduleType:               android_test_helper_app,
   targetSdkVersionInBp:     "current",
   targetSdkVersionExpected: "10000",
   testSuites:               "mts-suite",
  },
 }
 fixture := android.GroupFixturePreparers(
  prepareForJavaTest,
  android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
   variables.Platform_sdk_codename = &platformSdkCodename
   variables.Platform_version_active_codenames = []string{platformSdkCodename}
  }),
 )
 for _, testCase := range testCases {
  t.Run(testCase.desc, func(t *testing.T) {
   t.Parallel()
   result := fixture.RunTestWithBp(t, fmt.Sprintf(bpTemplate, testCase.moduleType, testCase.targetSdkVersionInBp, testCase.testSuites))
   mytest := result.ModuleForTests(t, "mytest", "android_common")
   manifestFixerArgs := mytest.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
   android.AssertStringDoesContain(t, testCase.desc, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
  })
 }
}

func TestPrivappAllowlist(t *testing.T) {
 t.Parallel()
 testJavaError(t, "privileged must be set in order to use privapp_allowlist", `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   privapp_allowlist: "perms.xml",
  }
 `)

 result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
  t,
  `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   privapp_allowlist: "privapp_allowlist_com.android.foo.xml",
   privileged: true,
   sdk_version: "current",
  }
  override_android_app {
   name: "bar",
   base: "foo",
   package_name: "com.google.android.foo",
  }
  `,
 )
 app := result.ModuleForTests(t, "foo", "android_common")
 overrideApp := result.ModuleForTests(t, "foo", "android_common_bar")

 // verify that privapp allowlist is created for override apps
 overrideApp.Output("out/soong/.intermediates/foo/android_common_bar/privapp_allowlist_com.google.android.foo.xml")
 expectedAllowlistInput := "privapp_allowlist_com.android.foo.xml"
 overrideActualAllowlistInput := overrideApp.Rule("modifyAllowlist").Input.String()
 if expectedAllowlistInput != overrideActualAllowlistInput {
  t.Errorf("expected override allowlist to be %q; got %q", expectedAllowlistInput, overrideActualAllowlistInput)
 }

 // verify that permissions are copied to device
 app.Output("out/target/product/test_device/system/etc/permissions/foo.xml")
 overrideApp.Output("out/target/product/test_device/system/etc/permissions/bar.xml")
}

func TestPreinstallAllowlist(t *testing.T) {
 t.Parallel()

 result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
  t,
  `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   preinstall_allowlist: "preinstall_allowlist_com.android.foo.xml",
   sdk_version: "current",
  }
  override_android_app {
   name: "bar",
   base: "foo",
   package_name: "com.google.android.foo",
  }
  `,
 )
 app := result.ModuleForTests(t, "foo", "android_common")
 overrideApp := result.ModuleForTests(t, "foo", "android_common_bar")

 // verify that preinstall allowlist is created for override apps
 overrideApp.Output("out/soong/.intermediates/foo/android_common_bar/preinstall_allowlist_com.google.android.foo.xml")
 expectedAllowlistInput := "preinstall_allowlist_com.android.foo.xml"
 overrideActualAllowlistInput := overrideApp.Rule("modifyPreinstallAllowlist").Input.String()
 if expectedAllowlistInput != overrideActualAllowlistInput {
  t.Errorf("expected override allowlist input to be %q; got %q", expectedAllowlistInput, overrideActualAllowlistInput)
 }
 expectedPackageName := "com.google.android.foo"
 overrideActualPackageName := overrideApp.Rule("modifyPreinstallAllowlist").Args["packageName"]
 if expectedPackageName != overrideActualPackageName {
  t.Errorf("expected override package name to be %q; got %q", expectedPackageName, overrideActualPackageName)
 }
 app.Output("out/target/product/test_device/system/etc/sysconfig/foo_preinstall_allowlist.xml")
 overrideApp.Output("out/target/product/test_device/system/etc/sysconfig/bar_preinstall_allowlist.xml")
}

func TestPrivappAllowlistAndroidMk(t *testing.T) {
 t.Parallel()
 result := android.GroupFixturePreparers(
  PrepareForTestWithJavaDefaultModules,
  android.PrepareForTestWithAndroidMk,
 ).RunTestWithBp(
  t,
  `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   privapp_allowlist: "privapp_allowlist_com.android.foo.xml",
   privileged: true,
   sdk_version: "current",
  }
  override_android_app {
   name: "bar",
   base: "foo",
   package_name: "com.google.android.foo",
  }
  `,
 )
 baseApp := result.ModuleForTests(t, "foo", "android_common")
 overrideApp := result.ModuleForTests(t, "foo", "android_common_bar")

 baseAndroidApp := baseApp.Module().(*AndroidApp)
 baseEntries := android.AndroidMkInfoForTest(t, result.TestContext, baseAndroidApp)
 android.AssertStringMatches(
  t,
  "androidmk has incorrect LOCAL_SOONG_INSTALLED_MODULE; expected to find foo.apk",
  baseEntries.PrimaryInfo.EntryMap["LOCAL_SOONG_INSTALLED_MODULE"][0],
  "\\S+foo.apk",
 )
 android.AssertStringMatches(
  t,
  "androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include foo.apk",
  baseEntries.PrimaryInfo.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
  "\\S+foo.apk",
 )
 android.AssertStringMatches(
  t,
  "androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include app",
  baseEntries.PrimaryInfo.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
  "\\S+foo.apk:\\S+/target/product/test_device/system/priv-app/foo/foo.apk",
 )
 android.AssertStringMatches(
  t,
  "androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include privapp_allowlist",
  baseEntries.PrimaryInfo.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
  "privapp_allowlist_com.android.foo.xml:\\S+/target/product/test_device/system/etc/permissions/foo.xml",
 )

 overrideAndroidApp := overrideApp.Module().(*AndroidApp)
 overrideEntries := android.AndroidMkInfoForTest(t, result.TestContext, overrideAndroidApp)
 android.AssertStringMatches(
  t,
  "androidmk has incorrect LOCAL_SOONG_INSTALLED_MODULE; expected to find bar.apk",
  overrideEntries.PrimaryInfo.EntryMap["LOCAL_SOONG_INSTALLED_MODULE"][0],
  "\\S+bar.apk",
 )
 android.AssertStringMatches(
  t,
  "androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include bar.apk",
  overrideEntries.PrimaryInfo.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
  "\\S+bar.apk",
 )
 android.AssertStringMatches(
  t,
  "androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include app",
  overrideEntries.PrimaryInfo.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
  "\\S+bar.apk:\\S+/target/product/test_device/system/priv-app/bar/bar.apk",
 )
 android.AssertStringMatches(
  t,
  "androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include privapp_allowlist",
  overrideEntries.PrimaryInfo.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
  "\\S+soong/.intermediates/foo/android_common_bar/privapp_allowlist_com.google.android.foo.xml:\\S+/target/product/test_device/system/etc/permissions/bar.xml",
 )
}

func TestAppFlagsPackages(t *testing.T) {
 t.Parallel()
 ctx := android.GroupFixturePreparers(
  prepareForJavaTest,
  android.FixtureMergeMockFs(
   map[string][]byte{
    "res/layout/layout.xml":         nil,
    "res/values/strings.xml":        nil,
    "res/values-en-rUS/strings.xml": nil,
   },
  ),
 ).RunTestWithBp(t, `
  android_app {
   name: "foo",
   srcs: ["a.java"],
   sdk_version: "current",
   flags_packages: [
    "bar",
    "baz",
   ],
  }
  aconfig_declarations {
   name: "bar",
   package: "com.example.package.bar",
   container: "com.android.foo",
   srcs: [
    "bar.aconfig",
   ],
  }
  aconfig_declarations {
   name: "baz",
   package: "com.example.package.baz",
   container: "com.android.foo",
   srcs: [
    "baz.aconfig",
   ],
  }
 `)

 foo := ctx.ModuleForTests(t, "foo", "android_common")

 // android_app module depends on aconfig_declarations listed in flags_packages
 android.AssertBoolEquals(t, "foo expected to depend on bar", true,
  CheckModuleHasDependency(t, ctx.TestContext, "foo", "android_common", "bar"))

 android.AssertBoolEquals(t, "foo expected to depend on baz", true,
  CheckModuleHasDependency(t, ctx.TestContext, "foo", "android_common", "baz"))

 aapt2LinkRule := foo.Rule("android/soong/java.aapt2Link")
 linkInFlags := aapt2LinkRule.Args["inFlags"]
 android.AssertStringDoesContain(t,
  "aapt2 link command expected to pass feature flags arguments",
  linkInFlags,
  "--feature-flags @out/soong/.intermediates/bar/aconfig-flags.txt --feature-flags @out/soong/.intermediates/baz/aconfig-flags.txt",
 )

 aapt2CompileRule := foo.Rule("android/soong/java.aapt2Compile")
 compileFlags := aapt2CompileRule.Args["cFlags"]
 android.AssertStringDoesContain(t,
  "aapt2 compile command expected to pass feature flags arguments",
  compileFlags,
  "--feature-flags @out/soong/.intermediates/bar/aconfig-flags.txt --feature-flags @out/soong/.intermediates/baz/aconfig-flags.txt",
 )
}

func TestAppFlagsPackagesPropagation(t *testing.T) {
 t.Parallel()
 ctx := testApp(t, `
  aconfig_declarations {
   name: "foo",
   package: "com.example.package.foo",
   container: "com.android.foo",
   srcs: [
    "foo.aconfig",
   ],
  }
  aconfig_declarations {
   name: "bar",
   package: "com.example.package.bar",
   container: "com.android.bar",
   srcs: [
    "bar.aconfig",
   ],
  }
  aconfig_declarations {
   name: "baz",
   package: "com.example.package.baz",
   container: "com.android.baz",
   srcs: [
    "baz.aconfig",
   ],
  }
  android_library {
   name: "foo_lib",
   srcs: ["a.java"],
   sdk_version: "current",
   flags_packages: [
    "foo",
   ],
  }
  android_library {
   name: "bar_lib",
   srcs: ["a.java"],
   sdk_version: "current",
   flags_packages: [
    "bar",
   ],
  }
  android_app {
   name: "baz_app",
   srcs: ["a.java"],
   sdk_version: "current",
   flags_packages: [
    "baz",
   ],
   static_libs: [
    "bar_lib",
   ],
   libs: [
    "foo_lib",
   ],
  }
 `)

 bazApp := ctx.ModuleForTests(t, "baz_app", "android_common")

 // android_app module depends on aconfig_declarations listed in flags_packages
 // and that of static libs, but not libs
 aapt2LinkRule := bazApp.Rule("android/soong/java.aapt2Link")
 linkInFlags := aapt2LinkRule.Args["inFlags"]
 android.AssertStringDoesContain(t,
  "aapt2 link command expected to pass feature flags arguments of flags_packages and that of its static libs",
  linkInFlags,
  "--feature-flags @out/soong/.intermediates/bar/aconfig-flags.txt --feature-flags @out/soong/.intermediates/baz/aconfig-flags.txt",
 )
 android.AssertStringDoesNotContain(t,
  "aapt2 link command expected to not pass feature flags arguments of flags_packages of its libs",
  linkInFlags,
  "--feature-flags @out/soong/.intermediates/foo/aconfig-flags.txt",
 )
}

// Test that dexpreopt is disabled if an optional_uses_libs exists, but does not provide an implementation.
func TestNoDexpreoptOptionalUsesLibDoesNotHaveImpl(t *testing.T) {
 t.Parallel()
 bp := `
  java_sdk_library_import {
   name: "sdklib_noimpl",
   public: {
    jars: ["stub.jar"],
   },
  }
  android_app {
   name: "app",
   srcs: ["a.java"],
   sdk_version: "current",
   optional_uses_libs: [
    "sdklib_noimpl",
   ],
  }
 `
 result := prepareForJavaTest.RunTestWithBp(t, bp)
 dexpreopt := result.ModuleForTests(t, "app", "android_common").MaybeRule("dexpreopt").Rule
 android.AssertBoolEquals(t, "dexpreopt should be disabled if optional_uses_libs does not have an implementation", true, dexpreopt == nil)
}

func TestTestOnlyApp(t *testing.T) {
 t.Parallel()
 ctx := android.GroupFixturePreparers(
  prepareForJavaTest,
 ).RunTestWithBp(t, `
                // These should be test-only
                android_test {
                        name: "android-test",
                }
                android_test_helper_app {
                        name: "helper-app",
                }
                override_android_test {
                        name: "override-test",
                        base: "android-app",
                }
                // And these should not be
  android_app {
   name: "android-app",
                        srcs: ["b.java"],
   sdk_version: "current",
  }
 `)

 expectedTestOnly := []string{
  "android-test",
  "helper-app",
  "override-test",
 }

 expectedTopLevel := []string{
  "android-test",
  "override-test",
 }

 assertTestOnlyAndTopLevel(t, ctx, expectedTestOnly, expectedTopLevel)
}

func TestTestConfigTemplate(t *testing.T) {
 t.Parallel()
 ctx := android.GroupFixturePreparers(
  prepareForJavaTest,
 ).RunTestWithBp(t, `
  android_test {
   name: "android-test",
   test_config_template: "AndroidTestTemplate.xml",
   test_options: {
    tradefed_options: [
     {
      name: "name1",
      key: "key1",
      value: "value1",
     },
     {
      name: "name2",
      key: "key2",
      value: "value2",
     },
    ],
    test_runner_options: [
     {
      name: "name3",
      key: "key3",
      value: "value3",
     },
     {
      name: "name4",
      key: "key4",
      value: "value4",
     },
    ],
   },
  }
 `)
 type option struct {
  name  string
  key   string
  value string
 }
 re := regexp.MustCompile(`<option name="(.*)" key="(.*)" value="(.*)" />`)
 parse_options := func(optionsString string) []option {
  lines := strings.Split(optionsString, `\n`)
  var ret []option
  for _, l := range lines {
   sm := re.FindStringSubmatch(l)
   if sm == nil {
    continue
   }
   ret = append(ret, option{sm[1], sm[2], sm[3]})
  }
  return ret
 }
 rule := ctx.ModuleForTests(t, "android-test", "android_common").Rule("autogenInstrumentationTest")
 android.AssertSameArray(t, "extraConfigs mismatch",
  []option{
   {"name1", "key1", "value1"},
   {"name2", "key2", "value2"},
  },
  parse_options(rule.Args["extraConfigs"]))
 android.AssertSameArray(t, "extraTestRunnerConfigs mismatch",
  []option{
   {"name3", "key3", "value3"},
   {"name4", "key4", "value4"},
  },
  parse_options(rule.Args["extraTestRunnerConfigs"]))
}

func TestAppStem(t *testing.T) {
 t.Parallel()
 ctx := testApp(t, `
    android_app {
     name: "foo",
     srcs: ["a.java"],
     stem: "foo-new",
     sdk_version: "current",
    }`)

 foo := ctx.ModuleForTests(t, "foo", "android_common")

 outputs := fmt.Sprint(foo.AllOutputs())
 if !strings.Contains(outputs, "foo-new.apk") {
  t.Errorf("Module output does not contain expected apk %s", "foo-new.apk")
 }
}

func TestAppMinSdkVersionOverride(t *testing.T) {
 t.Parallel()
 result := android.GroupFixturePreparers(
  PrepareForTestWithJavaDefaultModules,
 ).RunTestWithBp(t, `
  android_app {
   name: "com.android.foo",
   srcs: ["a.java"],
   sdk_version: "current",
   min_sdk_version: "31",
   updatable: true,
  }
  override_android_app {
   name: "com.android.go.foo",
   base: "com.android.foo",
   min_sdk_version: "33",
  }
 `)
 foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
 fooOverride := result.ModuleForTests(t, "com.android.foo", "android_common_com.android.go.foo").Rule("manifestFixer")

 android.AssertStringDoesContain(t,
  "com.android.foo: expected manifest fixer to set minSdkVersion to T",
  foo.BuildParams.Args["args"],
  "--minSdkVersion  31",
 )
 android.AssertStringDoesContain(t,
  "com.android.go.foo: expected manifest fixer to set minSdkVersion to T",
  fooOverride.BuildParams.Args["args"],
  "--minSdkVersion  33",
 )

}

func TestNotApplyDefaultUpdatableModuleVersion(t *testing.T) {
 t.Parallel()
 result := android.GroupFixturePreparers(
  PrepareForTestWithJavaDefaultModules,
 ).RunTestWithBp(t, `
  android_app {
   name: "com.android.foo",
   srcs: ["a.java"],
   sdk_version: "current",
   min_sdk_version: "31",
  }
 `)
 foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
 android.AssertStringDoesNotContain(t,
  "com.android.foo: expected manifest fixer to not set override-placeholder-version",
  foo.BuildParams.Args["args"],
  "--override-placeholder-version",
 )
}

func TestNotApplyOverrideApexManifestDefaultVersion(t *testing.T) {
 t.Parallel()
 result := android.GroupFixturePreparers(
  PrepareForTestWithJavaDefaultModules,
  android.FixtureMergeEnv(map[string]string{
   "OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION": "1234",
  }),
 ).RunTestWithBp(t, `
  android_app {
   name: "com.android.foo",
   srcs: ["a.java"],
   sdk_version: "current",
   min_sdk_version: "31",
  }
 `)
 foo := result.ModuleForTests(t, "com.android.foo", "android_common").Rule("manifestFixer")
 android.AssertStringDoesNotContain(t,
  "com.android.foo: expected manifest fixer to not set override-placeholder-version",
  foo.BuildParams.Args["args"],
  "--override-placeholder-version",
 )
}

func TestResourcesWithFlagDirectories(t *testing.T) {
 t.Parallel()
 result := android.GroupFixturePreparers(
  PrepareForTestWithJavaDefaultModules,
  android.FixtureMergeMockFs(android.MockFS{
   "res/flag(test.package.flag1)/values/bools.xml":                          nil,
   "res/flag(!test.package.flag2)/values/bools.xml":                         nil,
   "res/flag(test.package.flag1)/values-config/strings_google_services.xml": nil,
   "res/flags(test.package.flag1)/values/strings.xml":                       nil,
   "res/drawable/flag(test.package.flag1)/qs_flashlight_icon_off.xml":       nil,
  }),
 ).RunTestWithBp(t, `
  android_library {
   name: "foo",
   srcs: ["a.java"],
   use_resource_processor: true,
   resource_dirs: [
    "res",
   ],
  }
 `)
 fooModule := result.ModuleForTests(t, "foo", "android_common")
 compileOutputPaths := fooModule.Rule("aapt2Compile").Outputs.Strings()

 android.AssertStringListContains(
  t,
  "Expected to generate flag path",
  compileOutputPaths,
  "out/soong/.intermediates/foo/android_common/aapt2/res/values_bools.(test.package.flag1).arsc.flat",
 )
 android.AssertStringListContains(
  t,
  "Expected to generate flag path with ! prefix in name",
  compileOutputPaths,
  "out/soong/.intermediates/foo/android_common/aapt2/res/values_bools.(!test.package.flag2).arsc.flat",
 )
 android.AssertStringListContains(
  t,
  "Expected to generate flag path with configs",
  compileOutputPaths,
  "out/soong/.intermediates/foo/android_common/aapt2/res/values-config_strings_google_services.(test.package.flag1).arsc.flat",
 )
 android.AssertStringListDoesNotContain(
  t,
  "Expected to not generate flag path with non-flag(flag_name) pattern",
  compileOutputPaths,
  "out/soong/.intermediates/foo/android_common/aapt2/res/values_strings.(test.package.flag1).arsc.flat",
 )
 android.AssertStringListContains(
  t,
  "Expected to generate flag path when it is a subdirectory of resource type subdirectory",
  compileOutputPaths,
  "out/soong/.intermediates/foo/android_common/aapt2/res/drawable_qs_flashlight_icon_off.(test.package.flag1).xml.flat",
 )
}

func TestAutogeneratedStaticRro(t *testing.T) {
 t.Parallel()
 bp := `
android_app {
 name: "foo",
 srcs: ["foo.java"],
 platform_apis: true,
}
override_android_app {
 name: "override_foo",
 base: "foo",
}
`
 testCases := []struct {
  desc               string
  preparer           android.FixturePreparer
  overlayApkExpected bool
 }{
  {
   desc:               "No DEVICE_PACKAGE_OVERLAYS, no overlay .apk file",
   overlayApkExpected: false,
  },
  {
   desc:               "DEVICE_PACKAGE_OVERLAYS exists, but the directory is empty",
   overlayApkExpected: false,
   preparer: android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
    variables.DeviceResourceOverlays = []string{"device/company/test_product"}
   }),
  },
  {
   desc:               "DEVICE_PACKAGE_OVERLAYS exists, directory is non-empty, but does not contain a matching resource dir",
   overlayApkExpected: false,
   preparer: android.GroupFixturePreparers(
    android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
     variables.DeviceResourceOverlays = []string{"device/company/test_product"}
    }),
    android.MockFS{
     "res/foo.xml": nil,
     "device/company/test_product/different_res/foo.xml": nil, // different dir
    }.AddToFixture(),
   ),
  },
  {
   desc:               "DEVICE_PACKAGE_OVERLAYS and the directory contain a matching resource dir",
   overlayApkExpected: true,
   preparer: android.GroupFixturePreparers(
    android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
     variables.DeviceResourceOverlays = []string{"device/company/test_product"}
    }),
    android.MockFS{
     "res/foo.xml": nil,
     "device/company/test_product/res/foo.xml": nil,
    }.AddToFixture(),
   ),
  },
 }
 for _, tc := range testCases {
  t.Run(tc.desc, func(t *testing.T) {
   t.Parallel()
   result := android.GroupFixturePreparers(
    PrepareForTestWithJavaDefaultModules,
    android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
     variables.EnforceRROTargets = []string{"*"}
    }),
    android.OptionalFixturePreparer(tc.preparer),
   ).RunTestWithBp(t, bp)
   vendorOverlayApk := result.ModuleForTests(t, "foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("foo__test_product__auto_generated_rro_vendor.apk")
   android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, vendorOverlayApk.Rule != nil)
   overrideVendorOverlayApk := result.ModuleForTests(t, "override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").MaybeOutput("override_foo__test_product__auto_generated_rro_vendor.apk")
   android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.Rule != nil)
  })
 }
}

func TestNoAutogeneratedStaticRroForDisabledOverrideApps(t *testing.T) {
 t.Parallel()
 bp := `
soong_config_module_type {
 name: "my_custom_override_android_app",
 module_type: "override_android_app",
 config_namespace: "my_namespace",
 value_variables: ["my_app_enabled"],
 properties: ["enabled"],
}
soong_config_bool_variable {
 name: "my_app_enabled",
}
android_app {
 name: "foo",
 srcs: ["foo.java"],
 platform_apis: true,
}
my_custom_override_android_app {
 name: "override_foo",
 base: "foo",
 soong_config_variables: {
  my_app_enabled: {
   enabled: true,
   conditions_default: {
    enabled: false
   },
  },
 }
}
`
 testCases := []struct {
  desc               string
  preparer           android.FixturePreparer
  overlayApkExpected bool
 }{
  {
   desc:               "my_app_enabled is empty",
   overlayApkExpected: false,
  },
  {
   desc:               "my_app_enabled is true",
   overlayApkExpected: true,
   preparer: android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
    variables.VendorVars = map[string]map[string]string{
     "my_namespace": {
      "my_app_enabled": "true",
     },
    }
   }),
  },
 }
 for _, tc := range testCases {
  t.Run(tc.desc, func(t *testing.T) {
   t.Parallel()
   result := android.GroupFixturePreparers(
    PrepareForTestWithJavaDefaultModules,
    android.PrepareForTestWithSoongConfigModuleBuildComponents,
    android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
     variables.EnforceRROTargets = []string{"*"}
    }),
    android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
     variables.DeviceResourceOverlays = []string{"device/company/test_product"}
    }),
    android.MockFS{
     "res/foo.xml": nil,
     "device/company/test_product/res/foo.xml": nil,
    }.AddToFixture(),
    android.OptionalFixturePreparer(tc.preparer),
   ).RunTestWithBp(t, bp)
   overrideVendorOverlayApk := result.ModuleForTests(t, "override_foo__test_product__auto_generated_rro_vendor", "android_arm64_armv8-a").Module().(*AutogenRuntimeResourceOverlay)
   android.AssertBoolEquals(t, tc.desc, tc.overlayApkExpected, overrideVendorOverlayApk.exportPackage != nil)
  })
 }
}

[Dauer der Verarbeitung: 0.91 Sekunden, vorverarbeitet 2026-06-28]