Repository: runningcode/fladle (kotlin)

Issue:
Change API for additionalTestApks
The current API for additional test apks uses a map
```
additionalTestApks  = [ 
          // null in order to use existing app apk. key order matters
          null : [ "foo/bar/room-test.apk" ],
         ("${rootProject.build}/foo/bar/sample-app.apk".toString()) : [ "foo/bar/sample-app-test.apk", "foo/bar/room-test-3.apk" ]
         ("${rootProject.build}/module/foo/app.apk".toString()) : ["orange/blue/test-apk.apk"],
         ("${findApkInApp(buildType)}".toString()) : [ "red/green/test2-apk"]
]
```
The issue with the map is the weird groovy syntax since gstrings can't be keys.
"${rootProject.build}/module/foo/app.apk" or "${findApkInApp(buildType)}" would normally only be evaluated when you call toString . But since this is a map, you need to resolve the string in order to create the map's key.
That means that the `findApkInApp` function needs to be evaluated at config time in order to create the map's key.

I propose changing the syntax to the following:

List option:
```
additionalTestApks  = [ 
          "test: foo/bar/room-test.apk",
          "app: ${rootProject.build}/foo/bar/sample-app.apk",
          "test: foo/bar/sample-app-test.apk",
          "test: foo/bar/room-test-3.apk",
          "app: ${rootProject.build}/module/foo/app.apk",
          "test: orange/blue/test-apk",
          "app: ${findApkInApp(buildType)}",
          "test: red/green/test2-apk"
]
```

Both examples should result in the same tests being run in the same order, but the second list option seems to have much cleaner syntax.

Pull request description:
Fixes #114

Patch:
diff --git a/buildSrc/src/main/java/com/osacky/flank/gradle/FladleConfig.kt b/buildSrc/src/main/java/com/osacky/flank/gradle/FladleConfig.kt
index c9e4b50e..d0539e83 100644
--- a/buildSrc/src/main/java/com/osacky/flank/gradle/FladleConfig.kt
+++ b/buildSrc/src/main/java/com/osacky/flank/gradle/FladleConfig.kt
@@ -1,6 +1,7 @@
 package com.osacky.flank.gradle
 
 import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.ListProperty
 import org.gradle.api.provider.Property
 
 interface FladleConfig {
@@ -55,7 +56,7 @@ interface FladleConfig {
    */
   var resultsDir: String?
 
-  var additionalTestApks: Map<String, List<String>>
+  var additionalTestApks: ListProperty<String>
 
   /**
    * The max time this test run can execute before it is cancelled (default: unlimited).
diff --git a/buildSrc/src/main/java/com/osacky/flank/gradle/FladleConfigImpl.kt b/buildSrc/src/main/java/com/osacky/flank/gradle/FladleConfigImpl.kt
index cfd50233..5f2a7963 100644
--- a/buildSrc/src/main/java/com/osacky/flank/gradle/FladleConfigImpl.kt
+++ b/buildSrc/src/main/java/com/osacky/flank/gradle/FladleConfigImpl.kt
@@ -1,6 +1,7 @@
 package com.osacky.flank.gradle
 
 import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.ListProperty
 import org.gradle.api.provider.Property
 
 data class FladleConfigImpl(
@@ -26,7 +27,7 @@ data class FladleConfigImpl(
   override var resultsBucket: String?,
   override var keepFilePath: Boolean,
   override var resultsDir: String?,
-  override var additionalTestApks: Map<String, List<String>>,
+  override var additionalTestApks: ListProperty<String>,
   override var runTimeout: Property<String>,
   override var ignoreFailedTests: Property<Boolean>,
   override var disableSharding: Boolean,
diff --git a/buildSrc/src/main/java/com/osacky/flank/gradle/FlankGradleExtension.kt b/buildSrc/src/main/java/com/osacky/flank/gradle/FlankGradleExtension.kt
index cceddaf5..113881a4 100644
--- a/buildSrc/src/main/java/com/osacky/flank/gradle/FlankGradleExtension.kt
+++ b/buildSrc/src/main/java/com/osacky/flank/gradle/FlankGradleExtension.kt
@@ -4,7 +4,9 @@ import groovy.lang.Closure
 import org.gradle.api.NamedDomainObjectContainer
 import org.gradle.api.Project
 import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.provider.ListProperty
 import org.gradle.api.provider.Property
+import org.gradle.kotlin.dsl.listProperty
 import org.gradle.kotlin.dsl.property
 
 open class FlankGradleExtension(project: Project) : FladleConfig {
@@ -58,7 +60,7 @@ open class FlankGradleExtension(project: Project) : FladleConfig {
 
   override var resultsDir: String? = null
 
-  override var additionalTestApks: Map<String, List<String>> = emptyMap()
+  override var additionalTestApks: ListProperty<String> = project.objects.listProperty()
 
   override var runTimeout: Property<String> = project.objects.property()
 
diff --git a/buildSrc/src/main/java/com/osacky/flank/gradle/FlankGradlePlugin.kt b/buildSrc/src/main/java/com/osacky/flank/gradle/FlankGradlePlugin.kt
index ec3426c2..3b72e50a 100644
--- a/buildSrc/src/main/java/com/osacky/flank/gradle/FlankGradlePlugin.kt
+++ b/buildSrc/src/main/java/com/osacky/flank/gradle/FlankGradlePlugin.kt
@@ -109,9 +109,6 @@ class FlankGradlePlugin : Plugin<Project> {
     }
     check(base.debugApk.isPresent) { "debugApk file must be specified ${base.debugApk.orNull}" }
     check(base.instrumentationApk.isPresent) { "instrumentationApk file must be specified ${base.instrumentationApk.orNull}" }
-    base.additionalTestApks.forEach {
-      check(it.value.isNotEmpty()) { "must provide at least one instrumentation apk for ${it.key}" }
-    }
   }
 
   private fun automaticallyConfigureTestOrchestrator(project: Project, extension: FlankGradleExtension, androidExtension: AppExtension) {
diff --git a/buildSrc/src/main/java/com/osacky/flank/gradle/YamlWriter.kt b/buildSrc/src/main/java/com/osacky/flank/gradle/YamlWriter.kt
index 89ee1245..a4797c0e 100644
--- a/buildSrc/src/main/java/com/osacky/flank/gradle/YamlWriter.kt
+++ b/buildSrc/src/main/java/com/osacky/flank/gradle/YamlWriter.kt
@@ -61,15 +61,12 @@ internal class YamlWriter {
         appendln("  - $file")
       }
     }
-    val testApks = config.additionalTestApks.flatMap { (debugApk, instrumentationApks) ->
-      instrumentationApks.map { debugApk to it }
-    }
+    val additionalTestApks = config.additionalTestApks.getOrElse(emptyList())
 
-    if (testApks.isNotEmpty()) {
+    if (additionalTestApks.isNotEmpty()) {
       appendln("  additional-app-test-apks:")
-      testApks.forEach {
-        appendln("    - app: ${it.first}")
-        appendln("      test: ${it.second}")
+      additionalTestApks.forEach {
+        appendln("    - $it")
       }
     }
 
diff --git a/docs/changelog.md b/docs/changelog.md
index 53ebccf7..2ffc2c1f 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -1,5 +1,12 @@
 # Changelog
 
+## 0.9.5
+
+* Allow for debugging using [--dump-shards](faq/#debugging)
+
+!!! Warning "Breaking API Change"
+    [additionalTestApks](/configuration/#additionalTestApks) now uses ListProperty instead of the previous Map. This is to allow for lazy configuration of the provided files.
+
 ## 0.9.4
 * Update [Flank to 20.05.1](https://github.com/Flank/flank/releases/tag/v20.05.0). Huge new release!
 * Add support for new flank flags. Thanks [pawelpasterz](https://github.com/pawelpasterz) [PR](https://github.com/runningcode/fladle/pull/88)
diff --git a/docs/configuration.md b/docs/configuration.md
index a0a09a86..128d388a 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -117,7 +117,31 @@ This is the path to the app's instrumentation apk. Supports wildcard characters.
 
 ### additionalTestApks
 Paths to additional test configurations.
-
+Order matters. A test apk is run with the nearest previous listed app apk.
+It is not required to list an app apk here. If there is no app apk listed in additionalTestApks, the test apks are run against the [#debugApk].
+
+=== "Groovy"
+    ```groovy
+    additionalTestApks.value(project.provider { [
+    "app: ../main/app/build/output/apk/debug/app.apk",
+    "test: ../main/app/build/output/apk/androidTest/debug/app-test.apk",
+    "app: ../sample/app/build/output/apk/debug/sample-app.apk",
+    "test: ../sample/app/build/output/apk/androidTest/debug/sample-app-test.apk",
+    "test: ../feature/room/build/output/apk/androidTest/debug/feature-room-test.apk",
+    "test: ../library/databases/build/output/apk/androidTest/debug/sample-databases-test.apk"
+    ]})
+    ```
+=== "Kotlin"
+    ``` kotlin
+    additionalTestApks.value(project.provider { listOf(
+    "app: ../main/app/build/output/apk/debug/app.apk",
+    "test: ../main/app/build/output/apk/androidTest/debug/app-test.apk",
+    "app: ../sample/app/build/output/apk/debug/sample-app.apk",
+    "test: ../sample/app/build/output/apk/androidTest/debug/sample-app-test.apk",
+    "test: ../feature/room/build/output/apk/androidTest/debug/feature-room-test.apk",
+    "test: ../library/databases/build/output/apk/androidTest/debug/sample-databases-test.apk"
+    )})
+    ```
 ### autoGoogleLogin
 Whether or not to automatically log in using a preconfigured google account. [More Info](https://cloud.google.com/sdk/gcloud/reference/firebase/test/android/run#--auto-google-login)
 
diff --git a/sample-kotlin/build.gradle.kts b/sample-kotlin/build.gradle.kts
index 5f516a36..54dcb7f6 100644
--- a/sample-kotlin/build.gradle.kts
+++ b/sample-kotlin/build.gradle.kts
@@ -42,6 +42,16 @@ fladle {
             )
             flakyTestAttempts = 3
         }
+        create("additionalTests") {
+            additionalTestApks.value(project.provider { listOf(
+                "app: ../main/app/build/output/apk/debug/app.apk",
+                "test: ../main/app/build/output/apk/androidTest/debug/app-test.apk",
+                "app: ../sample/app/build/output/apk/debug/sample-app.apk",
+                "test: ../sample/app/build/output/apk/androidTest/debug/sample-app-test.apk",
+                "test: ../feature/room/build/output/apk/androidTest/debug/feature-room-test.apk",
+                "test: ../library/databases/build/output/apk/androidTest/debug/sample-databases-test.apk"
+            )})
+        }
     }
     flakyTestAttempts = 1
 }
diff --git a/sample/build.gradle b/sample/build.gradle
index 253ca890..78bfddc5 100644
--- a/sample/build.gradle
+++ b/sample/build.gradle
@@ -42,6 +42,21 @@ fladle {
             ]
             flakyTestAttempts = 3
         }
+        additionalTests {
+            useOrchestrator = false
+            testTargets = [
+                "class com.osacky.flank.gradle.sample.ExampleInstrumentedTest#runAndFail"
+            ]
+            flakyTestAttempts = 3
+            additionalTestApks.value(project.provider { [
+                "app: ../main/app/build/output/apk/debug/app.apk",
+                "test: ../main/app/build/output/apk/androidTest/debug/app-test.apk",
+                "app: ../sample/app/build/output/apk/debug/sample-app.apk",
+                "test: ../sample/app/build/output/apk/androidTest/debug/sample-app-test.apk",
+                "test: ../feature/room/build/output/apk/androidTest/debug/feature-room-test.apk",
+                "test: ../library/databases/build/output/apk/androidTest/debug/sample-databases-test.apk"
+            ]})
+        }
     }
     flakyTestAttempts 1
 }


Test patch:
diff --git a/buildSrc/src/test/java/com/osacky/flank/gradle/YamlWriterTest.kt b/buildSrc/src/test/java/com/osacky/flank/gradle/YamlWriterTest.kt
index 03576174..59f1270f 100644
--- a/buildSrc/src/test/java/com/osacky/flank/gradle/YamlWriterTest.kt
+++ b/buildSrc/src/test/java/com/osacky/flank/gradle/YamlWriterTest.kt
@@ -1,9 +1,8 @@
 package com.osacky.flank.gradle
 
+import com.google.common.truth.Truth.assertThat
 import org.gradle.api.Project
 import org.gradle.testfixtures.ProjectBuilder
-import org.hamcrest.CoreMatchers.equalTo
-import org.hamcrest.MatcherAssert.assertThat
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
 import org.junit.Assert.fail
@@ -600,42 +599,51 @@ class YamlWriterTest {
       keepFilePath = true
     }
 
-    assertEquals("flank:\n" +
+    assertThat(yamlWriter.writeFlankProperties(extension))
+        .isEqualTo("flank:\n" +
             "  keep-file-path: true\n" +
             "  ignore-failed-tests: false\n" +
             "  disable-sharding: false\n" +
-            "  smart-flank-disable-upload: false\n",
-      yamlWriter.writeFlankProperties(extension))
+            "  smart-flank-disable-upload: false\n"
+      )
   }
 
-  @Test
-  fun writeAdditionalTestApks() {
-    val extension = emptyExtension {
-      debugApk.set("../orange/build/output/app.apk")
-      instrumentationApk.set("../orange/build/output/app-test.apk")
-      additionalTestApks = mapOf(
-        "../orange/build/output/app.apk" to listOf("../orange/build/output/app-test2.apk"),
-        "../bob/build/output/app.apk" to listOf("../bob/build/output/app-test.apk")
-      )
+    @Test
+    fun writeAdditionalTestApks() {
+        val extension = emptyExtension {
+            debugApk.set("../orange/build/output/app.apk")
+            instrumentationApk.set("../orange/build/output/app-test.apk")
+            additionalTestApks.set(project.provider {
+                listOf(
+                    "app: ../orange/build/output/app.apk",
+                    "test: ../orange/build/output/app-test2.apk",
+                    "app: ../bob/build/output/app.apk",
+                    "test: ../bob/build/output/app-test.apk",
+                    "test: ../bob/build/output/app-test2.apk",
+                    "test: ../bob/build/output/app-test3.apk"
+                )
+            })
+        }
+
+        assertThat(
+            yamlWriter.writeFlankProperties(extension)).isEqualTo(
+            """
+             flank:
+               keep-file-path: false
+               additional-app-test-apks:
+                 - app: ../orange/build/output/app.apk
+                 - test: ../orange/build/output/app-test2.apk
+                 - app: ../bob/build/output/app.apk
+                 - test: ../bob/build/output/app-test.apk
+                 - test: ../bob/build/output/app-test2.apk
+                 - test: ../bob/build/output/app-test3.apk
+               ignore-failed-tests: false
+               disable-sharding: false
+               smart-flank-disable-upload: false
+          """.trimIndent() + '\n'
+        )
     }
 
-    assertThat(
-      yamlWriter.writeFlankProperties(extension),
-      equalTo(
-"flank:\n" +
-        "  keep-file-path: false\n" +
-        "  additional-app-test-apks:\n" +
-        "    - app: ../orange/build/output/app.apk\n" +
-        "      test: ../orange/build/output/app-test2.apk\n" +
-        "    - app: ../bob/build/output/app.apk\n" +
-        "      test: ../bob/build/output/app-test.apk\n" +
-        "  ignore-failed-tests: false\n" +
-        "  disable-sharding: false\n" +
-        "  smart-flank-disable-upload: false\n"
-      )
-    )
-  }
-
   @Test
   fun verifyDefaultValues() {
     val defaultFlankProperties = FlankGradleExtension(project).toFlankProperties()