-
Notifications
You must be signed in to change notification settings - Fork 0
Update AGP and dependencies #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| #Fri Mar 22 10:54:28 CET 2024 | ||
| distributionBase=GRADLE_USER_HOME | ||
| distributionPath=wrapper/dists | ||
| distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip | ||
| distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip | ||
| zipStoreBase=GRADLE_USER_HOME | ||
| zipStorePath=wrapper/dists |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,29 @@ | ||
| package com.telefonica.androidsnaptesting | ||
|
|
||
| import com.android.build.gradle.TestedExtension | ||
| import com.android.build.api.variant.AndroidComponentsExtension | ||
| import com.android.build.api.variant.ApplicationAndroidComponentsExtension | ||
| import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask | ||
| import org.gradle.api.Plugin | ||
| import org.gradle.api.Project | ||
| import org.gradle.api.Task | ||
| import org.gradle.api.file.Directory | ||
| import org.gradle.api.provider.Provider | ||
| import org.gradle.api.provider.ProviderFactory | ||
| import java.io.File | ||
|
|
||
| class AndroidSnaptestingPlugin : Plugin<Project> { | ||
|
|
||
| override fun apply(project: Project) { | ||
| // Collect applicationId per test-variant name at configuration time using the new variant API. | ||
| // onVariants runs during project configuration, before afterEvaluate. | ||
| val applicationIds = mutableMapOf<String, Provider<String>>() | ||
|
|
||
| project.extensions.findByType(ApplicationAndroidComponentsExtension::class.java) | ||
| ?.onVariants { variant -> | ||
| // variant.name == "debug" β test task variant name == "debugAndroidTest" | ||
| applicationIds["${variant.name}AndroidTest"] = variant.applicationId | ||
| } | ||
|
|
||
| project.afterEvaluate { | ||
|
|
||
| val deviceProviderInstrumentTestTasks = project.tasks | ||
|
|
@@ -21,8 +33,8 @@ class AndroidSnaptestingPlugin : Plugin<Project> { | |
| throw AndroidSnaptestingNoDeviceProviderInstrumentTestTasksException() | ||
| } | ||
|
|
||
| val extension = project.extensions.findByType(TestedExtension::class.java) | ||
| ?: throw RuntimeException("TestedExtension not found") | ||
| val androidComponents = project.extensions.findByType(AndroidComponentsExtension::class.java) | ||
| ?: throw RuntimeException("AndroidComponentsExtension not found") | ||
|
|
||
| val isRecordMode = project.properties["android.testInstrumentationRunnerArguments.record"] == "true" | ||
| val providerFactory: ProviderFactory = project.providers | ||
|
|
@@ -32,7 +44,18 @@ class AndroidSnaptestingPlugin : Plugin<Project> { | |
| taskName, | ||
| DeviceProviderInstrumentTestTask::class.java, | ||
| ).get() | ||
| registerTasksForVariant(project, taskName, deviceProviderTask, extension, isRecordMode, providerFactory) | ||
| val variantName = deviceProviderTask.variantName | ||
| val applicationIdProvider = applicationIds[variantName] | ||
| ?: throw RuntimeException( | ||
| "applicationId not found for test variant '$variantName'. " + | ||
| "Available variants: ${applicationIds.keys}. " + | ||
| "Make sure the plugin is applied to a com.android.application module." | ||
| ) | ||
| registerTasksForVariant( | ||
| project, taskName, deviceProviderTask, | ||
| androidComponents, applicationIdProvider, | ||
| isRecordMode, providerFactory, | ||
| ) | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -42,17 +65,13 @@ class AndroidSnaptestingPlugin : Plugin<Project> { | |
| project: Project, | ||
| taskName: String, | ||
| deviceProviderTask: DeviceProviderInstrumentTestTask, | ||
| extension: TestedExtension, | ||
| androidComponents: AndroidComponentsExtension<*, *, *>, | ||
| applicationIdProvider: Provider<String>, | ||
| isRecordMode: Boolean, | ||
| providerFactory: ProviderFactory, | ||
| ) { | ||
| val capitalizedVariant = deviceProviderTask.variantName.capitalizeFirstLetter() | ||
|
|
||
| val testedVariant = extension.testVariants | ||
| .firstOrNull { it.name == deviceProviderTask.variantName } | ||
| ?: throw RuntimeException("TestVariant not found for ${deviceProviderTask.variantName}") | ||
| val applicationIdProvider = providerFactory.provider { testedVariant.applicationId } | ||
| val adbExecutablePath = extension.adbExecutable.absolutePath | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. adbExecutable was removed from TestedExtension in AGP 9.x. |
||
| val adbExecutablePath = androidComponents.sdkComponents.adb.get().asFile.absolutePath | ||
|
|
||
| val goldenSnapshotsSourcePath = run { | ||
| val variantSourceFolder = deviceProviderTask | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,13 +2,10 @@ package com.telefonica.androidsnaptesting | |
|
|
||
| import com.android.build.gradle.internal.tasks.DeviceProviderInstrumentTestTask | ||
| import com.android.build.gradle.internal.testing.ConnectedDevice | ||
| import com.android.ddmlib.CollectingOutputReceiver | ||
| import com.android.ddmlib.FileListingService | ||
| import com.android.ddmlib.FileListingService.FileEntry | ||
| import com.android.ddmlib.IDevice | ||
| import org.gradle.api.file.RegularFile | ||
| import org.gradle.api.provider.ProviderFactory | ||
| import java.io.File | ||
| import java.util.concurrent.TimeUnit | ||
|
|
||
| fun DeviceProviderInstrumentTestTask.deviceFileManager( | ||
| applicationId: String, | ||
|
|
@@ -23,39 +20,25 @@ class DeviceFileManager( | |
| private val providerFactory: ProviderFactory, | ||
| ) { | ||
|
|
||
| fun pullRecordedSnapshots( | ||
| destinationPath: String, | ||
| ) { | ||
| fun pullRecordedSnapshots(destinationPath: String) { | ||
| pullSnapshots("recorded", destinationPath) | ||
| } | ||
|
|
||
| fun pullFailuresSnapshots( | ||
| destinationPath: String, | ||
| ) { | ||
| fun pullFailuresSnapshots(destinationPath: String) { | ||
| pullSnapshots("failures", destinationPath) | ||
| } | ||
|
|
||
| fun clearAllSnapshots() { | ||
| withConnectedDevices { devices -> | ||
| devices.forEach { | ||
| val receiver = CollectingOutputReceiver() | ||
| it.iDevice.executeShellCommand("rm -rf ${getDeviceAndroidSnaptestingRootAbsolutePath()}", receiver) | ||
| println(receiver.output) | ||
| devices.forEach { device -> | ||
| runAdb(device.serialNumber, "shell", "rm", "-rf", getDeviceAndroidSnaptestingRootAbsolutePath()) | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously used DDMLib's IDevice.executeShellCommand() to run 'rm -rf' on the device. Now delegates to runAdb(), which spawns an 'adb -s shell rm -rf ...' subprocess. |
||
| } | ||
| } | ||
| } | ||
|
|
||
| private fun String.toFileEntry(): FileEntry { | ||
| val parts = this.split("/") | ||
| var fileEntry = FileEntry(null, null, FileListingService.TYPE_DIRECTORY, true) | ||
| parts.forEach { | ||
| fileEntry = FileEntry(fileEntry, it, FileListingService.TYPE_DIRECTORY, false) | ||
| } | ||
| return fileEntry | ||
| } | ||
|
|
||
| private fun getDeviceAndroidSnaptestingRootAbsolutePath(): String = | ||
| "${FileListingService.DIRECTORY_SDCARD}/Download/android-snaptesting/$applicationId" | ||
| "/sdcard/Download/android-snaptesting/$applicationId" | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FileListingService.DIRECTORY_SDCARD was a DDMLib constant ("/sdcard"). Using the literal string directly removes the DDMLib dependency entirely. |
||
|
|
||
| private fun getDeviceAndroidSnaptestingSubfolderAbsolutePath(subFolder: String): String = | ||
| "${getDeviceAndroidSnaptestingRootAbsolutePath()}/$subFolder" | ||
|
|
||
|
|
@@ -77,25 +60,40 @@ class DeviceFileManager( | |
| androidSnaptestingSubFolderInDevice: String, | ||
| destinationPath: String, | ||
| ) { | ||
| val fileEntry = getDeviceAndroidSnaptestingSubfolderAbsolutePath(androidSnaptestingSubFolderInDevice).toFileEntry() | ||
| val remotePath = getDeviceAndroidSnaptestingSubfolderAbsolutePath(androidSnaptestingSubFolderInDevice) | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The old implementation built a FileEntry tree using DDMLib's FileListingService and then called IDevice.pullFile() for each entry. |
||
| withConnectedDevices { devices -> | ||
| devices.forEach { | ||
| pullFolderFiles( | ||
| fileEntry, | ||
| it.iDevice, | ||
| destinationPath, | ||
| ) | ||
| devices.forEach { device -> | ||
| val serial = device.serialNumber | ||
| // List files in the remote folder; ignore errors if the folder doesn't exist yet | ||
| val lsOutput = runAdbCapture(serial, "shell", "ls", remotePath) | ||
| val fileNames = lsOutput.lines() | ||
| .map { it.trim() } | ||
| .filter { it.isNotBlank() && !it.startsWith("ls:") && !it.contains("No such file") } | ||
| // Pull each file to the local destination | ||
| fileNames.forEach { fileName -> | ||
| runAdb(serial, "pull", "$remotePath/$fileName", "$destinationPath/$fileName") | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private fun pullFolderFiles( | ||
| androidSnaptestingDeviceFolder: FileEntry, | ||
| device: IDevice, | ||
| destinationPath: String, | ||
| ) { | ||
| device.fileListingService.getChildrenSync(androidSnaptestingDeviceFolder).forEach { | ||
| device.pullFile(it.fullPath, "$destinationPath/${it.name}") | ||
| private fun runAdb(serial: String, vararg args: String) { | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Convenience wrapper that additionally prints the output to the Gradle log. Both replace the DDMLib IDevice shell/pull APIs removed in AGP 9.x. |
||
| val output = runAdbCapture(serial, *args) | ||
| println(output) | ||
| } | ||
|
|
||
| private fun runAdbCapture(serial: String, vararg args: String): String { | ||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. runAdbCapture spawns a subprocess with the adb binary, targeting a specific device by serial (-s flag), merges stderr into stdout, and waits up to 60 seconds. |
||
| val command = buildList { | ||
| add(adbExecutablePath) | ||
| add("-s") | ||
| add(serial) | ||
| addAll(args.toList()) | ||
| } | ||
| val process = ProcessBuilder(command) | ||
| .redirectErrorStream(true) | ||
| .start() | ||
| val output = process.inputStream.bufferedReader().readText() | ||
| process.waitFor(60, TimeUnit.SECONDS) | ||
| return output | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AGP 9.x removed TestedExtension from the public API. We now use the stable
variant API (AndroidComponentsExtension / ApplicationAndroidComponentsExtension) which is the recommended way to interact with Android build variants since AGP 7.x.