1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.navigation
18 
19 import android.app.Instrumentation
20 import android.content.Context
21 import android.net.Uri
22 import androidx.navigation.test.R
23 import androidx.navigation.test.TestEnum
24 import androidx.test.core.app.ApplicationProvider
25 import androidx.test.ext.junit.runners.AndroidJUnit4
26 import androidx.test.filters.SmallTest
27 import androidx.test.platform.app.InstrumentationRegistry
28 import androidx.testutils.TestNavigatorProvider
29 import com.google.common.truth.Truth.assertThat
30 import org.junit.Before
31 import org.junit.Test
32 import org.junit.runner.RunWith
33 
34 @SmallTest
35 @RunWith(AndroidJUnit4::class)
36 class NavInflaterTest {
37     private lateinit var instrumentation: Instrumentation
38 
39     @Before
getInstrumentationnull40     fun getInstrumentation() {
41         instrumentation = InstrumentationRegistry.getInstrumentation()
42     }
43 
44     @Test
testInflateSimplenull45     fun testInflateSimple() {
46         val context = ApplicationProvider.getApplicationContext() as Context
47         val navInflater = NavInflater(context, TestNavigatorProvider())
48         val graph = navInflater.inflate(R.navigation.nav_simple)
49 
50         assertThat(graph).isNotNull()
51         assertThat(graph.startDestinationId).isEqualTo(R.id.start_test)
52     }
53 
54     @Test
testEmptyLabelnull55     fun testEmptyLabel() {
56         val context = ApplicationProvider.getApplicationContext() as Context
57         val navInflater = NavInflater(context, TestNavigatorProvider())
58         val graph = navInflater.inflate(R.navigation.nav_simple)
59 
60         assertThat(graph).isNotNull()
61         assertThat(graph.label).isEqualTo("")
62     }
63 
64     @Test(expected = RuntimeException::class)
testInflateInvalidArgumentArgTypenull65     fun testInflateInvalidArgumentArgType() {
66         val context = ApplicationProvider.getApplicationContext() as Context
67         val navInflater = NavInflater(context, TestNavigatorProvider())
68         navInflater.inflate(R.navigation.nav_invalid_argument_arg_type)
69     }
70 
71     @Test(expected = RuntimeException::class)
testInflateInvalidArgumentDefaultValuenull72     fun testInflateInvalidArgumentDefaultValue() {
73         val context = ApplicationProvider.getApplicationContext() as Context
74         val navInflater = NavInflater(context, TestNavigatorProvider())
75         navInflater.inflate(R.navigation.nav_invalid_argument_default_value)
76     }
77 
78     @Test
testInflateDeepLinkWithApplicationIdnull79     fun testInflateDeepLinkWithApplicationId() {
80         val context = ApplicationProvider.getApplicationContext() as Context
81         val navInflater = NavInflater(context, TestNavigatorProvider())
82         val graph = navInflater.inflate(R.navigation.nav_simple)
83 
84         assertThat(graph).isNotNull()
85         val expectedUri =
86             Uri.parse("android-app://" + instrumentation.targetContext.packageName + "/test/arg2")
87         val expectedDeepLinkRequest = NavDeepLinkRequest.Builder.fromUri(expectedUri).build()
88         val result = graph.matchDeepLink(expectedDeepLinkRequest)
89         assertThat(result).isNotNull()
90         assertThat(result?.destination).isNotNull()
91         assertThat(result?.destination?.id).isEqualTo(R.id.second_test)
92     }
93 
94     @Test
testInflateDeepLinkWithApplicationIdActionnull95     fun testInflateDeepLinkWithApplicationIdAction() {
96         val context = ApplicationProvider.getApplicationContext() as Context
97         val navInflater = NavInflater(context, TestNavigatorProvider())
98         val graph = navInflater.inflate(R.navigation.nav_deeplink)
99 
100         assertThat(graph).isNotNull()
101         val expectedDeepLinkRequest = NavDeepLinkRequest.Builder.fromAction("action.only").build()
102         val result = graph.matchDeepLink(expectedDeepLinkRequest)
103         assertThat(result).isNotNull()
104         assertThat(result?.destination).isNotNull()
105         assertThat(result?.destination?.id).isEqualTo(R.id.action_only)
106     }
107 
108     @Test
testInflateDeepLinkWithApplicationIdAction_nonNullableArgnull109     fun testInflateDeepLinkWithApplicationIdAction_nonNullableArg() {
110         val context = ApplicationProvider.getApplicationContext() as Context
111         val navInflater = NavInflater(context, TestNavigatorProvider())
112         val graph = navInflater.inflate(R.navigation.nav_simple)
113 
114         assertThat(graph).isNotNull()
115         val expectedDeepLinkRequest = NavDeepLinkRequest.Builder.fromAction("test.action2").build()
116         val result = graph.matchDeepLink(expectedDeepLinkRequest)
117         assertThat(result).isNull()
118     }
119 
120     @Test
testInflateDeepLinkWithApplicationIdEmptyActionnull121     fun testInflateDeepLinkWithApplicationIdEmptyAction() {
122         val context = ApplicationProvider.getApplicationContext() as Context
123         val navInflater = NavInflater(context, TestNavigatorProvider())
124         val graph = navInflater.inflate(R.navigation.nav_simple)
125 
126         assertThat(graph).isNotNull()
127         val expectedDeepLinkRequest =
128             NavDeepLinkRequest.Builder.fromUri(
129                     Uri.parse(
130                         "android-app://" +
131                             instrumentation.targetContext.packageName +
132                             "/test/param1/param2"
133                     )
134                 )
135                 .build()
136         val result = graph.matchDeepLink(expectedDeepLinkRequest)
137         assertThat(result).isNotNull()
138         assertThat(result?.destination).isNotNull()
139         assertThat(result?.destination?.id).isEqualTo(R.id.second_test)
140     }
141 
142     @Test
testInflateDeepLinkWithApplicationIdMimeTypenull143     fun testInflateDeepLinkWithApplicationIdMimeType() {
144         val context = ApplicationProvider.getApplicationContext() as Context
145         val navInflater = NavInflater(context, TestNavigatorProvider())
146         val graph = navInflater.inflate(R.navigation.nav_deeplink)
147 
148         assertThat(graph).isNotNull()
149         val expectedDeepLinkRequest = NavDeepLinkRequest.Builder.fromMimeType("mime/only").build()
150         val result = graph.matchDeepLink(expectedDeepLinkRequest)
151         assertThat(result).isNotNull()
152         assertThat(result?.destination).isNotNull()
153         assertThat(result?.destination?.id).isEqualTo(R.id.mime_only)
154     }
155 
156     @Test
testInflateDeepLinkWithApplicationIdMimeTypeAndUri_nonNullableArgMissingUrinull157     fun testInflateDeepLinkWithApplicationIdMimeTypeAndUri_nonNullableArgMissingUri() {
158         val context = ApplicationProvider.getApplicationContext() as Context
159         val navInflater = NavInflater(context, TestNavigatorProvider())
160         val graph = navInflater.inflate(R.navigation.nav_simple)
161 
162         assertThat(graph).isNotNull()
163         val expectedDeepLinkRequest = NavDeepLinkRequest.Builder.fromMimeType("type/test2").build()
164         val result = graph.matchDeepLink(expectedDeepLinkRequest)
165         assertThat(result).isNull()
166     }
167 
168     @Test
testInflateDeepLinkWithApplicationIdMimeTypeAndActionnull169     fun testInflateDeepLinkWithApplicationIdMimeTypeAndAction() {
170         val context = ApplicationProvider.getApplicationContext() as Context
171         val navInflater = NavInflater(context, TestNavigatorProvider())
172         val graph = navInflater.inflate(R.navigation.nav_deeplink)
173 
174         assertThat(graph).isNotNull()
175         val expectedDeepLinkRequest =
176             NavDeepLinkRequest.Builder.fromMimeType("mime/action").setAction("mime.action").build()
177         val result = graph.matchDeepLink(expectedDeepLinkRequest)
178         assertThat(result).isNotNull()
179         assertThat(result?.destination).isNotNull()
180         assertThat(result?.destination?.id).isEqualTo(R.id.mime_action)
181     }
182 
183     @Test
testInflateDeepLinkWithApplicationIdMimeTypeAndAction_missingUrinull184     fun testInflateDeepLinkWithApplicationIdMimeTypeAndAction_missingUri() {
185         val context = ApplicationProvider.getApplicationContext() as Context
186         val navInflater = NavInflater(context, TestNavigatorProvider())
187         val graph = navInflater.inflate(R.navigation.nav_deeplink)
188 
189         assertThat(graph).isNotNull()
190         val expectedDeepLinkRequest =
191             NavDeepLinkRequest.Builder.fromMimeType("uri/actionMime")
192                 .setAction("uri.action.mime")
193                 .build()
194         val result = graph.matchDeepLink(expectedDeepLinkRequest)
195         assertThat(result).isNull()
196     }
197 
198     @Test
testInflateDeepLinkWithApplicationIdMimeTypeAndUrinull199     fun testInflateDeepLinkWithApplicationIdMimeTypeAndUri() {
200         val context = ApplicationProvider.getApplicationContext() as Context
201         val navInflater = NavInflater(context, TestNavigatorProvider())
202         val graph = navInflater.inflate(R.navigation.nav_deeplink)
203 
204         assertThat(graph).isNotNull()
205         val expectedDeepLinkRequest =
206             NavDeepLinkRequest.Builder.fromMimeType("mime/uri")
207                 .setUri(
208                     Uri.parse(
209                         "android-app://" + instrumentation.targetContext.packageName + "/mime/uri"
210                     )
211                 )
212                 .build()
213         val result = graph.matchDeepLink(expectedDeepLinkRequest)
214         assertThat(result).isNotNull()
215         assertThat(result?.destination).isNotNull()
216         assertThat(result?.destination?.id).isEqualTo(R.id.mime_uri)
217     }
218 
219     @Test
testInflateDeepLinkWithApplicationIdMimeTypeAndUri_missingUrinull220     fun testInflateDeepLinkWithApplicationIdMimeTypeAndUri_missingUri() {
221         val context = ApplicationProvider.getApplicationContext() as Context
222         val navInflater = NavInflater(context, TestNavigatorProvider())
223         val graph = navInflater.inflate(R.navigation.nav_deeplink)
224 
225         assertThat(graph).isNotNull()
226         val expectedDeepLinkRequest = NavDeepLinkRequest.Builder.fromMimeType("mime/uri").build()
227         val result = graph.matchDeepLink(expectedDeepLinkRequest)
228         assertThat(result).isNull()
229     }
230 
231     @Test
testInflateDeepLinkWithApplicationIdActionAndUrinull232     fun testInflateDeepLinkWithApplicationIdActionAndUri() {
233         val context = ApplicationProvider.getApplicationContext() as Context
234         val navInflater = NavInflater(context, TestNavigatorProvider())
235         val graph = navInflater.inflate(R.navigation.nav_deeplink)
236 
237         assertThat(graph).isNotNull()
238         val expectedDeepLinkRequest =
239             NavDeepLinkRequest.Builder.fromAction("action.uri")
240                 .setUri(
241                     Uri.parse(
242                         "android-app://" + instrumentation.targetContext.packageName + "/action/uri"
243                     )
244                 )
245                 .build()
246         val result = graph.matchDeepLink(expectedDeepLinkRequest)
247         assertThat(result).isNotNull()
248         assertThat(result?.destination).isNotNull()
249         assertThat(result?.destination?.id).isEqualTo(R.id.action_uri)
250     }
251 
252     @Test
testInflateDeepLinkWithApplicationIdActionAndUri_missingActionnull253     fun testInflateDeepLinkWithApplicationIdActionAndUri_missingAction() {
254         val context = ApplicationProvider.getApplicationContext() as Context
255         val navInflater = NavInflater(context, TestNavigatorProvider())
256         val graph = navInflater.inflate(R.navigation.nav_deeplink)
257 
258         assertThat(graph).isNotNull()
259         val expectedDeepLinkRequest =
260             NavDeepLinkRequest.Builder.fromUri(
261                     Uri.parse(
262                         "android-app://" + instrumentation.targetContext.packageName + "/action/uri"
263                     )
264                 )
265                 .build()
266         val result = graph.matchDeepLink(expectedDeepLinkRequest)
267         assertThat(result).isNull()
268     }
269 
270     @Test
testInflateWithDataPatternApplicationIdnull271     fun testInflateWithDataPatternApplicationId() {
272         val context = ApplicationProvider.getApplicationContext() as Context
273         val activityNavigator = ActivityNavigator(context)
274         val navInflater =
275             NavInflater(context, TestNavigatorProvider().apply { addNavigator(activityNavigator) })
276 
277         val graph = navInflater.inflate(R.navigation.nav_applicationid_arg)
278         val destination = graph.nodes.get(R.id.dataPattern_appId) as ActivityNavigator.Destination
279 
280         assertThat(destination.dataPattern)
281             .isEqualTo("http://www.example.com/${context.packageName}")
282     }
283 
284     @Test
testInflateWithNullDataPatternnull285     fun testInflateWithNullDataPattern() {
286         val context = ApplicationProvider.getApplicationContext() as Context
287         val activityNavigator = ActivityNavigator(context)
288         val navInflater =
289             NavInflater(context, TestNavigatorProvider().apply { addNavigator(activityNavigator) })
290 
291         val graph = navInflater.inflate(R.navigation.nav_applicationid_arg)
292         val destination = graph.nodes.get(R.id.dataPattern_null) as ActivityNavigator.Destination
293 
294         assertThat(destination.dataPattern).isNull()
295     }
296 
297     @Test
testInflateWithDataApplicationIdnull298     fun testInflateWithDataApplicationId() {
299         val context = ApplicationProvider.getApplicationContext() as Context
300         val activityNavigator = ActivityNavigator(context)
301 
302         val navInflater =
303             NavInflater(context, TestNavigatorProvider().apply { addNavigator(activityNavigator) })
304 
305         val graph = navInflater.inflate(R.navigation.nav_applicationid_arg)
306         val destination = graph.nodes.get(R.id.data_appId) as ActivityNavigator.Destination
307 
308         assertThat(destination.data)
309             .isEqualTo(Uri.parse("http://www.example.com/${context.packageName}"))
310     }
311 
312     @Test
testInflateWithNullDatanull313     fun testInflateWithNullData() {
314         val context = ApplicationProvider.getApplicationContext() as Context
315         val activityNavigator = ActivityNavigator(context)
316         val navInflater =
317             NavInflater(context, TestNavigatorProvider().apply { addNavigator(activityNavigator) })
318 
319         val graph = navInflater.inflate(R.navigation.nav_applicationid_arg)
320         val destination = graph.nodes.get(R.id.data_null) as ActivityNavigator.Destination
321 
322         assertThat(destination.data).isNull()
323     }
324 
325     @Test
testDefaultArgumentsIntegernull326     fun testDefaultArgumentsInteger() {
327         val defaultArguments = inflateDefaultArgumentsFromGraph()
328 
329         assertThat(defaultArguments["test_int"]?.run { type to defaultValue })
330             .isEqualTo(NavType.IntType to 12)
331     }
332 
333     @Test
testDefaultArgumentsFloatnull334     fun testDefaultArgumentsFloat() {
335         val defaultArguments = inflateDefaultArgumentsFromGraph()
336 
337         assertThat(defaultArguments["test_float"]?.run { type to defaultValue })
338             .isEqualTo(NavType.FloatType to 3.14f)
339     }
340 
341     @Test
testDefaultIntArgumentsFloatnull342     fun testDefaultIntArgumentsFloat() {
343         val defaultArguments = inflateDefaultArgumentsFromGraph()
344 
345         assertThat(defaultArguments["test_int_as_float"]?.run { type to defaultValue })
346             .isEqualTo(NavType.FloatType to 3f)
347     }
348 
349     @Test
testDefaultArgumentsBooleannull350     fun testDefaultArgumentsBoolean() {
351         val defaultArguments = inflateDefaultArgumentsFromGraph()
352 
353         assertThat(defaultArguments["test_boolean"]?.run { type to defaultValue })
354             .isEqualTo(NavType.BoolType to true)
355         assertThat(defaultArguments["test_boolean_false"]?.run { type to defaultValue })
356             .isEqualTo(NavType.BoolType to false)
357         assertThat(defaultArguments["test_boolean_with_argType"]?.run { type to defaultValue })
358             .isEqualTo(NavType.BoolType to true)
359         assertThat(
360                 defaultArguments["test_boolean_with_argType_false"]?.run { type to defaultValue }
361             )
362             .isEqualTo(NavType.BoolType to false)
363     }
364 
365     @Test
testDefaultArgumentsLongnull366     fun testDefaultArgumentsLong() {
367         val defaultArguments = inflateDefaultArgumentsFromGraph()
368 
369         assertThat(defaultArguments["test_long"]?.run { type to defaultValue })
370             .isEqualTo(NavType.LongType to 456789013456L)
371         assertThat(defaultArguments["test_long_with_argType"]?.run { type to defaultValue })
372             .isEqualTo(NavType.LongType to 456789013456L)
373         assertThat(defaultArguments["test_long_short"]?.run { type to defaultValue })
374             .isEqualTo(NavType.LongType to 123L)
375     }
376 
377     @Test
testDefaultArgumentsEnumnull378     fun testDefaultArgumentsEnum() {
379         val defaultArguments = inflateDefaultArgumentsFromGraph()
380 
381         assertThat(defaultArguments["test_enum"]?.run { type to defaultValue })
382             .isEqualTo(NavType.EnumType(TestEnum::class.java) to TestEnum.VALUE_ONE)
383     }
384 
385     @Test
testDefaultArgumentsStringnull386     fun testDefaultArgumentsString() {
387         val defaultArguments = inflateDefaultArgumentsFromGraph()
388 
389         assertThat(defaultArguments["test_string"]?.run { type to defaultValue })
390             .isEqualTo(NavType.StringType to "abc")
391         assertThat(defaultArguments["test_string_bool"]?.run { type to defaultValue })
392             .isEqualTo(NavType.StringType to "true")
393         assertThat(defaultArguments["test_string_long"]?.run { type to defaultValue })
394             .isEqualTo(NavType.StringType to "123L")
395         assertThat(defaultArguments["test_string_integer"]?.run { type to defaultValue })
396             .isEqualTo(NavType.StringType to "123")
397 
398         assertThat(
399                 defaultArguments["test_string_no_default"]?.run { type to isDefaultValuePresent }
400             )
401             .isEqualTo(NavType.StringType to false)
402     }
403 
404     @Test
testDefaultArgumentsStringArraynull405     fun testDefaultArgumentsStringArray() {
406         val defaultArguments = inflateDefaultArgumentsFromGraph()
407 
408         assertThat(defaultArguments["test_string_array"]?.run { type to defaultValue })
409             .isEqualTo(NavType.StringArrayType to null)
410     }
411 
412     @Test
testDefaultArgumentsReferencenull413     fun testDefaultArgumentsReference() {
414         val defaultArguments = inflateDefaultArgumentsFromGraph()
415 
416         assertThat(defaultArguments["test_reference"]?.run { type to defaultValue })
417             .isEqualTo(NavType.ReferenceType to R.style.AppTheme)
418         assertThat(defaultArguments["test_reference_dimen"]?.run { type to defaultValue })
419             .isEqualTo(NavType.ReferenceType to R.dimen.test_dimen_arg)
420         assertThat(defaultArguments["test_reference_integer"]?.run { type to defaultValue })
421             .isEqualTo(NavType.ReferenceType to R.integer.test_integer_arg)
422         assertThat(defaultArguments["test_reference_string"]?.run { type to defaultValue })
423             .isEqualTo(NavType.ReferenceType to R.string.test_string_arg)
424         assertThat(defaultArguments["test_reference_bool"]?.run { type to defaultValue })
425             .isEqualTo(NavType.ReferenceType to R.bool.test_bool_arg)
426         assertThat(defaultArguments["test_reference_color"]?.run { type to defaultValue })
427             .isEqualTo(NavType.ReferenceType to R.color.test_color_arg)
428         assertThat(defaultArguments["test_reference_zero_default"]?.run { type to defaultValue })
429             .isEqualTo(NavType.ReferenceType to 0)
430     }
431 
432     @Test
testRelativeClassNamenull433     fun testRelativeClassName() {
434         val defaultArguments = inflateDefaultArgumentsFromGraph()
435         assertThat(defaultArguments["test_relative_classname"]?.run { type to defaultValue })
436             .isEqualTo(NavType.EnumType(TestEnum::class.java) to TestEnum.VALUE_TWO)
437     }
438 
439     @Test
440     @Suppress("DEPRECATION")
testActionArgumentsnull441     fun testActionArguments() {
442         val context = ApplicationProvider.getApplicationContext() as Context
443         val navInflater = NavInflater(context, TestNavigatorProvider())
444         val graph = navInflater.inflate(R.navigation.nav_default_arguments)
445         val startDestination = graph.findNode(graph.startDestinationId)
446         val action = startDestination?.getAction(R.id.my_action)
447         assertThat(action?.defaultArguments?.get("test_action_arg")).isEqualTo(123L)
448     }
449 
inflateDefaultArgumentsFromGraphnull450     private fun inflateDefaultArgumentsFromGraph(): Map<String, NavArgument> {
451         val context = ApplicationProvider.getApplicationContext() as Context
452         val navInflater = NavInflater(context, TestNavigatorProvider())
453         val graph = navInflater.inflate(R.navigation.nav_default_arguments)
454 
455         val startDestination = graph.findNode(graph.startDestinationId)
456         val defaultArguments = startDestination?.arguments
457 
458         assertThat(defaultArguments).isNotNull()
459         return defaultArguments as Map<String, NavArgument>
460     }
461 }
462