IMPORTANT: The file content has been truncated. Status: Showing lines 150-350 of 557 total lines. Action: To read more of the file, you can use the 'start_line' and 'end_line' parameters in a subsequent 'read_file' call. For example, to read the next section of the file, use start_line: 351. --- FILE CONTENT (truncated) --- testedAbi = "x86_64" } } } } lint { abortOnError = true warningsAsErrors = false xmlReport = true htmlReport = true baseline = file("lint-baseline.xml") } } tasks.withType().configureEach { compilerOptions { jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) } } detekt { config.setFrom(rootProject.file("config/detekt/detekt.yml")) buildUponDefaultConfig = true autoCorrect = true } tasks.named("check") { dependsOn("lintDebug") } afterEvaluate { // Note: This reflective block and the use of afterEvaluate make the build incompatible with // the Configuration Cache (CC) for managed device test tasks. ManagedDeviceInstrumentationTestTask // cannot be serialized when configured reflectively in afterEvaluate. We accept this trade-off // to enable per-device instrumentation arguments which AGP 8.x does not yet expose via a public DSL. tasks.withType().configureEach { val deviceDirName = when { name.contains("phonePixel6api34", ignoreCase = true) -> "phonePixel6api34" name.contains("tablet7Nexus7api34", ignoreCase = true) -> "tablet7Nexus7api34" name.contains("tablet10PixelTabletApi34", ignoreCase = true) -> "tablet10PixelTabletApi34" name.contains("pixel2api33", ignoreCase = true) -> "pixel2api33" else -> null } if (deviceDirName != null) { // Set the host-side directory where GMD will pull files from. getAdditionalTestOutputDir().set( layout.buildDirectory.dir("outputs/managed_device_android_test_additional_output/release/$deviceDirName"), ) } // AGP does not provide a public DSL to set per-ManagedVirtualDevice instrumentation arguments. // We use reflection to reach into the internal testData property and its instrumentationRunnerArguments map. val getTestDataMethod = javaClass.getMethod("getTestData") val testDataProperty = getTestDataMethod.invoke(this) as org.gradle.api.provider.Property<*> val testData = testDataProperty.get() val getArgsMethod = testData.javaClass.getMethod("getInstrumentationRunnerArguments") @Suppress("UNCHECKED_CAST") val argsProperty = getArgsMethod.invoke(testData) as org.gradle.api.provider.MapProperty // Use set(provider) to ensure that the arguments are evaluated specifically for this task instance. // This prevents aliasing/merging issues when AGP shares the underlying property object across tasks. val taskName = name argsProperty.set( provider { val map = mutableMapOf() when { taskName.contains("phonePixel6api34", ignoreCase = true) -> { map["package"] = screenshotTestPackage map["additionalTestOutputDir"] = "/sdcard/test-outputs/screenshots" } taskName.contains("tablet7Nexus7api34", ignoreCase = true) -> { map["package"] = screenshotTestPackage map["additionalTestOutputDir"] = "/sdcard/test-outputs/screenshots" } taskName.contains("tablet10PixelTabletApi34", ignoreCase = true) -> { map["package"] = screenshotTestPackage map["additionalTestOutputDir"] = "/sdcard/test-outputs/screenshots" } taskName.contains("pixel2api33", ignoreCase = true) -> { map["notPackage"] = screenshotTestPackage map["additionalTestOutputDir"] = "/sdcard/test-outputs/smoke" } } map }, ) } } tasks.register("generateGmdCoverage") { group = "Verification" description = "Runs GMD tests and copies the coverage file to the baseline directory." dependsOn("pixel2api33ReleaseAndroidTest") from(layout.buildDirectory.dir("outputs/managed_device_code_coverage/release/pixel2api33")) { include("coverage.ec") } into(layout.projectDirectory.dir("coverage-baselines")) rename { "gmd_smoke.ec" } includeEmptyDirs = false duplicatesStrategy = DuplicatesStrategy.FAIL doLast { val destFile = layout.projectDirectory.file("coverage-baselines/gmd_smoke.ec").asFile if (!destFile.exists()) { throw GradleException("Failed to generate GMD coverage. Expected file not found: $destFile") } } } val storeScreenshotFiles = listOf( "screen_01_vehicle_list.png", "screen_02_vehicle_detail.png", "screen_03_add_record.png", "screen_04_categories.png", "screen_05_settings_privacy.png", "screen_06_paywall_dialog.png", ) val storeScreenshotsRoot = rootProject.layout.projectDirectory.dir("distribution/store-assets/screenshots") val storeAdditionalOutputRoots = listOf( layout.buildDirectory.dir("outputs/managed_device_android_test_additional_output/release"), ) fun registerStoreScreenshotCopy( taskPrefix: String, deviceTaskName: String, outputDirectory: String, previousCopyTask: TaskProvider? = null, ): TaskProvider { tasks.matching { it.name == deviceTaskName }.configureEach { // We inject a doFirst block to wipe the additional output root before every run. // This ensures a clean state for the subsequent copy task, but has the side-effect // of clearing previous outputs even when running a single device test for debugging. doFirst { delete(storeAdditionalOutputRoots) } } return tasks.register("copy${taskPrefix}StoreScreenshots") { previousCopyTask?.let { dependsOn(it) } dependsOn(tasks.matching { it.name == deviceTaskName }) into(storeScreenshotsRoot.dir(outputDirectory)) includeEmptyDirs = false duplicatesStrategy = DuplicatesStrategy.FAIL doFirst { delete(storeScreenshotsRoot.dir(outputDirectory)) } storeAdditionalOutputRoots.forEach { outputRoot -> from(outputRoot) { // AGP sometimes aliases instrumentation arguments across GMD tasks, // leading to screenshots being saved in the wrong bucket subfolder on the host. // We use a wildcard to grab any screenshot found in the device's output root. include("**/$outputDirectory/screen_*.png") include("**/screen_*.png") eachFile { relativePath = RelativePath(true, name) } } } doLast { val missing = storeScreenshotFiles.mapNotNull { fileName -> val file = storeScreenshotsRoot.file("$outputDirectory/$fileName").asFile if (file.exists()) null else file } if (missing.isNotEmpty()) { throw GradleException( "Missing generated store screenshots for $outputDirectory:\n" + missing.joinToString(separator = "\n") { "- ${it.path}" }, ) } } } } val copyPhoneStoreScreenshots = registerStoreScreenshotCopy( taskPrefix = "Phone", deviceTaskName = "phonePixel6api34ReleaseAndroidTest", outputDirectory = "phone", ) val copyTablet7StoreScreenshots = registerStoreScreenshotCopy( taskPrefix = "Tablet7", deviceTaskName = "tablet7Nexus7api34ReleaseAndroidTest", outputDirectory = "tablet-7", previousCopyTask = copyPhoneStoreScreenshots, ) val copyTablet10StoreScreenshots = registerStoreScreenshotCopy( taskPrefix = "Tablet10",