// Copyright (C) 2021 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package sdk

import (
	"fmt"
	"path/filepath"
	"strings"
	"testing"

	"android/soong/android"
	"android/soong/dexpreopt"
	"android/soong/java"
)

// fixtureAddPlatformBootclasspathForBootclasspathFragment adds a platform_bootclasspath module that
// references the bootclasspath fragment.
func fixtureAddPlatformBootclasspathForBootclasspathFragment(apex, fragment string) android.FixturePreparer {
	return fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra(apex, fragment, "")
}

// fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra is the same as above, but also adds extra fragments.
func fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra(apex, fragment, extraFragments string) android.FixturePreparer {
	return android.GroupFixturePreparers(
		// Add a platform_bootclasspath module.
		android.FixtureAddTextFile("frameworks/base/boot/Android.bp", fmt.Sprintf(`
			platform_bootclasspath {
				name: "platform-bootclasspath",
				fragments: [
					{
						apex: "%s",
						module: "%s",
					},
					%s
				],
			}
		`, apex, fragment, extraFragments)),
		android.FixtureAddFile("frameworks/base/boot/boot-profile.txt", nil),
		android.FixtureAddFile("frameworks/base/boot/boot-image-profile.txt", nil),
		android.FixtureAddFile("art/build/boot/boot-image-profile.txt", nil),
		android.FixtureAddFile("build/soong/scripts/check_boot_jars/package_allowed_list.txt", nil),
	)
}

// fixtureAddPrebuiltApexForBootclasspathFragment adds a prebuilt_apex that exports the fragment.
func fixtureAddPrebuiltApexForBootclasspathFragment(apex, fragment string) android.FixturePreparer {
	apexFile := fmt.Sprintf("%s.apex", apex)
	dir := "prebuilts/apex"
	return android.GroupFixturePreparers(
		// A preparer to add a prebuilt apex to the test fixture.
		android.FixtureAddTextFile(filepath.Join(dir, "Android.bp"), fmt.Sprintf(`
			prebuilt_apex {
				name: "%s",
				src: "%s",
				exported_bootclasspath_fragments: [
					"%s",
				],
				prefer: false,
			}
		`, apex, apexFile, fragment)),
		android.FixtureAddFile(filepath.Join(dir, apexFile), nil),
	)
}

func TestSnapshotWithBootclasspathFragment_ImageName(t *testing.T) {
	t.Parallel()
	result := android.GroupFixturePreparers(
		prepareForSdkTestWithJava,
		java.PrepareForTestWithDexpreopt,
		prepareForSdkTestWithApex,

		// Some additional files needed for the art apex.
		android.FixtureMergeMockFs(android.MockFS{
			"com.android.art.avbpubkey":                          nil,
			"com.android.art.pem":                                nil,
			"system/sepolicy/apex/com.android.art-file_contexts": nil,
		}),

		// Add a platform_bootclasspath that depends on the fragment.
		fixtureAddPlatformBootclasspathForBootclasspathFragmentWithExtra(
			"com.android.art", "art-bootclasspath-fragment", java.ApexBootJarFragmentsForPlatformBootclasspath),

		dexpreopt.FixtureSetBootImageProfiles("art/build/boot/boot-image-profile.txt"),

		java.PrepareForBootImageConfigTest,
		java.PrepareApexBootJarConfigsAndModules,
		android.FixtureWithRootAndroidBp(`
			sdk {
				name: "mysdk",
				bootclasspath_fragments: ["art-bootclasspath-fragment"],
			}

			apex {
				name: "com.android.art",
				key: "com.android.art.key",
				bootclasspath_fragments: [
					"art-bootclasspath-fragment",
				],
				updatable: false,
			}

			bootclasspath_fragment {
				name: "art-bootclasspath-fragment",
				image_name: "art",
				contents: ["core1", "core2"],
				apex_available: ["com.android.art"],
				hidden_api: {
					split_packages: ["*"],
				},
			}

			apex_key {
				name: "com.android.art.key",
				public_key: "com.android.art.avbpubkey",
				private_key: "com.android.art.pem",
			}

			java_library {
				name: "core1",
				srcs: ["Test.java"],
				system_modules: "none",
				sdk_version: "none",
				compile_dex: true,
				apex_available: ["com.android.art"],
			}

			java_library {
				name: "core2",
				srcs: ["Test.java"],
				system_modules: "none",
				sdk_version: "none",
				compile_dex: true,
				apex_available: ["com.android.art"],
			}
`),
	).RunTest(t)

	// A preparer to update the test fixture used when processing an unpackage snapshot.
	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("com.android.art", "art-bootclasspath-fragment")

	// Check that source on its own configures the bootImageConfig correctly.
	java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
	java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")

	CheckSnapshot(t, result, "mysdk", "",
		checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.

apex_contributions_defaults {
    name: "mysdk.contributions",
    contents: [],
}

prebuilt_bootclasspath_fragment {
    name: "art-bootclasspath-fragment",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["com.android.art"],
    image_name: "art",
    contents: [
        "core1",
        "core2",
    ],
    hidden_api: {
        annotation_flags: "hiddenapi/annotation-flags.csv",
        metadata: "hiddenapi/metadata.csv",
        index: "hiddenapi/index.csv",
        signature_patterns: "hiddenapi/signature-patterns.csv",
        filtered_stub_flags: "hiddenapi/filtered-stub-flags.csv",
        filtered_flags: "hiddenapi/filtered-flags.csv",
    },
}

java_import {
    name: "core1",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["com.android.art"],
    jars: ["java_boot_libs/snapshot/jars/are/invalid/core1.jar"],
}

java_import {
    name: "core2",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["com.android.art"],
    jars: ["java_boot_libs/snapshot/jars/are/invalid/core2.jar"],
}

`),
		checkAllCopyRules(`
.intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
.intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
.intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
.intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
.intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
.intermediates/art-bootclasspath-fragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core1.jar
.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/core2.jar
		`),
		snapshotTestPreparer(checkSnapshotWithoutSource, android.GroupFixturePreparers(
			preparerForSnapshot,
			// Flag ART prebuilts
			android.FixtureMergeMockFs(android.MockFS{
				"apex_contributions/Android.bp": []byte(`
				apex_contributions {
					name: "prebuilt_art_contributions",
					contents: ["prebuilt_com.android.art"],
					api_domain: "com.android.art",
				}
			`)}),
			android.PrepareForTestWithBuildFlag("RELEASE_APEX_CONTRIBUTIONS_ART", "prebuilt_art_contributions"),
		)),

		// Check the behavior of the snapshot without the source.
		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
			// Make sure that the boot jars package check rule includes the dex jars retrieved from the prebuilt apex.
			checkBootJarsPackageCheckRule(t, result,
				append(
					[]string{
						"out/soong/.intermediates/prebuilts/apex/com.android.art/android_common_prebuilt_com.android.art/deapexer/javalib/core1.jar",
						"out/soong/.intermediates/prebuilts/apex/com.android.art/android_common_prebuilt_com.android.art/deapexer/javalib/core2.jar",
						"out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar",
					},
					java.ApexBootJarDexJarPaths...,
				)...,
			)
			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
			java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
		}),

		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),

		// Check the behavior of the snapshot when the source is preferred.
		snapshotTestChecker(checkSnapshotWithSourcePreferred, func(t *testing.T, result *android.TestResult) {
			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
			java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
		}),

		snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),

		// Check the behavior of the snapshot when it is preferred.
		snapshotTestChecker(checkSnapshotPreferredWithSource, func(t *testing.T, result *android.TestResult) {
			java.CheckMutatedArtBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
			java.CheckMutatedFrameworkBootImageConfig(t, result, "out/soong/.intermediates/default/java/dex_bootjars/android_common/meta_lic")
		}),
	)

	// Make sure that the boot jars package check rule includes the dex jars created from the source.
	checkBootJarsPackageCheckRule(t, result,
		append(
			[]string{
				"out/soong/.intermediates/core1/android_common_apex10000/aligned/core1.jar",
				"out/soong/.intermediates/core2/android_common_apex10000/aligned/core2.jar",
				"out/soong/.intermediates/default/java/framework/android_common/aligned/framework.jar",
			},
			java.ApexBootJarDexJarPaths...,
		)...,
	)
}

// checkBootJarsPackageCheckRule checks that the supplied module is an input to the boot jars
// package check rule.
func checkBootJarsPackageCheckRule(t *testing.T, result *android.TestResult, expectedModules ...string) {
	t.Helper()
	platformBcp := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
	bootJarsCheckRule := platformBcp.Rule("boot_jars_package_check")
	command := bootJarsCheckRule.RuleParams.Command
	expectedCommandArgs := " build/soong/scripts/check_boot_jars/package_allowed_list.txt " + strings.Join(expectedModules, " ") + " &&"
	android.AssertStringDoesContain(t, "boot jars package check", command, expectedCommandArgs)
}

func testSnapshotWithBootClasspathFragment_Contents(t *testing.T, sdk string, copyRules string) {
	result := android.GroupFixturePreparers(
		prepareForSdkTestWithJava,
		java.PrepareForTestWithJavaDefaultModules,
		java.PrepareForTestWithJavaSdkLibraryFiles,
		java.FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"),
		java.FixtureConfigureApexBootJars("myapex:mybootlib", "myapex:myothersdklibrary"),
		prepareForSdkTestWithApex,

		// Add a platform_bootclasspath that depends on the fragment.
		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),

		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),
		// Make sure that we have atleast one platform library so that we can check the monolithic hiddenapi
		// file creation.
		java.FixtureConfigureBootJars("platform:foo"),
		android.FixtureModifyMockFS(func(fs android.MockFS) {
			fs["platform/Android.bp"] = []byte(`
		java_library {
			name: "foo",
			srcs: ["Test.java"],
			compile_dex: true,
		}
		`)
			fs["platform/Test.java"] = nil
		}),

		android.FixtureWithRootAndroidBp(sdk+`
			apex {
				name: "myapex",
				key: "myapex.key",
				min_sdk_version: "2",
				bootclasspath_fragments: ["mybootclasspathfragment"],
			}

			bootclasspath_fragment {
				name: "mybootclasspathfragment",
				apex_available: ["myapex"],
				contents: [
					// This should be automatically added to the sdk_snapshot as a java_boot_libs module.
					"mybootlib",
					// This should be automatically added to the sdk_snapshot as a java_sdk_libs module.
					"myothersdklibrary",
				],
				api: {
					stub_libs: ["mysdklibrary"],
				},
				core_platform_api: {
					// This should be automatically added to the sdk_snapshot as a java_sdk_libs module.
					stub_libs: ["mycoreplatform"],
				},
				hidden_api: {
					split_packages: ["*"],
				},
			}

			java_library {
				name: "mybootlib",
				apex_available: ["myapex"],
				srcs: ["Test.java"],
				system_modules: "none",
				sdk_version: "none",
				min_sdk_version: "2",
				compile_dex: true,
				permitted_packages: ["mybootlib"],
			}

			java_sdk_library {
				name: "mysdklibrary",
				apex_available: ["myapex"],
				srcs: ["Test.java"],
				shared_library: false,
				public: {enabled: true},
				min_sdk_version: "2",
				sdk_version: "current",
			}

			java_sdk_library {
				name: "myothersdklibrary",
				apex_available: ["myapex"],
				srcs: ["Test.java"],
				compile_dex: true,
				public: {enabled: true},
				min_sdk_version: "2",
				permitted_packages: ["myothersdklibrary"],
				sdk_version: "current",
			}

			java_sdk_library {
				name: "mycoreplatform",
				apex_available: ["myapex"],
				srcs: ["Test.java"],
				compile_dex: true,
				public: {enabled: true},
				min_sdk_version: "2",
				sdk_version: "current",
			}
		`),
	).RunTest(t)

	// A preparer to update the test fixture used when processing an unpackage snapshot.
	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")

	CheckSnapshot(t, result, "mysdk", "",
		checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.

apex_contributions_defaults {
    name: "mysdk.contributions",
    contents: [
        "prebuilt_myothersdklibrary",
        "prebuilt_mysdklibrary",
        "prebuilt_mycoreplatform",
    ],
}

prebuilt_bootclasspath_fragment {
    name: "mybootclasspathfragment",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["myapex"],
    contents: [
        "mybootlib",
        "myothersdklibrary",
    ],
    api: {
        stub_libs: ["mysdklibrary"],
    },
    core_platform_api: {
        stub_libs: ["mycoreplatform"],
    },
    hidden_api: {
        annotation_flags: "hiddenapi/annotation-flags.csv",
        metadata: "hiddenapi/metadata.csv",
        index: "hiddenapi/index.csv",
        signature_patterns: "hiddenapi/signature-patterns.csv",
        filtered_stub_flags: "hiddenapi/filtered-stub-flags.csv",
        filtered_flags: "hiddenapi/filtered-flags.csv",
    },
}

java_import {
    name: "mybootlib",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["myapex"],
    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
    min_sdk_version: "2",
    permitted_packages: ["mybootlib"],
}

java_sdk_library_import {
    name: "myothersdklibrary",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["myapex"],
    shared_library: true,
    compile_dex: true,
    permitted_packages: ["myothersdklibrary"],
    public: {
        jars: ["sdk_library/public/myothersdklibrary-stubs.jar"],
        stub_srcs: ["sdk_library/public/myothersdklibrary_stub_sources"],
        current_api: "sdk_library/public/myothersdklibrary.txt",
        removed_api: "sdk_library/public/myothersdklibrary-removed.txt",
        sdk_version: "current",
    },
}

java_sdk_library_import {
    name: "mysdklibrary",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["myapex"],
    shared_library: false,
    public: {
        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
        current_api: "sdk_library/public/mysdklibrary.txt",
        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
        sdk_version: "current",
    },
}

java_sdk_library_import {
    name: "mycoreplatform",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["myapex"],
    shared_library: true,
    compile_dex: true,
    public: {
        jars: ["sdk_library/public/mycoreplatform-stubs.jar"],
        stub_srcs: ["sdk_library/public/mycoreplatform_stub_sources"],
        current_api: "sdk_library/public/mycoreplatform.txt",
        removed_api: "sdk_library/public/mycoreplatform-removed.txt",
        sdk_version: "current",
    },
}
		`),
		checkAllCopyRules(copyRules),
		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
		snapshotTestChecker(checkSnapshotWithoutSource, func(t *testing.T, result *android.TestResult) {
			module := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
			var rule android.TestingBuildParams
			rule = module.Output("out/soong/hiddenapi/hiddenapi-flags.csv")
			java.CheckHiddenAPIRuleInputs(t, "monolithic flags", `
				out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/annotation-flags-from-classes.csv
        out/soong/hiddenapi/hiddenapi-stub-flags.txt
        snapshot/hiddenapi/annotation-flags.csv
			`, rule)

			rule = module.Output("out/soong/hiddenapi/hiddenapi-unsupported.csv")
			java.CheckHiddenAPIRuleInputs(t, "monolithic metadata", `
				out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/metadata-from-classes.csv
        snapshot/hiddenapi/metadata.csv
			`, rule)

			rule = module.Output("out/soong/hiddenapi/hiddenapi-index.csv")
			java.CheckHiddenAPIRuleInputs(t, "monolithic index", `
				out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
        snapshot/hiddenapi/index.csv
			`, rule)

			rule = module.Output("out/soong/hiddenapi/hiddenapi-flags.csv.valid")
			android.AssertStringDoesContain(t, "verify-overlaps", rule.RuleParams.Command, " snapshot/hiddenapi/filtered-flags.csv:snapshot/hiddenapi/signature-patterns.csv ")
		}),
		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
		snapshotTestChecker(checkSnapshotWithSourcePreferred, func(t *testing.T, result *android.TestResult) {
			module := result.ModuleForTests(t, "platform-bootclasspath", "android_common")
			rule := module.Output("out/soong/hiddenapi/hiddenapi-flags.csv.valid")
			android.AssertStringDoesContain(t, "verify-overlaps", rule.RuleParams.Command, " out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv:out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv ")
		}),
		snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
	)
}

func TestSnapshotWithBootClasspathFragment_Contents(t *testing.T) {
	t.Parallel()
	t.Run("added-directly", func(t *testing.T) {
		t.Parallel()
		testSnapshotWithBootClasspathFragment_Contents(t, `
			sdk {
				name: "mysdk",
				bootclasspath_fragments: ["mybootclasspathfragment"],
				java_sdk_libs: [
					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
					// a java_sdk_libs module because it is used in the mybootclasspathfragment's
					// api.stub_libs property. However, it is specified here to ensure that duplicates are
					// correctly deduped.
					"mysdklibrary",
				],
			}
		`, `
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
.intermediates/myothersdklibrary.stubs.exportable/android_common/combined/myothersdklibrary.stubs.exportable.jar -> sdk_library/public/myothersdklibrary-stubs.jar
.intermediates/myothersdklibrary.stubs.source/android_common/exportable/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
.intermediates/myothersdklibrary.stubs.source/android_common/exportable/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
.intermediates/mysdklibrary.stubs.exportable/android_common/combined/mysdklibrary.stubs.exportable.jar -> sdk_library/public/mysdklibrary-stubs.jar
.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
.intermediates/mycoreplatform.stubs.exportable/android_common/combined/mycoreplatform.stubs.exportable.jar -> sdk_library/public/mycoreplatform-stubs.jar
.intermediates/mycoreplatform.stubs.source/android_common/exportable/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
.intermediates/mycoreplatform.stubs.source/android_common/exportable/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
`)
	})

	copyBootclasspathFragmentFromApexVariantRules := `
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/index.csv -> hiddenapi/index.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
.intermediates/myothersdklibrary.stubs.exportable/android_common/combined/myothersdklibrary.stubs.exportable.jar -> sdk_library/public/myothersdklibrary-stubs.jar
.intermediates/myothersdklibrary.stubs.source/android_common/exportable/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
.intermediates/myothersdklibrary.stubs.source/android_common/exportable/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
.intermediates/mysdklibrary.stubs.exportable/android_common/combined/mysdklibrary.stubs.exportable.jar -> sdk_library/public/mysdklibrary-stubs.jar
.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
.intermediates/mycoreplatform.stubs.exportable/android_common/combined/mycoreplatform.stubs.exportable.jar -> sdk_library/public/mycoreplatform-stubs.jar
.intermediates/mycoreplatform.stubs.source/android_common/exportable/mycoreplatform.stubs.source_api.txt -> sdk_library/public/mycoreplatform.txt
.intermediates/mycoreplatform.stubs.source/android_common/exportable/mycoreplatform.stubs.source_removed.txt -> sdk_library/public/mycoreplatform-removed.txt
`
	t.Run("added-via-apex", func(t *testing.T) {
		t.Parallel()
		testSnapshotWithBootClasspathFragment_Contents(t, `
			sdk {
				name: "mysdk",
				apexes: ["myapex"],
			}
		`, copyBootclasspathFragmentFromApexVariantRules)
	})

	t.Run("added-directly-and-indirectly", func(t *testing.T) {
		t.Parallel()
		testSnapshotWithBootClasspathFragment_Contents(t, `
			sdk {
				name: "mysdk",
				apexes: ["myapex"],
				// This is not strictly needed as it should be automatically added to the sdk_snapshot as
				// a bootclasspath_fragments module because it is used in the myapex's
				// bootclasspath_fragments property. However, it is specified here to ensure that duplicates
				// are correctly deduped.
				bootclasspath_fragments: ["mybootclasspathfragment"],
				java_sdk_libs: [
					// This is not strictly needed as it should be automatically added to the sdk_snapshot as
					// a java_sdk_libs module because it is used in the mybootclasspathfragment's
					// api.stub_libs property. However, it is specified here to ensure that duplicates are
					// correctly deduped.
					"mysdklibrary",
				],
			}
		`, copyBootclasspathFragmentFromApexVariantRules)
	})
}

// TestSnapshotWithBootClasspathFragment_Fragments makes sure that the fragments property of a
// bootclasspath_fragment is correctly output to the sdk snapshot.
func TestSnapshotWithBootClasspathFragment_Fragments(t *testing.T) {
	t.Parallel()
	result := android.GroupFixturePreparers(
		prepareForSdkTestWithJava,
		java.PrepareForTestWithJavaDefaultModules,
		java.PrepareForTestWithJavaSdkLibraryFiles,
		java.FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary"),
		java.FixtureConfigureApexBootJars("someapex:mysdklibrary", "myotherapex:myotherlib"),
		prepareForSdkTestWithApex,

		// Some additional files needed for the myotherapex.
		android.FixtureMergeMockFs(android.MockFS{
			"system/sepolicy/apex/myotherapex-file_contexts": nil,
			"myotherapex/apex_manifest.json":                 nil,
			"myotherapex/Test.java":                          nil,
		}),

		android.FixtureAddTextFile("myotherapex/Android.bp", `
			apex {
				name: "myotherapex",
				key: "myapex.key",
				min_sdk_version: "2",
				bootclasspath_fragments: ["myotherbootclasspathfragment"],
			}

			bootclasspath_fragment {
				name: "myotherbootclasspathfragment",
				apex_available: ["myotherapex"],
				contents: [
					"myotherlib",
				],
				hidden_api: {
					split_packages: ["*"],
				},
			}

			java_library {
				name: "myotherlib",
				apex_available: ["myotherapex"],
				srcs: ["Test.java"],
				min_sdk_version: "2",
				permitted_packages: ["myothersdklibrary"],
				compile_dex: true,
				sdk_version: "current",
			}
		`),

		android.FixtureWithRootAndroidBp(`
			sdk {
				name: "mysdk",
				bootclasspath_fragments: ["mybootclasspathfragment"],
			}

			bootclasspath_fragment {
				name: "mybootclasspathfragment",
				contents: [
					"mysdklibrary",
				],
				fragments: [
					{
						apex: "myotherapex",
						module: "myotherbootclasspathfragment"
					},
				],
				hidden_api: {
					split_packages: ["*"],
				},
			}

			java_sdk_library {
				name: "mysdklibrary",
				srcs: ["Test.java"],
				shared_library: false,
				public: {enabled: true},
				min_sdk_version: "2",
				sdk_version: "current",
			}
		`),
	).RunTest(t)

	// A preparer to update the test fixture used when processing an unpackage snapshot.
	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")

	CheckSnapshot(t, result, "mysdk", "",
		checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.

apex_contributions_defaults {
    name: "mysdk.contributions",
    contents: ["prebuilt_mysdklibrary"],
}

prebuilt_bootclasspath_fragment {
    name: "mybootclasspathfragment",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["//apex_available:platform"],
    contents: ["mysdklibrary"],
    fragments: [
        {
            apex: "myotherapex",
            module: "myotherbootclasspathfragment",
        },
    ],
    hidden_api: {
        annotation_flags: "hiddenapi/annotation-flags.csv",
        metadata: "hiddenapi/metadata.csv",
        index: "hiddenapi/index.csv",
        signature_patterns: "hiddenapi/signature-patterns.csv",
        filtered_stub_flags: "hiddenapi/filtered-stub-flags.csv",
        filtered_flags: "hiddenapi/filtered-flags.csv",
    },
}

java_sdk_library_import {
    name: "mysdklibrary",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["//apex_available:platform"],
    shared_library: false,
    public: {
        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
        current_api: "sdk_library/public/mysdklibrary.txt",
        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
        sdk_version: "current",
    },
}
		`),
		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
		snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
	)
}

// Test that bootclasspath_fragment works with sdk.
func TestBasicSdkWithBootclasspathFragment(t *testing.T) {
	t.Parallel()
	android.GroupFixturePreparers(
		prepareForSdkTestWithApex,
		prepareForSdkTestWithJava,
		java.PrepareForTestWithJavaDefaultModules,
		android.FixtureMergeMockFs(android.MockFS{
			"java/mybootlib.jar":                nil,
			"hiddenapi/annotation-flags.csv":    nil,
			"hiddenapi/index.csv":               nil,
			"hiddenapi/metadata.csv":            nil,
			"hiddenapi/signature-patterns.csv":  nil,
			"hiddenapi/filtered-stub-flags.csv": nil,
			"hiddenapi/filtered-flags.csv":      nil,
		}),
		android.FixtureWithRootAndroidBp(`
		sdk {
			name: "mysdk",
			bootclasspath_fragments: ["mybootclasspathfragment"],
		}

		apex {
			name: "myapex",
			key: "myapex.key",
			min_sdk_version: "1",
			bootclasspath_fragments: ["mybootclasspathfragment"],
		}

		bootclasspath_fragment {
			name: "mybootclasspathfragment",
			image_name: "art",
			contents: ["mybootlib"],
			apex_available: ["myapex"],
			hidden_api: {
				split_packages: ["*"],
			},
		}

		java_library {
			name: "mybootlib",
			apex_available: ["myapex"],
			srcs: ["Test.java"],
			system_modules: "none",
			sdk_version: "none",
			min_sdk_version: "1",
			compile_dex: true,
		}

		prebuilt_bootclasspath_fragment {
			name: "mybootclasspathfragment",
			prefer: false,
			visibility: ["//visibility:public"],
			apex_available: [
				"myapex",
			],
			image_name: "art",
			contents: ["mybootlib"],
			hidden_api: {
				annotation_flags: "hiddenapi/annotation-flags.csv",
				metadata: "hiddenapi/metadata.csv",
				index: "hiddenapi/index.csv",
				signature_patterns: "hiddenapi/signature-patterns.csv",
				filtered_stub_flags: "hiddenapi/filtered-stub-flags.csv",
				filtered_flags: "hiddenapi/filtered-flags.csv",
			},
		}

		java_import {
			name: "mybootlib",
			visibility: ["//visibility:public"],
			apex_available: ["myapex"],
			jars: ["java/mybootlib.jar"],
		}
	`),
	).RunTest(t)
}

func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) {
	t.Parallel()
	result := android.GroupFixturePreparers(
		prepareForSdkTestWithJava,
		java.PrepareForTestWithJavaDefaultModules,
		java.PrepareForTestWithJavaSdkLibraryFiles,
		java.FixtureWithLastReleaseApis("mysdklibrary", "mynewlibrary"),
		java.FixtureConfigureApexBootJars("myapex:mybootlib", "myapex:mynewlibrary"),
		prepareForSdkTestWithApex,

		// Add a platform_bootclasspath that depends on the fragment.
		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),

		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),

		android.MockFS{
			"my-blocked.txt":                   nil,
			"my-max-target-o-low-priority.txt": nil,
			"my-max-target-p.txt":              nil,
			"my-max-target-q.txt":              nil,
			"my-max-target-r-low-priority.txt": nil,
			"my-removed.txt":                   nil,
			"my-unsupported-packages.txt":      nil,
			"my-unsupported.txt":               nil,
			"my-new-max-target-q.txt":          nil,
		}.AddToFixture(),
		android.FixtureWithRootAndroidBp(`
			sdk {
				name: "mysdk",
				bootclasspath_fragments: ["mybootclasspathfragment"],
			}

			apex {
				name: "myapex",
				key: "myapex.key",
				min_sdk_version: "1",
				bootclasspath_fragments: ["mybootclasspathfragment"],
			}

			bootclasspath_fragment {
				name: "mybootclasspathfragment",
				apex_available: ["myapex"],
				contents: ["mybootlib", "mynewlibrary"],
				api: {
					stub_libs: ["mysdklibrary"],
				},
				hidden_api: {
					unsupported: [
							"my-unsupported.txt",
					],
					removed: [
							"my-removed.txt",
					],
					max_target_r_low_priority: [
							"my-max-target-r-low-priority.txt",
					],
					max_target_q: [
							"my-max-target-q.txt",
					],
					max_target_p: [
							"my-max-target-p.txt",
					],
					max_target_o_low_priority: [
							"my-max-target-o-low-priority.txt",
					],
					blocked: [
							"my-blocked.txt",
					],
					unsupported_packages: [
							"my-unsupported-packages.txt",
					],
					split_packages: ["sdklibrary"],
					package_prefixes: ["sdklibrary.all.mine"],
					single_packages: ["sdklibrary.mine"],
				},
			}

			java_library {
				name: "mybootlib",
				apex_available: ["myapex"],
				srcs: ["Test.java"],
				system_modules: "none",
				sdk_version: "none",
				min_sdk_version: "1",
				compile_dex: true,
				permitted_packages: ["mybootlib"],
			}

			java_sdk_library {
				name: "mysdklibrary",
				srcs: ["Test.java"],
				compile_dex: true,
				public: {enabled: true},
				permitted_packages: ["mysdklibrary"],
				min_sdk_version: "current",
				sdk_version: "current",
			}

			java_sdk_library {
				name: "mynewlibrary",
				apex_available: ["myapex"],
				srcs: ["Test.java"],
				min_sdk_version: "10",
				compile_dex: true,
				public: {enabled: true},
				permitted_packages: ["mysdklibrary"],
				hidden_api: {
					max_target_q: [
							"my-new-max-target-q.txt",
					],
					split_packages: ["sdklibrary", "newlibrary"],
					package_prefixes: ["newlibrary.all.mine"],
					single_packages: ["newlibrary.mine"],
				},
				sdk_version: "current",
			}
		`),
	).RunTest(t)

	// A preparer to update the test fixture used when processing an unpackage snapshot.
	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")

	CheckSnapshot(t, result, "mysdk", "",
		checkAndroidBpContents(`
// This is auto-generated. DO NOT EDIT.

apex_contributions_defaults {
    name: "mysdk.contributions",
    contents: [
        "prebuilt_mynewlibrary",
        "prebuilt_mysdklibrary",
    ],
}

prebuilt_bootclasspath_fragment {
    name: "mybootclasspathfragment",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["myapex"],
    contents: [
        "mybootlib",
        "mynewlibrary",
    ],
    api: {
        stub_libs: ["mysdklibrary"],
    },
    hidden_api: {
        unsupported: ["hiddenapi/my-unsupported.txt"],
        removed: ["hiddenapi/my-removed.txt"],
        max_target_r_low_priority: ["hiddenapi/my-max-target-r-low-priority.txt"],
        max_target_q: [
            "hiddenapi/my-max-target-q.txt",
            "hiddenapi/my-new-max-target-q.txt",
        ],
        max_target_p: ["hiddenapi/my-max-target-p.txt"],
        max_target_o_low_priority: ["hiddenapi/my-max-target-o-low-priority.txt"],
        blocked: ["hiddenapi/my-blocked.txt"],
        unsupported_packages: ["hiddenapi/my-unsupported-packages.txt"],
        annotation_flags: "hiddenapi/annotation-flags.csv",
        metadata: "hiddenapi/metadata.csv",
        index: "hiddenapi/index.csv",
        signature_patterns: "hiddenapi/signature-patterns.csv",
        filtered_stub_flags: "hiddenapi/filtered-stub-flags.csv",
        filtered_flags: "hiddenapi/filtered-flags.csv",
    },
}

java_import {
    name: "mybootlib",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["myapex"],
    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
    min_sdk_version: "1",
    permitted_packages: ["mybootlib"],
}

java_sdk_library_import {
    name: "mynewlibrary",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["myapex"],
    shared_library: true,
    compile_dex: true,
    permitted_packages: ["mysdklibrary"],
    public: {
        jars: ["sdk_library/public/mynewlibrary-stubs.jar"],
        stub_srcs: ["sdk_library/public/mynewlibrary_stub_sources"],
        current_api: "sdk_library/public/mynewlibrary.txt",
        removed_api: "sdk_library/public/mynewlibrary-removed.txt",
        sdk_version: "current",
    },
}

java_sdk_library_import {
    name: "mysdklibrary",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["//apex_available:platform"],
    shared_library: true,
    compile_dex: true,
    permitted_packages: ["mysdklibrary"],
    public: {
        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
        current_api: "sdk_library/public/mysdklibrary.txt",
        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
        sdk_version: "current",
    },
}
`),
		checkAllCopyRules(`
my-unsupported.txt -> hiddenapi/my-unsupported.txt
my-removed.txt -> hiddenapi/my-removed.txt
my-max-target-r-low-priority.txt -> hiddenapi/my-max-target-r-low-priority.txt
my-max-target-q.txt -> hiddenapi/my-max-target-q.txt
my-new-max-target-q.txt -> hiddenapi/my-new-max-target-q.txt
my-max-target-p.txt -> hiddenapi/my-max-target-p.txt
my-max-target-o-low-priority.txt -> hiddenapi/my-max-target-o-low-priority.txt
my-blocked.txt -> hiddenapi/my-blocked.txt
my-unsupported-packages.txt -> hiddenapi/my-unsupported-packages.txt
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
.intermediates/mynewlibrary.stubs.exportable/android_common/combined/mynewlibrary.stubs.exportable.jar -> sdk_library/public/mynewlibrary-stubs.jar
.intermediates/mynewlibrary.stubs.source/android_common/exportable/mynewlibrary.stubs.source_api.txt -> sdk_library/public/mynewlibrary.txt
.intermediates/mynewlibrary.stubs.source/android_common/exportable/mynewlibrary.stubs.source_removed.txt -> sdk_library/public/mynewlibrary-removed.txt
.intermediates/mysdklibrary.stubs.exportable/android_common/combined/mysdklibrary.stubs.exportable.jar -> sdk_library/public/mysdklibrary-stubs.jar
.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
`),
		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
		snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
	)
}

func testSnapshotWithBootClasspathFragment_MinSdkVersion(t *testing.T, targetBuildRelease string,
	expectedSdkSnapshot string,
	expectedCopyRules string,
	expectedStubFlagsInputs []string,
	suffix string) {

	result := android.GroupFixturePreparers(
		prepareForSdkTestWithJava,
		java.PrepareForTestWithJavaDefaultModules,
		java.PrepareForTestWithJavaSdkLibraryFiles,
		java.FixtureWithLastReleaseApis("mysdklibrary", "mynewsdklibrary"),
		java.FixtureConfigureApexBootJars("myapex:mysdklibrary", "myapex:mynewsdklibrary"),
		prepareForSdkTestWithApex,

		// Add a platform_bootclasspath that depends on the fragment.
		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),

		android.FixtureMergeEnv(map[string]string{
			"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": targetBuildRelease,
		}),
		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
			variables.Platform_version_active_codenames = []string{"VanillaIceCream"}
		}),

		android.PrepareForTestWithBuildFlag("RELEASE_HIDDEN_API_EXPORTABLE_STUBS", "true"),

		android.FixtureWithRootAndroidBp(`
			sdk {
				name: "mysdk",
				apexes: ["myapex"],
			}

			apex {
				name: "myapex",
				key: "myapex.key",
				min_sdk_version: "S",
				bootclasspath_fragments: ["mybootclasspathfragment"],
			}

			bootclasspath_fragment {
				name: "mybootclasspathfragment",
				apex_available: ["myapex"],
				contents: [
					"mysdklibrary",
					"mynewsdklibrary",
				],

				hidden_api: {
					split_packages: [],
				},
			}

			java_sdk_library {
				name: "mysdklibrary",
				apex_available: ["myapex"],
				srcs: ["Test.java"],
				shared_library: false,
				public: {enabled: true},
				min_sdk_version: "S",
				sdk_version: "current",
			}

			java_sdk_library {
				name: "mynewsdklibrary",
				apex_available: ["myapex"],
				srcs: ["Test.java"],
				compile_dex: true,
				public: {enabled: true},
				min_sdk_version: "Tiramisu",
				permitted_packages: ["mynewsdklibrary"],
				sdk_version: "current",
			}
		`),
	).RunTest(t)

	bcpf := result.ModuleForTests(t, "mybootclasspathfragment", "android_common")
	rule := bcpf.Output("out/soong/.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi" + suffix + "/stub-flags.csv")
	android.AssertPathsRelativeToTopEquals(t, "stub flags inputs", android.SortedUniqueStrings(expectedStubFlagsInputs), android.SortedUniquePaths(rule.Implicits))

	CheckSnapshot(t, result, "mysdk", "",
		checkAndroidBpContents(expectedSdkSnapshot),
		checkAllCopyRules(expectedCopyRules),
	)
}

func TestSnapshotWithBootClasspathFragment_MinSdkVersion(t *testing.T) {
	t.Parallel()
	t.Run("target S build", func(t *testing.T) {
		t.Parallel()
		expectedSnapshot := `
// This is auto-generated. DO NOT EDIT.

prebuilt_bootclasspath_fragment {
    name: "mybootclasspathfragment",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["myapex"],
    contents: ["mysdklibrary"],
    hidden_api: {
        annotation_flags: "hiddenapi/annotation-flags.csv",
        metadata: "hiddenapi/metadata.csv",
        index: "hiddenapi/index.csv",
        stub_flags: "hiddenapi/stub-flags.csv",
        all_flags: "hiddenapi/all-flags.csv",
    },
}

java_sdk_library_import {
    name: "mysdklibrary",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["myapex"],
    shared_library: false,
    public: {
        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
        current_api: "sdk_library/public/mysdklibrary.txt",
        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
        sdk_version: "current",
    },
}
`
		expectedCopyRules := `
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi-for-sdk-snapshot/annotation-flags.csv -> hiddenapi/annotation-flags.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi-for-sdk-snapshot/metadata.csv -> hiddenapi/metadata.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi-for-sdk-snapshot/index.csv -> hiddenapi/index.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi-for-sdk-snapshot/stub-flags.csv -> hiddenapi/stub-flags.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi-for-sdk-snapshot/all-flags.csv -> hiddenapi/all-flags.csv
.intermediates/mysdklibrary.stubs.exportable/android_common/combined/mysdklibrary.stubs.exportable.jar -> sdk_library/public/mysdklibrary-stubs.jar
.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
`

		// On S the stub flags should only be generated from mysdklibrary as mynewsdklibrary is not part
		// of the snapshot.
		expectedStubFlagsInputs := []string{
			"out/soong/.intermediates/mysdklibrary.stubs.exportable/android_common/dex/mysdklibrary.stubs.exportable.jar",
			"out/soong/.intermediates/mysdklibrary.impl/android_common/aligned/mysdklibrary.jar",
		}

		testSnapshotWithBootClasspathFragment_MinSdkVersion(t, "S",
			expectedSnapshot, expectedCopyRules, expectedStubFlagsInputs, "-for-sdk-snapshot")
	})

	t.Run("target-Tiramisu-build", func(t *testing.T) {
		t.Parallel()
		expectedSnapshot := `
// This is auto-generated. DO NOT EDIT.

prebuilt_bootclasspath_fragment {
    name: "mybootclasspathfragment",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["myapex"],
    contents: [
        "mysdklibrary",
        "mynewsdklibrary",
    ],
    hidden_api: {
        annotation_flags: "hiddenapi/annotation-flags.csv",
        metadata: "hiddenapi/metadata.csv",
        index: "hiddenapi/index.csv",
        signature_patterns: "hiddenapi/signature-patterns.csv",
        filtered_stub_flags: "hiddenapi/filtered-stub-flags.csv",
        filtered_flags: "hiddenapi/filtered-flags.csv",
    },
}

java_sdk_library_import {
    name: "mysdklibrary",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["myapex"],
    shared_library: false,
    public: {
        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
        current_api: "sdk_library/public/mysdklibrary.txt",
        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
        sdk_version: "current",
    },
}

java_sdk_library_import {
    name: "mynewsdklibrary",
    prefer: false,
    visibility: ["//visibility:public"],
    apex_available: ["myapex"],
    shared_library: true,
    compile_dex: true,
    permitted_packages: ["mynewsdklibrary"],
    public: {
        jars: ["sdk_library/public/mynewsdklibrary-stubs.jar"],
        stub_srcs: ["sdk_library/public/mynewsdklibrary_stub_sources"],
        current_api: "sdk_library/public/mynewsdklibrary.txt",
        removed_api: "sdk_library/public/mynewsdklibrary-removed.txt",
        sdk_version: "current",
    },
}
`
		expectedCopyRules := `
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/index.csv -> hiddenapi/index.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
.intermediates/mysdklibrary.stubs.exportable/android_common/combined/mysdklibrary.stubs.exportable.jar -> sdk_library/public/mysdklibrary-stubs.jar
.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
.intermediates/mysdklibrary.stubs.source/android_common/exportable/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
.intermediates/mynewsdklibrary.stubs.exportable/android_common/combined/mynewsdklibrary.stubs.exportable.jar -> sdk_library/public/mynewsdklibrary-stubs.jar
.intermediates/mynewsdklibrary.stubs.source/android_common/exportable/mynewsdklibrary.stubs.source_api.txt -> sdk_library/public/mynewsdklibrary.txt
.intermediates/mynewsdklibrary.stubs.source/android_common/exportable/mynewsdklibrary.stubs.source_removed.txt -> sdk_library/public/mynewsdklibrary-removed.txt
`

		// On tiramisu the stub flags should be generated from both mynewsdklibrary and mysdklibrary as
		// they are both part of the snapshot.
		expectedStubFlagsInputs := []string{
			"out/soong/.intermediates/mynewsdklibrary.stubs.exportable/android_common/dex/mynewsdklibrary.stubs.exportable.jar",
			"out/soong/.intermediates/mynewsdklibrary.impl/android_common/aligned/mynewsdklibrary.jar",
			"out/soong/.intermediates/mysdklibrary.stubs.exportable/android_common/dex/mysdklibrary.stubs.exportable.jar",
			"out/soong/.intermediates/mysdklibrary.impl/android_common/aligned/mysdklibrary.jar",
		}

		testSnapshotWithBootClasspathFragment_MinSdkVersion(t, "Tiramisu",
			expectedSnapshot, expectedCopyRules, expectedStubFlagsInputs, "")
	})
}

func TestSnapshotWithEmptyBootClasspathFragment(t *testing.T) {
	t.Parallel()
	result := android.GroupFixturePreparers(
		prepareForSdkTestWithJava,
		java.PrepareForTestWithJavaDefaultModules,
		java.PrepareForTestWithJavaSdkLibraryFiles,
		java.FixtureWithLastReleaseApis("mysdklibrary", "mynewsdklibrary"),
		java.FixtureConfigureApexBootJars("myapex:mysdklibrary", "myapex:mynewsdklibrary"),
		prepareForSdkTestWithApex,
		// Add a platform_bootclasspath that depends on the fragment.
		fixtureAddPlatformBootclasspathForBootclasspathFragment("myapex", "mybootclasspathfragment"),
		android.FixtureMergeEnv(map[string]string{
			"SOONG_SDK_SNAPSHOT_TARGET_BUILD_RELEASE": "S",
		}),
		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
			variables.Platform_version_active_codenames = []string{"VanillaIceCream"}
		}),
		android.FixtureWithRootAndroidBp(`
			sdk {
				name: "mysdk",
				apexes: ["myapex"],
			}
			apex {
				name: "myapex",
				key: "myapex.key",
				min_sdk_version: "S",
				bootclasspath_fragments: ["mybootclasspathfragment"],
			}
			bootclasspath_fragment {
				name: "mybootclasspathfragment",
				apex_available: ["myapex"],
				contents: ["mysdklibrary", "mynewsdklibrary"],
				hidden_api: {
					split_packages: [],
				},
			}
			java_sdk_library {
				name: "mysdklibrary",
				apex_available: ["myapex"],
				srcs: ["Test.java"],
				shared_library: false,
				public: {enabled: true},
				min_sdk_version: "Tiramisu",
				sdk_version: "current",
			}
			java_sdk_library {
				name: "mynewsdklibrary",
				apex_available: ["myapex"],
				srcs: ["Test.java"],
				compile_dex: true,
				public: {enabled: true},
				min_sdk_version: "Tiramisu",
				permitted_packages: ["mynewsdklibrary"],
				sdk_version: "current",
			}
		`),
	).RunTest(t)

	CheckSnapshot(t, result, "mysdk", "", checkAndroidBpContents(`// This is auto-generated. DO NOT EDIT.`))
}
