Move `generateAllEnumerations` to testutils-common
1. Moved `generateAllEnumerations` to testutils-common so that it can be used everywhere in AndroidX.
2. The function is reimplemented iteratively to prevent stack overflow.
3. @Parameterized.Parameters functions in Room were updated with this function when suitable.
Test: ParameterizedHelperTest
Change-Id: Ice737ec716210fc72f98df3f90604435c30773d2
diff --git a/room/benchmark/build.gradle b/room/benchmark/build.gradle
index b985390..c2f2aa9 100644
--- a/room/benchmark/build.gradle
+++ b/room/benchmark/build.gradle
@@ -37,4 +37,5 @@
androidTestImplementation(libs.testRunner)
androidTestImplementation(libs.testRules)
androidTestImplementation(libs.kotlinStdlib)
+ androidTestImplementation(project(":internal-testutils-common"))
}
diff --git a/room/benchmark/src/androidTest/java/androidx/room/benchmark/InvalidationTrackerBenchmark.kt b/room/benchmark/src/androidTest/java/androidx/room/benchmark/InvalidationTrackerBenchmark.kt
index 861da78..e3552e1 100644
--- a/room/benchmark/src/androidTest/java/androidx/room/benchmark/InvalidationTrackerBenchmark.kt
+++ b/room/benchmark/src/androidTest/java/androidx/room/benchmark/InvalidationTrackerBenchmark.kt
@@ -31,6 +31,7 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
+import androidx.testutils.generateAllEnumerations
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -103,19 +104,15 @@
companion object {
@JvmStatic
@Parameterized.Parameters(name = "sampleSize={0}, mode={1}")
- fun data(): List<Array<Any>> {
- return mutableListOf<Array<Any>>().apply {
- arrayOf(
+ fun data(): List<Array<Any>> =
+ generateAllEnumerations(
+ listOf(100, 1000, 5000, 10000),
+ listOf(
Mode.MEASURE_INSERT,
Mode.MEASURE_DELETE,
Mode.MEASURE_INSERT_AND_DELETE
- ).forEach { mode ->
- arrayOf(100, 1000, 5000, 10000).forEach { sampleSize ->
- add(arrayOf(sampleSize, mode))
- }
- }
- }
- }
+ )
+ )
private const val DB_NAME = "invalidation-benchmark-test"
}
diff --git a/room/benchmark/src/androidTest/java/androidx/room/benchmark/RelationBenchmark.kt b/room/benchmark/src/androidTest/java/androidx/room/benchmark/RelationBenchmark.kt
index 1000c14..d9ec441 100644
--- a/room/benchmark/src/androidTest/java/androidx/room/benchmark/RelationBenchmark.kt
+++ b/room/benchmark/src/androidTest/java/androidx/room/benchmark/RelationBenchmark.kt
@@ -32,6 +32,7 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.filters.LargeTest
import androidx.test.filters.SdkSuppress
+import androidx.testutils.generateAllEnumerations
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -89,11 +90,7 @@
companion object {
@JvmStatic
@Parameterized.Parameters(name = "parentSampleSize={0}, childSampleSize={1}")
- fun data() = arrayOf(100, 500, 1000).flatMap { parentSampleSize ->
- arrayOf(10).map { childSampleSize ->
- arrayOf(parentSampleSize, childSampleSize)
- }
- }
+ fun data() = generateAllEnumerations(listOf(100, 500, 1000), listOf(10))
private const val DB_NAME = "relation-benchmark-test"
}
diff --git a/room/room-compiler-processing/build.gradle b/room/room-compiler-processing/build.gradle
index d63f0af..95225c5 100644
--- a/room/room-compiler-processing/build.gradle
+++ b/room/room-compiler-processing/build.gradle
@@ -42,6 +42,7 @@
testImplementation(libs.jsr250)
testImplementation(libs.ksp)
testImplementation(project(":room:room-compiler-processing-testing"))
+ testImplementation(project(":internal-testutils-common"))
}
tasks.withType(KotlinCompile).configureEach {
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
index 4cc1736..f9e64b5 100644
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
+++ b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/MethodSpecHelperTest.kt
@@ -21,7 +21,7 @@
import androidx.room.compiler.processing.util.Source
import androidx.room.compiler.processing.util.XTestInvocation
import androidx.room.compiler.processing.util.compileFiles
-import androidx.room.compiler.processing.util.generateAllEnumerations
+import androidx.testutils.generateAllEnumerations
import androidx.room.compiler.processing.util.javaTypeUtils
import androidx.room.compiler.processing.util.runKaptTest
import androidx.room.compiler.processing.util.runProcessorTest
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/ParameterizedHelper.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/ParameterizedHelper.kt
deleted file mode 100644
index 35161e7..0000000
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/ParameterizedHelper.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 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 androidx.room.compiler.processing.util
-
-/**
- * Used to generate all argument enumerations for Parameterized tests.
- * See [ParameterizedHelperTest] for usage.
- */
-fun generateAllEnumerations(vararg args: List<Any?>): List<Array<Any?>> =
- when (args.size) {
- 0 -> emptyList()
- 1 -> args[0].map {
- arrayOf(it)
- }
- else -> generateAllEnumerations(
- *args.dropLast(1).toTypedArray()
- ).flatMap { prev ->
- args.last().map { arg ->
- prev + arg
- }
- }
- }
diff --git a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/ParameterizedHelperTest.kt b/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/ParameterizedHelperTest.kt
deleted file mode 100644
index a534f52..0000000
--- a/room/room-compiler-processing/src/test/java/androidx/room/compiler/processing/util/ParameterizedHelperTest.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 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 androidx.room.compiler.processing.util
-
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-
-class ParameterizedHelperTest {
- @Test
- fun testEnumerations() {
- assertThat(generateAllEnumerations()).isEmpty()
-
- // Comparing List of Arrays doesn't work(https://github.com/google/truth/issues/928), so
- // we're mapping it to List of Lists
- assertThat(
- generateAllEnumerations(
- listOf(false, true)
- ).map { it.toList() }).isEqualTo(
- listOf(
- listOf<Any>(false), listOf<Any>(true)
- )
- )
- assertThat(generateAllEnumerations(listOf(false, true), listOf())).isEmpty()
- assertThat(
- generateAllEnumerations(
- listOf(false, true),
- listOf(false, true)
- ).map { it.toList() }
- ).isEqualTo(
- listOf(
- listOf(false, false), listOf(false, true),
- listOf(true, false), listOf(true, true)
- )
- )
- assertThat(
- generateAllEnumerations(
- listOf(false, true),
- (0..2).toList(),
- listOf("low", "hi")
- ).map { it.toList() }
- ).isEqualTo(
- listOf(
- listOf(false, 0, "low"),
- listOf(false, 0, "hi"),
- listOf(false, 1, "low"),
- listOf(false, 1, "hi"),
- listOf(false, 2, "low"),
- listOf(false, 2, "hi"),
- listOf(true, 0, "low"),
- listOf(true, 0, "hi"),
- listOf(true, 1, "low"),
- listOf(true, 1, "hi"),
- listOf(true, 2, "low"),
- listOf(true, 2, "hi")
- )
- )
- }
-}
diff --git a/room/room-compiler/build.gradle b/room/room-compiler/build.gradle
index 0da469d6..0d266f3 100644
--- a/room/room-compiler/build.gradle
+++ b/room/room-compiler/build.gradle
@@ -122,6 +122,7 @@
dir: "${new File(project(":sqlite:sqlite").buildDir, "libJar")}",
include : "*.jar"
))
+ testImplementation(project(":internal-testutils-common"))
}
def generateAntlrTask = task("generateAntlrGrammar", type: GenerateAntlrGrammar) {
diff --git a/room/room-compiler/src/test/kotlin/androidx/room/writer/DatabaseWriterTest.kt b/room/room-compiler/src/test/kotlin/androidx/room/writer/DatabaseWriterTest.kt
index 20772ab..a8b459a 100644
--- a/room/room-compiler/src/test/kotlin/androidx/room/writer/DatabaseWriterTest.kt
+++ b/room/room-compiler/src/test/kotlin/androidx/room/writer/DatabaseWriterTest.kt
@@ -22,6 +22,7 @@
import androidx.room.compiler.processing.util.XTestInvocation
import androidx.room.compiler.processing.util.runProcessorTest
import androidx.room.testing.asTestInvocationHandler
+import androidx.testutils.generateAllEnumerations
import loadTestSource
import org.junit.Test
import org.junit.experimental.runners.Enclosed
@@ -123,15 +124,10 @@
companion object {
@Parameterized.Parameters(name = "(maxStatementCount, valuesPerEntity)={0}")
@JvmStatic
- fun getParams(): List<Pair<Int, Int>> {
- val result = arrayListOf<Pair<Int, Int>>()
- arrayListOf(500, 1000, 3000).forEach { maxStatementCount ->
- arrayListOf(50, 100, 200).forEach { valuesPerEntity ->
- result.add(maxStatementCount to valuesPerEntity)
- }
+ fun getParams(): List<Pair<Int, Int>> =
+ generateAllEnumerations(listOf(500, 1000, 3000), listOf(50, 100, 200)).map {
+ it[0] as Int to it[1] as Int
}
- return result
- }
}
}
}
diff --git a/testutils/testutils-common/build.gradle b/testutils/testutils-common/build.gradle
index 19480c5..6090eaf 100644
--- a/testutils/testutils-common/build.gradle
+++ b/testutils/testutils-common/build.gradle
@@ -26,6 +26,9 @@
dependencies {
implementation(libs.kotlinStdlib)
implementation(libs.kotlinCoroutinesAndroid)
+
+ testImplementation(libs.junit)
+ testImplementation(libs.truth)
}
// Allow usage of Kotlin's @OptIn.
diff --git a/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt b/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt
new file mode 100644
index 0000000..270b30c
--- /dev/null
+++ b/testutils/testutils-common/src/main/java/androidx/testutils/ParameterizedHelper.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 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 androidx.testutils
+
+/**
+ * Generate all argument enumerations for Parameterized tests. For example,
+ * `generateAllEnumerations(listOf(false, true), listOf(1, 2, 3))` would return:
+ *
+ * ```
+ * [
+ * [false, 1],
+ * [false, 2],
+ * [false, 3],
+ * [true, 1],
+ * [true, 2],
+ * [true, 3]
+ * ]
+ * ```
+ *
+ * See [ParameterizedHelperTest] for more examples.
+ */
+fun generateAllEnumerations(vararg args: List<Any>): List<Array<Any>> =
+ generateAllEnumerationsIteratively(args.toList()).map { it.toTypedArray() }
+
+internal fun generateAllEnumerationsIteratively(elements: List<List<Any>>): List<List<Any>> {
+ if (elements.isEmpty()) return emptyList()
+ var number = elements.map { RadixDigit(it.size, 0) }
+ val total = elements.map { it.size }.product()
+ val result = mutableListOf<List<Any>>()
+ for (i in 0 until total) {
+ result.add(elements.mapIndexed { index, element -> element[number[index].digit] })
+ number = increment(number)
+ }
+ return result
+}
+
+internal fun increment(number: List<RadixDigit>): List<RadixDigit> {
+ var index = number.size - 1
+ var carry = 1
+ val result = mutableListOf<RadixDigit>()
+ while (index >= 0) {
+ val rd = number[index]
+ if (carry > 0) {
+ if (rd.digit < rd.radix - 1) {
+ result.add(rd.copy(digit = rd.digit + 1))
+ carry = 0
+ } else {
+ result.add(rd.copy(digit = 0))
+ }
+ } else {
+ result.add(rd)
+ }
+ index--
+ }
+ return result.reversed()
+}
+
+internal fun List<Int>.product() = this.fold(1) { acc, elem -> acc * elem }
+
+internal data class RadixDigit(val radix: Int, val digit: Int)
\ No newline at end of file
diff --git a/testutils/testutils-common/src/test/java/androidx/testutils/ParameterizedHelperTest.kt b/testutils/testutils-common/src/test/java/androidx/testutils/ParameterizedHelperTest.kt
new file mode 100644
index 0000000..43fc951e
--- /dev/null
+++ b/testutils/testutils-common/src/test/java/androidx/testutils/ParameterizedHelperTest.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright 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 androidx.testutils
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class ParameterizedHelperTest {
+ @Test
+ fun testIncrement() {
+ val number = listOf(RadixDigit(2, 0), RadixDigit(3, 0))
+
+ assertThat(::increment.invoke(number, 1)).isEqualTo(
+ listOf(RadixDigit(2, 0), RadixDigit(3, 1))
+ )
+ assertThat(::increment.invoke(number, 2)).isEqualTo(
+ listOf(RadixDigit(2, 0), RadixDigit(3, 2))
+ )
+ assertThat(::increment.invoke(number, 3)).isEqualTo(
+ listOf(RadixDigit(2, 1), RadixDigit(3, 0))
+ )
+ assertThat(::increment.invoke(number, 4)).isEqualTo(
+ listOf(RadixDigit(2, 1), RadixDigit(3, 1))
+ )
+ assertThat(::increment.invoke(number, 5)).isEqualTo(
+ listOf(RadixDigit(2, 1), RadixDigit(3, 2))
+ )
+ assertThat(::increment.invoke(number, 6)).isEqualTo(
+ listOf(RadixDigit(2, 0), RadixDigit(3, 0))
+ )
+ assertThat(::increment.invoke(number, 7)).isEqualTo(
+ listOf(RadixDigit(2, 0), RadixDigit(3, 1))
+ )
+ }
+
+ @Test
+ fun testProduct() {
+ assertThat(listOf<Int>().product()).isEqualTo(1)
+ assertThat(listOf(0).product()).isEqualTo(0)
+ assertThat(listOf(2).product()).isEqualTo(2)
+ assertThat(listOf(2, 3).product()).isEqualTo(6)
+ }
+
+ @Test
+ fun testEnumerations() {
+ assertThat(generateAllEnumerations()).isEmpty()
+
+ // Comparing List of Arrays doesn't work(https://github.com/google/truth/issues/928), so
+ // we're mapping it to List of Lists
+ assertThat(
+ generateAllEnumerations(listOf(false)).map { it.toList() }).isEqualTo(
+ listOf(
+ listOf<Any>(false)
+ )
+ )
+ assertThat(
+ generateAllEnumerations(listOf(false, true)).map { it.toList() }).isEqualTo(
+ listOf(
+ listOf<Any>(false),
+ listOf<Any>(true)
+ )
+ )
+ assertThat(generateAllEnumerations(listOf(false, true), listOf())).isEmpty()
+ assertThat(
+ generateAllEnumerations(
+ listOf(false, true),
+ listOf(false, true)
+ ).map { it.toList() }
+ ).isEqualTo(
+ listOf(
+ listOf(false, false),
+ listOf(false, true),
+ listOf(true, false),
+ listOf(true, true)
+ )
+ )
+ assertThat(
+ generateAllEnumerations(
+ listOf(false, true),
+ (0..2).toList(),
+ listOf("low", "hi")
+ ).map { it.toList() }
+ ).isEqualTo(
+ listOf(
+ listOf(false, 0, "low"),
+ listOf(false, 0, "hi"),
+ listOf(false, 1, "low"),
+ listOf(false, 1, "hi"),
+ listOf(false, 2, "low"),
+ listOf(false, 2, "hi"),
+ listOf(true, 0, "low"),
+ listOf(true, 0, "hi"),
+ listOf(true, 1, "low"),
+ listOf(true, 1, "hi"),
+ listOf(true, 2, "low"),
+ listOf(true, 2, "hi")
+ )
+ )
+ }
+
+ // `::f.invoke(0, 3)` is equivalent to `f(f(f(0)))`
+ private fun <T> ((T) -> T).invoke(argument: T, repeat: Int): T {
+ var result = argument
+ for (i in 0 until repeat) {
+ result = this(result)
+ }
+ return result
+ }
+
+ @Test
+ fun testInvoke() {
+ val addOne = { i: Int -> i + 1 }
+ assertThat(addOne.invoke(42, 0)).isEqualTo(42)
+ assertThat(addOne.invoke(42, 1)).isEqualTo(addOne(42))
+ assertThat(addOne.invoke(42, 2)).isEqualTo(addOne(addOne(42)))
+
+ val appendA = { str: String -> str + "a" }
+ assertThat(appendA.invoke("a", 2)).isEqualTo(appendA(appendA("a")))
+ }
+}