Add tests for CollectionNavType
Test: ./gradlew navigation:navigation-common:cC
Test: ./gradlew navigation:navigation-common:test
Test: ./gradlew navigation:navigation-runtime:cC
Bug: 188693139
Change-Id: I9bb8e49046d7c8335e979f61044b4058653ecad4
diff --git a/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteDecoderTest.kt b/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteDecoderTest.kt
index c07082f..6905a17 100644
--- a/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteDecoderTest.kt
+++ b/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteDecoderTest.kt
@@ -379,6 +379,17 @@
assertThat(result.custom.arg).isNull()
}
+ @Test
+ fun decodeCollectionNavType() {
+ val arg = listOf(CustomType(1), CustomType(3), CustomType(5))
+ val bundle = bundleOf("list" to arg)
+ val result = decode<TestClassCollectionArg>(
+ bundle,
+ listOf(navArgument("list") { type = collectionNavType })
+ )
+ assertThat(result.list).containsExactlyElementsIn(arg).inOrder()
+ }
+
private inline fun <reified T : Any> decode(
bundle: Bundle,
args: List<NamedNavArgument> = emptyList()
diff --git a/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt b/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt
index 6563a2b..21f2e2e 100644
--- a/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt
+++ b/navigation/navigation-common/src/androidTest/java/androidx/navigation/serialization/RouteFilledTest.kt
@@ -17,6 +17,7 @@
package androidx.navigation.serialization
import android.os.Bundle
+import androidx.navigation.CollectionNavType
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavType
import androidx.navigation.navArgument
@@ -745,6 +746,16 @@
PATH_SERIAL_NAME
)
}
+
+ @Test
+ fun collectionNavType() {
+ assertThatRouteFilledFrom(
+ TestClassCollectionArg(listOf(CustomType(1), CustomType(3), CustomType(5))),
+ listOf(navArgument("list") { type = collectionNavType })
+ ).isEqualTo(
+ "$PATH_SERIAL_NAME?list=1&list=3&list=5"
+ )
+ }
}
private fun <T : Any> assertThatRouteFilledFrom(
@@ -809,6 +820,25 @@
CustomSerializerClass(decoder.decodeLong())
}
+@Serializable
+data class CustomType(val id: Int)
+
+@Serializable
+@SerialName(PATH_SERIAL_NAME)
+class TestClassCollectionArg(val list: List<CustomType>)
+
+val collectionNavType = object : CollectionNavType<List<CustomType>>(false) {
+ override fun put(bundle: Bundle, key: String, value: List<CustomType>) { }
+ override fun serializeAsValues(value: List<CustomType>): List<String> =
+ value.map { it.id.toString() }
+ @Suppress("UNCHECKED_CAST", "DEPRECATION")
+ override fun get(bundle: Bundle, key: String): List<CustomType> {
+ return bundle[key] as List<CustomType>
+ }
+ override fun parseValue(value: String): List<CustomType> = listOf()
+ override fun serializeAsValue(value: List<CustomType>) = "customValue"
+}
+
private interface TestInterface
internal fun stringArgument(
diff --git a/navigation/navigation-common/src/test/java/androidx/navigation/serialization/RoutePatternTest.kt b/navigation/navigation-common/src/test/java/androidx/navigation/serialization/RoutePatternTest.kt
index 41e24f8..dee4366 100644
--- a/navigation/navigation-common/src/test/java/androidx/navigation/serialization/RoutePatternTest.kt
+++ b/navigation/navigation-common/src/test/java/androidx/navigation/serialization/RoutePatternTest.kt
@@ -17,6 +17,7 @@
package androidx.navigation.serialization
import android.os.Bundle
+import androidx.navigation.CollectionNavType
import androidx.navigation.NavType
import com.google.common.truth.Truth.assertThat
import kotlin.reflect.KType
@@ -512,6 +513,28 @@
PATH_SERIAL_NAME
)
}
+
+ @Test
+ fun collectionNavType() {
+ @Serializable
+ class CustomType
+
+ @Serializable
+ @SerialName(PATH_SERIAL_NAME)
+ class TestClass(val list: List<CustomType>)
+
+ val type = object : CollectionNavType<List<CustomType>>(false) {
+ override fun put(bundle: Bundle, key: String, value: List<CustomType>) { }
+ override fun serializeAsValues(value: List<CustomType>): List<String> = emptyList()
+ override fun get(bundle: Bundle, key: String): List<CustomType>? = null
+ override fun parseValue(value: String): List<CustomType> = listOf()
+ override fun serializeAsValue(value: List<CustomType>) = "customValue"
+ }
+ val map = mapOf(typeOf<List<CustomType>>() to type)
+ assertThatRoutePatternFrom(serializer<TestClass>(), map).isEqualTo(
+ "$PATH_SERIAL_NAME?list={list}"
+ )
+ }
}
private fun <T> assertThatRoutePatternFrom(
diff --git a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
index 07069ca..e7de37a 100644
--- a/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
+++ b/navigation/navigation-runtime/src/androidTest/java/androidx/navigation/NavControllerRouteTest.kt
@@ -54,6 +54,7 @@
import com.google.common.truth.Truth.assertWithMessage
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
+import kotlin.reflect.typeOf
import kotlin.test.assertFailsWith
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@@ -261,7 +262,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = TestClass::class) {
- test(route = TestClass::class)
+ test<TestClass>()
}
assertThat(navController.currentDestination?.route).isEqualTo("test")
assertThat(navController.currentDestination?.id).isEqualTo(
@@ -278,7 +279,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = TestClass::class) {
- test(route = TestClass::class)
+ test<TestClass>()
}
assertThat(navController.currentDestination?.route).isEqualTo("test/{arg}")
assertThat(navController.currentDestination?.id).isEqualTo(
@@ -301,7 +302,7 @@
startDestination = NestedGraph::class
) {
navigation<NestedGraph>(startDestination = TestClass::class) {
- test(route = TestClass::class)
+ test<TestClass>()
}
}
assertThat(navController.currentDestination?.route).isEqualTo("test")
@@ -319,7 +320,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = TestClass()) {
- test(route = TestClass::class)
+ test<TestClass>()
}
assertThat(navController.currentDestination?.route).isEqualTo("test")
assertThat(navController.currentDestination?.id).isEqualTo(
@@ -338,7 +339,7 @@
navController.graph = navController.createGraph(
startDestination = TestClass(0)
) {
- test(route = TestClass::class)
+ test<TestClass>()
}
assertThat(navController.currentDestination?.route).isEqualTo(
"test/{arg}"
@@ -362,7 +363,7 @@
navController.graph = navController.createGraph(
startDestination = TestClass(false)
) {
- test(route = TestClass::class)
+ test<TestClass>()
}
assertThat(navController.currentDestination?.route).isEqualTo(
"test?arg={arg}"
@@ -467,7 +468,7 @@
startDestination = NestedGraph(0)
) {
navigation<NestedGraph>(startDestination = TestClass(1)) {
- test(route = TestClass::class)
+ test<TestClass>()
}
}
assertThat(navController.currentDestination?.route).isEqualTo(
@@ -710,7 +711,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class)
+ test<TestClass>()
}
assertThat(navController.currentDestination?.route).isEqualTo("start")
assertThat(navController.currentBackStack.value.size).isEqualTo(2)
@@ -726,7 +727,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClassPathArg::class)
+ test<TestClassPathArg>()
}
assertThat(navController.currentDestination?.route).isEqualTo("start")
assertThat(navController.currentBackStack.value.size).isEqualTo(2)
@@ -747,7 +748,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class)
+ test<TestClass>()
}
assertThat(navController.currentDestination?.route).isEqualTo("start")
assertThat(navController.currentBackStack.value.size).isEqualTo(2)
@@ -771,7 +772,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class)
+ test<TestClass>()
}
assertThat(navController.currentDestination?.route).isEqualTo("start")
assertThat(navController.currentBackStack.value.size).isEqualTo(2)
@@ -790,6 +791,56 @@
@UiThreadTest
@Test
+ fun testNavigateWithObjectCollectionNavType() {
+ val navController = createNavController()
+ @Serializable
+ data class CustomType(val id: Int)
+ @Serializable
+ class TestClass(val list: List<CustomType>)
+
+ val collectionNavType = object : CollectionNavType<List<CustomType>>(false) {
+ override fun put(bundle: Bundle, key: String, value: List<CustomType>) {
+ val array = value.map { it.id }.toIntArray()
+ bundle.putIntArray(key, array)
+ }
+ override fun serializeAsValues(value: List<CustomType>) = value.map { it.id.toString() }
+ override fun get(bundle: Bundle, key: String): List<CustomType> {
+ return bundle.getIntArray(key)!!.map { CustomType(it) }
+ }
+ override fun parseValue(value: String) = listOf(CustomType(value.toInt()))
+ override fun parseValue(
+ value: String,
+ previousValue: List<CustomType>
+ ): List<CustomType> {
+ val list = mutableListOf<CustomType>()
+ list.addAll(previousValue)
+ list.add(CustomType(value.toInt()))
+ return list
+ }
+ }
+
+ navController.graph = navController.createGraph(startDestination = "start") {
+ test("start")
+ test<TestClass>(mapOf(typeOf<List<CustomType>>() to collectionNavType))
+ }
+
+ assertThat(navController.currentDestination?.route).isEqualTo("start")
+ assertThat(navController.currentBackStack.value.size).isEqualTo(2)
+
+ val list = listOf(CustomType(1), CustomType(3), CustomType(5))
+ navController.navigate(TestClass(list))
+ assertThat(navController.currentDestination?.route).isEqualTo(
+ "androidx.navigation.NavControllerRouteTest." +
+ "testNavigateWithObjectCollectionNavType.TestClass?list={list}"
+ )
+ assertThat(navController.currentBackStack.value.size).isEqualTo(3)
+ assertThat(
+ navController.currentBackStackEntry!!.toRoute<TestClass>().list
+ ).containsExactlyElementsIn(list).inOrder()
+ }
+
+ @UiThreadTest
+ @Test
fun testNavigateWithObjectInvalidObject() {
@Serializable
class WrongTestClass
@@ -797,7 +848,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class)
+ test<TestClass>()
}
assertThat(navController.currentDestination?.route).isEqualTo("start")
assertThat(navController.currentBackStack.value.size).isEqualTo(2)
@@ -813,7 +864,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class)
+ test<TestClass>()
}
assertThat(navController.currentDestination?.route).isEqualTo("start")
assertThat(navController.currentBackStack.value.size).isEqualTo(2)
@@ -831,7 +882,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClassPathArg::class)
+ test<TestClassPathArg>()
}
assertThat(navController.currentDestination?.route).isEqualTo("start")
assertThat(navController.currentBackStack.value.size).isEqualTo(2)
@@ -862,7 +913,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClassPathArg::class)
+ test<TestClassPathArg>()
}
assertThat(navController.currentDestination?.route).isEqualTo("start")
assertThat(navController.currentBackStack.value.size).isEqualTo(2)
@@ -1403,7 +1454,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class)
+ test<TestClass>()
}
navController.navigate(TEST_CLASS_ROUTE)
@@ -1421,7 +1472,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClassPathArg::class)
+ test<TestClassPathArg>()
}
navController.navigate(TEST_CLASS_PATH_ARG_ROUTE.replace("{arg}", "0"))
@@ -1439,7 +1490,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class)
+ test<TestClass>()
}
navController.navigate(TEST_CLASS_ROUTE)
@@ -1459,7 +1510,7 @@
route = TestGraph::class,
startDestination = TestClass::class
) {
- test(TestClass::class)
+ test<TestClass>()
}
assertThat(navController.currentBackStack.value.size).isEqualTo(2)
@@ -1479,7 +1530,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClassPathArg::class)
+ test<TestClassPathArg>()
}
navController.navigate(TEST_CLASS_PATH_ARG_ROUTE.replace("{arg}", "0"))
@@ -1497,7 +1548,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClassPathArg::class)
+ test<TestClassPathArg>()
}
navController.navigate(TEST_CLASS_PATH_ARG_ROUTE.replace("{arg}", "0"))
@@ -1727,7 +1778,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class)
+ test<TestClass>()
}
// first nav
@@ -1752,7 +1803,7 @@
route = TestGraph::class,
startDestination = TestClass::class
) {
- test(TestClass::class)
+ test<TestClass>()
}
assertThat(navController.currentBackStack.value.size).isEqualTo(2)
@@ -1773,7 +1824,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClassPathArg::class)
+ test<TestClassPathArg>()
}
// first nav
@@ -1796,7 +1847,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class)
+ test<TestClass>()
}
// first nav
@@ -1821,7 +1872,7 @@
route = TestGraph::class,
startDestination = TestClass::class
) {
- test(TestClass::class)
+ test<TestClass>()
}
assertThat(navController.currentBackStack.value.size).isEqualTo(2)
@@ -1842,7 +1893,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClassPathArg::class)
+ test<TestClassPathArg>()
}
// first nav
@@ -1865,7 +1916,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClassPathArg::class)
+ test<TestClassPathArg>()
}
// first nav
@@ -2217,7 +2268,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class)
+ test<TestClass>()
}
navController.navigate(TEST_CLASS_ROUTE)
@@ -2236,7 +2287,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class)
+ test<TestClass>()
}
navController.navigate(TEST_CLASS_ROUTE)
@@ -2255,7 +2306,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClassPathArg::class)
+ test<TestClassPathArg>()
}
navController.navigate(TEST_CLASS_PATH_ARG_ROUTE.replace("{arg}", "0"))
@@ -2274,7 +2325,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class)
+ test<TestClass>()
}
navController.navigate(TEST_CLASS_ROUTE)
@@ -2293,7 +2344,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class)
+ test<TestClass>()
}
navController.navigate(TEST_CLASS_ROUTE)
@@ -2312,7 +2363,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClassPathArg::class)
+ test<TestClassPathArg>()
}
// second nav
@@ -2339,7 +2390,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class) {
+ test<TestClass> {
deepLink(
navDeepLink<TestClass> { uriPattern = baseUri }
)
@@ -2365,7 +2416,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class) {
+ test<TestClass> {
deepLink(
navDeepLink<TestClass> { uriPattern = baseUri }
)
@@ -2391,7 +2442,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class) {
+ test<TestClass> {
deepLink(
navDeepLink<TestClass> { uriPattern = baseUri }
)
@@ -2417,7 +2468,7 @@
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = "start") {
test("start")
- test(TestClass::class) {
+ test<TestClass> {
deepLink(
navDeepLink<TestClass> { uriPattern = baseUri }
)
@@ -3188,7 +3239,7 @@
fun testNavigateWithPopKClassInclusive() {
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = TestClass::class) {
- test(TestClass::class)
+ test<TestClass>()
test("second")
}
@@ -3212,7 +3263,7 @@
fun testNavigateWithPopKClassNotInclusive() {
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = TestClass::class) {
- test(TestClass::class)
+ test<TestClass>()
test("second")
test("third")
}
@@ -3241,7 +3292,7 @@
fun testNavigateWithPopObjectInclusive() {
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = TestClass::class) {
- test(TestClass::class)
+ test<TestClass>()
test("second")
}
@@ -3265,7 +3316,7 @@
fun testNavigateWithPopObjectNotInclusive() {
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = TestClass::class) {
- test(TestClass::class)
+ test<TestClass>()
test("second")
test("third")
}
@@ -3294,7 +3345,7 @@
fun testNavigateWithPopObjectArg() {
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = TestClassPathArg(0)) {
- test(TestClassPathArg::class)
+ test<TestClassPathArg>()
test("second")
}
@@ -3318,7 +3369,7 @@
fun testNavigateWithPopObjectWrongArg() {
val navController = createNavController()
navController.graph = navController.createGraph(startDestination = TestClassPathArg(0)) {
- test(TestClassPathArg::class)
+ test<TestClassPathArg>()
test("second")
}
diff --git a/testutils/testutils-navigation/src/main/java/androidx/testutils/TestNavigatorDestinationBuilder.kt b/testutils/testutils-navigation/src/main/java/androidx/testutils/TestNavigatorDestinationBuilder.kt
index d321417..b24ff3b 100644
--- a/testutils/testutils-navigation/src/main/java/androidx/testutils/TestNavigatorDestinationBuilder.kt
+++ b/testutils/testutils-navigation/src/main/java/androidx/testutils/TestNavigatorDestinationBuilder.kt
@@ -23,8 +23,10 @@
import androidx.navigation.NavDestinationBuilder
import androidx.navigation.NavDestinationDsl
import androidx.navigation.NavGraphBuilder
+import androidx.navigation.NavType
import androidx.navigation.get
import kotlin.reflect.KClass
+import kotlin.reflect.KType
/**
* Construct a new [TestNavigator.Destination]
@@ -39,7 +41,9 @@
/**
* Construct a new [TestNavigator.Destination]
*/
-inline fun NavGraphBuilder.test(route: KClass<*>) = test(route) {}
+inline fun <reified T : Any> NavGraphBuilder.test(
+ typeMap: Map<KType, NavType<*>> = emptyMap()
+) = test<T>(typeMap) {}
/**
* Construct a new [TestNavigator.Destination]
@@ -68,21 +72,26 @@
/**
* Construct a new [TestNavigator.Destination]
*/
-inline fun NavGraphBuilder.test(
- route: KClass<*>,
+inline fun <reified T : Any> NavGraphBuilder.test(
+ typeMap: Map<KType, NavType<*>> = emptyMap(),
builder: TestNavigatorDestinationBuilder.() -> Unit
) = destination(
- TestNavigatorDestinationBuilder(provider[TestNavigator::class], route).apply(builder)
+ TestNavigatorDestinationBuilder(provider[TestNavigator::class], T::class, typeMap)
+ .apply(builder)
)
/**
* DSL for constructing a new [TestNavigator.Destination]
*/
@NavDestinationDsl
+@OptIn(ExperimentalSafeArgsApi::class)
class TestNavigatorDestinationBuilder : NavDestinationBuilder<TestNavigator.Destination> {
@Suppress("DEPRECATION")
constructor(navigator: TestNavigator, @IdRes id: Int = 0) : super(navigator, id)
constructor(navigator: TestNavigator, route: String) : super(navigator, route)
- @OptIn(ExperimentalSafeArgsApi::class)
- constructor(navigator: TestNavigator, route: KClass<*>) : super(navigator, route, emptyMap())
+ constructor(
+ navigator: TestNavigator,
+ route: KClass<*>,
+ typeMap: Map<KType, NavType<*>>
+ ) : super(navigator, route, typeMap)
}