From ccbf7f234cc5eed0a62d0429b8dfe9e2a1df7d2a Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Fri, 15 May 2020 23:12:17 +0200 Subject: [PATCH] Configure entire project APKs for additionalTestApks. This adds a new plugin called `com.osacky.fulladle` which is to be applied at the root of the project. It scans the project's submodules and adds all the apks and test apks to additionalTestApks. References #96 --- README.md | 2 +- build.gradle | 7 ++ buildSrc/build.gradle.kts | 77 ++++++++++-------- .../com/osacky/flank/gradle/FulladlePlugin.kt | 78 +++++++++++++++++++ .../com.osacky.fulladle.properties | 1 + .../FulladlePluginIntegrationTest.kt | 32 ++++++++ docs/changelog.md | 3 +- docs/configuration.md | 26 ++++++- docs/faq.md | 2 +- docs/multi-module-testing.md | 70 +++++++++++++++++ docs/quick-start.md | 14 +++- mkdocs.yml | 2 + sample-android-library/build.gradle.kts | 28 +++++++ .../gradle/sample/ExampleInstrumentedTest.kt | 20 +++++ .../src/main/AndroidManifest.xml | 2 + .../flank/gradle/sample/MainActivity.kt | 11 +++ settings.gradle | 1 + 17 files changed, 336 insertions(+), 40 deletions(-) create mode 100644 buildSrc/src/main/java/com/osacky/flank/gradle/FulladlePlugin.kt create mode 100644 buildSrc/src/main/resources/META-INF/gradle-plugins/com.osacky.fulladle.properties create mode 100644 buildSrc/src/test/java/com/osacky/flank/gradle/integration/FulladlePluginIntegrationTest.kt create mode 100644 docs/multi-module-testing.md create mode 100644 sample-android-library/build.gradle.kts create mode 100644 sample-android-library/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt create mode 100644 sample-android-library/src/main/AndroidManifest.xml create mode 100644 sample-android-library/src/main/java/com/osacky/flank/gradle/sample/MainActivity.kt diff --git a/README.md b/README.md index 47c6df94..c9370ec8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Fladle -The Gradle Plugin for Firebase Test Lab and Flank +Easily scale your Android Instrumentation tests on Firebase Test Lab with Flank. ### Documentation is at [runningcode.github.io/fladle](https://runningcode.github.io/fladle) diff --git a/build.gradle b/build.gradle index 12d694ca..3c9b2c37 100644 --- a/build.gradle +++ b/build.gradle @@ -14,15 +14,22 @@ buildscript { plugins { id "com.github.ben-manes.versions" version "0.28.0" + id "com.osacky.fulladle" } allprojects { repositories { google() mavenCentral() + // jcenter is required for "org.jetbrains.trove4j:trove4j:20160824." + jcenter() } } +fladle { + serviceAccountCredentials = project.layout.projectDirectory.file("sample/flank-gradle-5cf02dc90531.json") +} + task clean(type: Delete) { delete rootProject.buildDir } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 2caed09e..f0a94c3f 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,5 +1,6 @@ group = "com.osacky.flank.gradle" -version = "0.9.5-SNAPSHOT" +version = "0.10.0-SNAPSHOT" +description = "Easily Scale your Android Instrumentation Tests with Firebase Test Lab with Flank" repositories { google() @@ -52,9 +53,15 @@ gradlePlugin { create("fladle") { id = "com.osacky.fladle" displayName = "Fladle" - description = "The Gradle Plugin for Flank" + description = project.description implementationClass = "com.osacky.flank.gradle.FlankGradlePlugin" } + create("fulladle") { + id = "com.osacky.fulladle" + displayName = "Fulladle" + description = project.description + implementationClass = "com.osacky.flank.gradle.FulladlePlugin" + } } } @@ -92,19 +99,23 @@ publishing { } } publications { - afterEvaluate { - named("fladlePluginMarkerMaven") { - signing.sign(this) - pom.configureForFladle() - } - - named("pluginMaven") { - artifact(tasks["sourcesJar"]) - artifact(tasks["javadocJar"]) - signing.sign(this) - pom.configureForFladle() - } + afterEvaluate { + named("fladlePluginMarkerMaven") { + signing.sign(this) + pom.configureForFladle("Fladle") + } + + named("pluginMaven") { + artifact(tasks["sourcesJar"]) + artifact(tasks["javadocJar"]) + signing.sign(this) + pom.configureForFladle("Fladle") + } + named("fulladlePluginMarkerMaven") { + signing.sign(this) + pom.configureForFladle("Fulladle") } + } } } @@ -112,25 +123,25 @@ signing { isRequired = isReleaseBuild } -fun org.gradle.api.publish.maven.MavenPom.configureForFladle() { - name.set("Fladle") - description.set("The Gradle Plugin for Flank") - url.set("https://github.com/runningcode/fladle") - licenses { - license { - name.set("The Apache License, Version 2.0") - url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") - } +fun org.gradle.api.publish.maven.MavenPom.configureForFladle(pluginName: String) { + name.set(pluginName) + description.set(project.description) + url.set("https://github.com/runningcode/fladle") + licenses { + license { + name.set("The Apache License, Version 2.0") + url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") } - developers { - developer { - id.set("runningcode") - name.set("Nelson Osacky") - } - } - scm { - connection.set("scm:git:git://github.com/runningcode/fladle.git") - developerConnection.set("scm:git:ssh://github.com/runningcode/fladle.git") - url.set("https://github.com/runningcode/fladle") + } + developers { + developer { + id.set("runningcode") + name.set("Nelson Osacky") } + } + scm { + connection.set("scm:git:git://github.com/runningcode/fladle.git") + developerConnection.set("scm:git:ssh://github.com/runningcode/fladle.git") + url.set("https://github.com/runningcode/fladle") + } } diff --git a/buildSrc/src/main/java/com/osacky/flank/gradle/FulladlePlugin.kt b/buildSrc/src/main/java/com/osacky/flank/gradle/FulladlePlugin.kt new file mode 100644 index 00000000..6af84621 --- /dev/null +++ b/buildSrc/src/main/java/com/osacky/flank/gradle/FulladlePlugin.kt @@ -0,0 +1,78 @@ +package com.osacky.flank.gradle + +import com.android.build.gradle.AppExtension +import com.android.build.gradle.LibraryExtension +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.getByType + +/** + * Like the Fladle plugin, but it configures additionalTestApks for the _full_ project. Hence fulladle. + */ +class FulladlePlugin : Plugin { + override fun apply(root: Project) { + println(""" + Warning: Fulladle is still in development. It is very likely not to work. + * Report bugs to the Firebase Community slack in #flank or as an issue in the Fladle project. + * Include the output from the printYml task. + """.trimIndent()) + + check(root.parent == null) { "Fulladle must be applied in the root project in order to configure subprojects." } + FladlePluginDelegate().apply(root) + + val flankGradleExtension = root.extensions.getByType(FlankGradleExtension::class) + + root.subprojects { + pluginManager.withPlugin("com.android.application") { + val appExtension = extensions.getByType() + // Only configure the first test variant per module. + // Does anyone test more than one variant per module? + var addedTestsForModule = false + + // TODO deal with ignored/filtered variants + appExtension.testVariants.configureEach testVariant@{ + if (addedTestsForModule) { + return@testVariant + } + val appVariant = testedVariant + appVariant.outputs.configureEach app@{ + + this@testVariant.outputs.configureEach test@{ + // TODO is this racy? + // If the debugApk isn't yet set, let's use this one. + if (!flankGradleExtension.debugApk.isPresent) { + flankGradleExtension.debugApk.set(root.provider { this@app.outputFile.absolutePath }) + } else { + // Otherwise, let's just add it to the list. + flankGradleExtension.additionalTestApks.add(root.provider { + "- app: ${this@app.outputFile}" + }) + } + // If the instrumentation apk isn't yet set, let's use this one. + if (!flankGradleExtension.instrumentationApk.isPresent) { + flankGradleExtension.instrumentationApk.set(root.provider { this@test.outputFile.absolutePath }) + } else { + // Otherwise, let's just add it to the list. + flankGradleExtension.additionalTestApks.add(root.provider { + " test: ${this@test.outputFile}" + }) + } + addedTestsForModule = true + return@test + } + } + } + } + pluginManager.withPlugin("com.android.library") { + val library = extensions.getByType() + library.testVariants.configureEach { + outputs.configureEach { + flankGradleExtension.additionalTestApks.add(root.provider { + "- test: $outputFile" + }) + } + } + } + } + } +} diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/com.osacky.fulladle.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/com.osacky.fulladle.properties new file mode 100644 index 00000000..5b4d2a1f --- /dev/null +++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/com.osacky.fulladle.properties @@ -0,0 +1 @@ +implementation-class=com.osacky.flank.gradle.FulladlePlugin diff --git a/buildSrc/src/test/java/com/osacky/flank/gradle/integration/FulladlePluginIntegrationTest.kt b/buildSrc/src/test/java/com/osacky/flank/gradle/integration/FulladlePluginIntegrationTest.kt new file mode 100644 index 00000000..3fca5710 --- /dev/null +++ b/buildSrc/src/test/java/com/osacky/flank/gradle/integration/FulladlePluginIntegrationTest.kt @@ -0,0 +1,32 @@ +package com.osacky.flank.gradle.integration + +import com.google.common.truth.Truth.assertThat +import org.gradle.testkit.runner.GradleRunner +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder + +class FulladlePluginIntegrationTest { + @get:Rule + var testProjectRoot = TemporaryFolder() + + fun writeBuildGradle(build: String) { + val file = testProjectRoot.newFile("build.gradle") + file.writeText(build) + } + + @Test + fun fladleSmokeTest() { + writeBuildGradle( + """plugins { + | id "com.osacky.fulladle" + |}""".trimMargin() + ) + val result = GradleRunner.create() + .withProjectDir(testProjectRoot.root) + .withPluginClasspath() + .withGradleVersion("6.0") + .build() + assertThat(result.output).contains("SUCCESS") + } +} diff --git a/docs/changelog.md b/docs/changelog.md index 81616c52..8eebabb6 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,10 +1,11 @@ # Changelog -## 0.9.5 +## 0.10.0 * Allow for debugging using [--dump-shards](/fladle/faq/#debugging) * Fix naming for variant discovery of apk and instrumentation apk. Instead of chocolate-debug, variant must now be set as chocolateDebug. * Update [Flank to 20.05.2](https://github.com/Flank/flank/releases/tag/v20.05.2). +* [Fulladle Preview](/fladle/multi-module-testing) !!! Warning "Breaking API Change" [additionalTestApks](/fladle/configuration/#additionaltestapks) now uses ListProperty instead of the previous Map. This is to allow for lazy configuration of the provided files. diff --git a/docs/configuration.md b/docs/configuration.md index e94a0692..00532483 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -62,7 +62,7 @@ fladle { [ "model": "Nexus5", "version": "23" ] ] projectId("flank-gradle") - flankVersion = "20.05.2" + flankVersion = "{{ fladle.flank_version }}" debugApk = "$buildDir/outputs/apk/debug/sample-debug.apk" instrumentationApk = "$buildDir/outputs/apk/androidTest/debug/sample-debug-androidTest.apk" additionalTestApks = [ @@ -128,9 +128,29 @@ The projectId is a unique identifier which can be found in the project's URL: `h This is automatically discovered based on the service credential by default. ### flankVersion -`flankVersion = "flank_snapshot"` to specify a Flank snapshot. +Need a different Flank version? Specify it with `flankVersion`. -`flankVersion = "20.05.2"` to specify a specific Flank version. +To use a snapshot: +=== "Groovy" + ``` groovy + flankVersion = "flank_snapshot"` + ``` +=== "Kotlin" + ``` kotlin + flankVersion.set("flank_snapshot") + ``` + +Need more than 50 shards? Use Flank `8.1.0`. + +To use a different version: +=== "Groovy" + ``` groovy + flankVersion = "{{ fladle.flank_version }}" + ``` +=== "Kotlin" + ``` kotlin + flankVersion.set("{{ fladle.flank_version }}") + ``` ### flankCoordinates `flankCoordinates = "com.github.flank:flank"` to specify custom flank coordinates. diff --git a/docs/faq.md b/docs/faq.md index b965bc81..39c2e6d0 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -22,7 +22,7 @@ No signature of method: flank_4vvjv7w3oopge32w1tl9cs6e4.fladle() is applicable f Possible solutions: file(java.lang.Object), find(), findAll(), file(java.lang.Object, org.gradle.api.PathValidation), files([Ljava.lang.Object;), findAll(groovy.lang.Closure) ``` -If you receive a similar error, please check [configuration](/configuration#sample-configuration) for a sample configuration. +If you receive a similar error, please check [configuration](configuration/#sample-configuration) for a sample configuration. ## Debugging `./gradlew runFlank -PdumpShards` Will dump shards and exit the process without running the tests. diff --git a/docs/multi-module-testing.md b/docs/multi-module-testing.md new file mode 100644 index 00000000..29f0dcad --- /dev/null +++ b/docs/multi-module-testing.md @@ -0,0 +1,70 @@ +# Multi-module testing + +Multi module testing can be done by manually specifying [additionalTestApks](/fladle/configuration/#additionaltestapks) or applying the Fulladle plugin to automacally gather all the additional test apks. + +## Fulladle Plugin + +!!! Warning + Fulladle is still under development and is not guaranteed to work and may change at any moment. + +1. Apply the Fulladle plugin at the root of the project. + + === "Groovy" + ``` groovy + plugins { + id 'com.osacky.fulladle' version '{{ fladle.current_release }}' + } + ``` + === "Kotlin" + ``` kotlin + plugins { + id 'com.osacky.fulladle' version '{{ fladle.current_release }}' + } + ``` + +2. Configure the Fladle extension. + + ===! "Groovy" + ``` groovy + fladle { + serviceAccountCredentials = project.layout.projectDirectory.file("flank-gradle-service-account.json") + } + ``` + === "Kotlin" + ``` kotlin + fladle { + serviceAccountCredentials.set(project.layout.projectDirectory.file("flank-gradle-service-account.json")) + } + ``` + + !!! Warning + If using buildFlavors or testing against a non default variant, Fulladle might not test the variant you are expecting. + +3. Run the tests. + First assemble all your debug apks and test apks. + ``` bash + ./gradlew assembleDebug assembleDebugAndroidTest + ``` + + !!! note + When using flavors, make sure to assemble your buildVariants as well. + + `./gradlew :app:assembleFreeDebug :app:assembleFreeDebugAndroidTest` + + Run Flank! + ``` bash + ./gradlew runFlank + ``` + + +## Troubleshooting +Fulladle isn't ready yet, but we'd love feedback. Please join us in the [Firebase Community Slack](https://firebase.community/) with any feedback you may have. +You can also file [Fladle Github issues](https://github.com/runningcode/fladle/issues). + +When filing a bug report, please include the Flank version number, the Fladle version number and the output of the following: + +`./gradlew printYml` + +`./gradlew runFlank -PdumpShards` + + diff --git a/docs/quick-start.md b/docs/quick-start.md index 2e8081ac..06e41d3b 100644 --- a/docs/quick-start.md +++ b/docs/quick-start.md @@ -57,7 +57,19 @@ Using Fladle takes 3 steps: !!! Warning If using buildFlavors or testing against a non default variant, [variant must also configured](/fladle/configuration#variant) -3. Run the Flank Gradle task. +3. Run your tests! + + First assemble your debug apk and test apk. + ``` bash + ./gradlew :app:assembleDebug :app:assembleDebugAndroidTest + ``` + + !!! note + When using flavors, make sure to assemble your buildVariants. + + `./gradlew :app:assembleFreeDebug :app:assembleFreeDebugAndroidTest` + + Run Flank! ``` bash ./gradlew runFlank ``` diff --git a/mkdocs.yml b/mkdocs.yml index 330e4d39..4aa9be77 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -6,6 +6,7 @@ extra: fladle: current_release: '0.9.4' next_release: '0.9.5' + flank_version: '20.05.2' site_name: Fladle site_url: https://runningcode.github.io/fladle/ @@ -20,6 +21,7 @@ nav: - Configuration: configuration.md - Changelog: changelog.md - Results: results.md + - Multi Module Testing: multi-module-testing.md - FAQ: faq.md - Testing Snapshots: snapshots.md - Releasing: releasing.md diff --git a/sample-android-library/build.gradle.kts b/sample-android-library/build.gradle.kts new file mode 100644 index 00000000..6cb3e50b --- /dev/null +++ b/sample-android-library/build.gradle.kts @@ -0,0 +1,28 @@ +plugins { + id("com.android.library") + kotlin("android") +} + +android { + compileSdkVersion(28) + defaultConfig { + minSdkVersion(23) + targetSdkVersion(28) + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + testOptions { + execution = "ANDROIDX_TEST_ORCHESTRATOR" + } +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7") + implementation("androidx.appcompat:appcompat:1.1.0") + implementation("android.arch.navigation:navigation-fragment-ktx:1.0.0") + implementation("androidx.constraintlayout:constraintlayout:1.1.3") + testImplementation("junit:junit:4.13") + androidTestImplementation("androidx.test:runner:1.2.0") + androidTestImplementation("androidx.test:rules:1.2.0") + androidTestImplementation("androidx.test.espresso:espresso-core:3.2.0") +} + diff --git a/sample-android-library/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt b/sample-android-library/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..0cdcf28f --- /dev/null +++ b/sample-android-library/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt @@ -0,0 +1,20 @@ +package com.osacky.flank.gradle.sample + + +import androidx.test.runner.AndroidJUnit4 +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + + @Test + fun seeView() { + assert(true) + } + + @Test + fun runAndFail() { + throw RuntimeException("Test failed") + } +} diff --git a/sample-android-library/src/main/AndroidManifest.xml b/sample-android-library/src/main/AndroidManifest.xml new file mode 100644 index 00000000..8487ce01 --- /dev/null +++ b/sample-android-library/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/sample-android-library/src/main/java/com/osacky/flank/gradle/sample/MainActivity.kt b/sample-android-library/src/main/java/com/osacky/flank/gradle/sample/MainActivity.kt new file mode 100644 index 00000000..c3c528be --- /dev/null +++ b/sample-android-library/src/main/java/com/osacky/flank/gradle/sample/MainActivity.kt @@ -0,0 +1,11 @@ +package com.osacky.flank.gradle.sample + +import androidx.appcompat.app.AppCompatActivity +import android.os.Bundle + +class MainActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } +} diff --git a/settings.gradle b/settings.gradle index 9020fedc..3991f993 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,3 +5,4 @@ plugins { include ':sample' include ':sample-kotlin' include ':sample-flavors-kotlin' +include ':sample-android-library'