Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

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.97 Sekunden, vorverarbeitet 2026-06-28]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik