1/* 2 * Copyright 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 17package androidx.navigation; 18 19import static org.hamcrest.CoreMatchers.is; 20import static org.hamcrest.CoreMatchers.notNullValue; 21import static org.hamcrest.CoreMatchers.nullValue; 22import static org.hamcrest.MatcherAssert.assertThat; 23 24import android.content.Context; 25import android.content.Intent; 26import android.os.Bundle; 27import android.os.Parcel; 28import android.support.test.InstrumentationRegistry; 29import android.support.test.filters.SmallTest; 30import android.support.v4.app.TaskStackBuilder; 31 32import androidx.navigation.test.R; 33import androidx.navigation.testing.TestNavigator; 34 35import org.junit.Assert; 36import org.junit.Test; 37 38@SmallTest 39public class NavControllerTest { 40 private static final String TEST_ARG = "test"; 41 private static final String TEST_ARG_VALUE = "value"; 42 private static final String TEST_OVERRIDDEN_VALUE_ARG = "test_overriden_value"; 43 private static final String TEST_OVERRIDDEN_VALUE_ARG_VALUE = "override"; 44 45 @Test 46 public void testStartDestination() { 47 NavController navController = createNavController(); 48 navController.setGraph(R.navigation.nav_start_destination); 49 assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test)); 50 } 51 52 @Test(expected = IllegalStateException.class) 53 public void testMissingStartDestination() { 54 NavController navController = createNavController(); 55 navController.setGraph(R.navigation.nav_missing_start_destination); 56 } 57 58 @Test(expected = IllegalArgumentException.class) 59 public void testInvalidStartDestination() { 60 NavController navController = createNavController(); 61 navController.setGraph(R.navigation.nav_invalid_start_destination); 62 } 63 64 @Test 65 public void testNestedStartDestination() { 66 NavController navController = createNavController(); 67 navController.setGraph(R.navigation.nav_nested_start_destination); 68 assertThat(navController.getCurrentDestination().getId(), is(R.id.nested_test)); 69 } 70 71 @Test 72 public void testSetGraph() { 73 NavController navController = createNavController(); 74 assertThat(navController.getGraph(), is(nullValue(NavGraph.class))); 75 76 navController.setGraph(R.navigation.nav_start_destination); 77 assertThat(navController.getGraph(), is(notNullValue(NavGraph.class))); 78 assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test)); 79 } 80 81 @Test 82 public void testNavigate() { 83 NavController navController = createNavController(); 84 navController.setGraph(R.navigation.nav_simple); 85 TestNavigator navigator = navController.getNavigatorProvider() 86 .getNavigator(TestNavigator.class); 87 assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test)); 88 assertThat(navigator.mBackStack.size(), is(1)); 89 90 navController.navigate(R.id.second_test); 91 assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test)); 92 assertThat(navigator.mBackStack.size(), is(2)); 93 } 94 95 @Test 96 public void testSaveRestoreStateXml() { 97 Context context = InstrumentationRegistry.getTargetContext(); 98 NavController navController = new NavController(context); 99 TestNavigator navigator = new TestNavigator(); 100 navController.getNavigatorProvider().addNavigator(navigator); 101 navController.setGraph(R.navigation.nav_simple); 102 navController.navigate(R.id.second_test); 103 104 Bundle savedState = navController.saveState(); 105 navController = new NavController(context); 106 navController.getNavigatorProvider().addNavigator(navigator); 107 108 // Restore state should automatically re-inflate the graph 109 // Since the graph has a set id 110 navController.restoreState(savedState); 111 assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test)); 112 assertThat(navigator.mBackStack.size(), is(2)); 113 } 114 115 @Test 116 public void testSaveRestoreStateProgrammatic() { 117 Context context = InstrumentationRegistry.getTargetContext(); 118 NavController navController = new NavController(context); 119 TestNavigator navigator = new TestNavigator(); 120 navController.getNavigatorProvider().addNavigator(navigator); 121 NavGraph graph = new NavInflater(context, navController.getNavigatorProvider()) 122 .inflate(R.navigation.nav_simple); 123 navController.setGraph(graph); 124 navController.navigate(R.id.second_test); 125 126 Bundle savedState = navController.saveState(); 127 navController = new NavController(context); 128 navController.getNavigatorProvider().addNavigator(navigator); 129 130 // Restore state doesn't recreate any graph 131 navController.restoreState(savedState); 132 assertThat(navController.getGraph(), is(nullValue(NavGraph.class))); 133 134 // Explicitly setting a graph then restores the state 135 navController.setGraph(graph); 136 assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test)); 137 assertThat(navigator.mBackStack.size(), is(2)); 138 } 139 140 @Test 141 public void testNavigateWithNoDefaultValue() { 142 Bundle returnedArgs = navigateWithArgs(null); 143 144 // Test that arguments without a default value aren't passed through at all 145 assertThat(returnedArgs.containsKey("test_no_default_value"), is(false)); 146 } 147 148 @Test 149 public void testNavigateWithDefaultArgs() { 150 Bundle returnedArgs = navigateWithArgs(null); 151 152 // Test that default values are passed through 153 assertThat(returnedArgs.getString("test_default_value"), is("default")); 154 } 155 156 @Test 157 public void testNavigateWithArgs() { 158 Bundle args = new Bundle(); 159 args.putString(TEST_ARG, TEST_ARG_VALUE); 160 Bundle returnedArgs = navigateWithArgs(args); 161 162 // Test that programmatically constructed arguments are passed through 163 assertThat(returnedArgs.getString(TEST_ARG), is(TEST_ARG_VALUE)); 164 } 165 166 @Test 167 public void testNavigateWithOverriddenDefaultArgs() { 168 Bundle args = new Bundle(); 169 args.putString(TEST_OVERRIDDEN_VALUE_ARG, TEST_OVERRIDDEN_VALUE_ARG_VALUE); 170 Bundle returnedArgs = navigateWithArgs(args); 171 172 // Test that default values can be overridden by programmatic values 173 assertThat(returnedArgs.getString(TEST_OVERRIDDEN_VALUE_ARG), 174 is(TEST_OVERRIDDEN_VALUE_ARG_VALUE)); 175 } 176 177 private Bundle navigateWithArgs(Bundle args) { 178 NavController navController = createNavController(); 179 navController.setGraph(R.navigation.nav_arguments); 180 181 navController.navigate(R.id.second_test, args); 182 183 TestNavigator navigator = navController.getNavigatorProvider() 184 .getNavigator(TestNavigator.class); 185 args = navigator.mBackStack.peekLast().second; 186 assertThat(args, is(notNullValue(Bundle.class))); 187 188 return args; 189 } 190 191 @Test 192 public void testNavigateThenPop() { 193 NavController navController = createNavController(); 194 navController.setGraph(R.navigation.nav_simple); 195 TestNavigator navigator = navController.getNavigatorProvider() 196 .getNavigator(TestNavigator.class); 197 assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test)); 198 assertThat(navigator.mBackStack.size(), is(1)); 199 200 navController.navigate(R.id.second_test); 201 assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test)); 202 assertThat(navigator.mBackStack.size(), is(2)); 203 204 navController.popBackStack(); 205 assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test)); 206 assertThat(navigator.mBackStack.size(), is(1)); 207 } 208 209 @Test 210 public void testNavigateFromNestedThenNavigatorInstigatedPop() { 211 NavController navController = createNavController(); 212 navController.setGraph(R.navigation.nav_nested_start_destination); 213 TestNavigator navigator = navController.getNavigatorProvider() 214 .getNavigator(TestNavigator.class); 215 assertThat(navController.getCurrentDestination().getId(), is(R.id.nested_test)); 216 assertThat(navigator.mBackStack.size(), is(1)); 217 218 navController.navigate(R.id.second_test); 219 assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test)); 220 assertThat(navigator.mBackStack.size(), is(2)); 221 222 // A Navigator can pop a destination off its own back stack 223 // then inform the NavController via dispatchOnNavigatorNavigated 224 navigator.mBackStack.removeLast(); 225 NavDestination newDestination = navigator.mBackStack.peekLast().first; 226 assertThat(newDestination, is(notNullValue())); 227 navigator.dispatchOnNavigatorNavigated(newDestination.getId(), 228 Navigator.BACK_STACK_DESTINATION_POPPED); 229 assertThat(navController.getCurrentDestination().getId(), is(R.id.nested_test)); 230 assertThat(navigator.mBackStack.size(), is(1)); 231 } 232 233 @Test 234 public void testNavigateThenNavigateUp() { 235 NavController navController = createNavController(); 236 navController.setGraph(R.navigation.nav_simple); 237 TestNavigator navigator = navController.getNavigatorProvider() 238 .getNavigator(TestNavigator.class); 239 assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test)); 240 assertThat(navigator.mBackStack.size(), is(1)); 241 242 navController.navigate(R.id.second_test); 243 assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test)); 244 assertThat(navigator.mBackStack.size(), is(2)); 245 246 // This should function identically to popBackStack() 247 navController.navigateUp(); 248 assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test)); 249 assertThat(navigator.mBackStack.size(), is(1)); 250 } 251 252 @Test 253 public void testNavigateViaAction() { 254 NavController navController = createNavController(); 255 navController.setGraph(R.navigation.nav_simple); 256 assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test)); 257 TestNavigator navigator = navController.getNavigatorProvider() 258 .getNavigator(TestNavigator.class); 259 assertThat(navigator.mBackStack.size(), is(1)); 260 261 navController.navigate(R.id.second); 262 assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test)); 263 assertThat(navigator.mBackStack.size(), is(2)); 264 } 265 266 @Test 267 public void testNavigateOptionSingleTop() { 268 NavController navController = createNavController(); 269 navController.setGraph(R.navigation.nav_simple); 270 navController.navigate(R.id.second_test); 271 assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test)); 272 TestNavigator navigator = navController.getNavigatorProvider() 273 .getNavigator(TestNavigator.class); 274 assertThat(navigator.mBackStack.size(), is(2)); 275 276 navController.navigate(R.id.self); 277 assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test)); 278 assertThat(navigator.mBackStack.size(), is(2)); 279 } 280 281 @Test 282 public void testNavigateOptionPopUpToInAction() { 283 NavController navController = createNavController(); 284 navController.setGraph(R.navigation.nav_simple); 285 navController.navigate(R.id.second_test); 286 assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test)); 287 TestNavigator navigator = navController.getNavigatorProvider() 288 .getNavigator(TestNavigator.class); 289 assertThat(navigator.mBackStack.size(), is(2)); 290 291 navController.navigate(R.id.finish); 292 assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test)); 293 assertThat(navigator.mBackStack.size(), is(1)); 294 } 295 296 @Test 297 public void testNavigateWithPopUpOptionsOnly() { 298 NavController navController = createNavController(); 299 navController.setGraph(R.navigation.nav_simple); 300 navController.navigate(R.id.second_test); 301 assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test)); 302 TestNavigator navigator = navController.getNavigatorProvider() 303 .getNavigator(TestNavigator.class); 304 assertThat(navigator.mBackStack.size(), is(2)); 305 306 NavOptions navOptions = new NavOptions.Builder().setPopUpTo(R.id.start_test, false).build(); 307 // the same as to call .navigate(R.id.finish) 308 navController.navigate(0, null, navOptions); 309 310 assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test)); 311 assertThat(navigator.mBackStack.size(), is(1)); 312 } 313 314 @Test 315 public void testNoDestinationNoPopUpTo() { 316 NavController navController = createNavController(); 317 navController.setGraph(R.navigation.nav_simple); 318 NavOptions options = new NavOptions.Builder().build(); 319 try { 320 navController.navigate(0, null, options); 321 Assert.fail("navController.navigate must throw"); 322 } catch (IllegalArgumentException e) { 323 // expected exception 324 } 325 } 326 327 @Test 328 public void testNavigateOptionPopSelf() { 329 NavController navController = createNavController(); 330 navController.setGraph(R.navigation.nav_simple); 331 navController.navigate(R.id.second_test); 332 assertThat(navController.getCurrentDestination().getId(), is(R.id.second_test)); 333 TestNavigator navigator = navController.getNavigatorProvider() 334 .getNavigator(TestNavigator.class); 335 assertThat(navigator.mBackStack.size(), is(2)); 336 337 navController.navigate(R.id.finish_self); 338 assertThat(navController.getCurrentDestination().getId(), is(R.id.start_test)); 339 assertThat(navigator.mBackStack.size(), is(1)); 340 } 341 342 @Test 343 public void testNavigateViaActionWithArgs() { 344 NavController navController = createNavController(); 345 navController.setGraph(R.navigation.nav_arguments); 346 347 Bundle args = new Bundle(); 348 args.putString(TEST_ARG, TEST_ARG_VALUE); 349 args.putString(TEST_OVERRIDDEN_VALUE_ARG, TEST_OVERRIDDEN_VALUE_ARG_VALUE); 350 navController.navigate(R.id.second, args); 351 352 TestNavigator navigator = navController.getNavigatorProvider() 353 .getNavigator(TestNavigator.class); 354 Bundle returnedArgs = navigator.mBackStack.peekLast().second; 355 assertThat(returnedArgs, is(notNullValue(Bundle.class))); 356 357 // Test that arguments without a default value aren't passed through at all 358 assertThat(returnedArgs.containsKey("test_no_default_value"), is(false)); 359 // Test that default values are passed through 360 assertThat(returnedArgs.getString("test_default_value"), is("default")); 361 // Test that programmatically constructed arguments are passed through 362 assertThat(returnedArgs.getString(TEST_ARG), is(TEST_ARG_VALUE)); 363 // Test that default values can be overridden by programmatic values 364 assertThat(returnedArgs.getString(TEST_OVERRIDDEN_VALUE_ARG), 365 is(TEST_OVERRIDDEN_VALUE_ARG_VALUE)); 366 } 367 368 @Test 369 public void testDeepLinkFromNavGraph() { 370 NavController navController = createNavController(); 371 navController.setGraph(R.navigation.nav_simple); 372 373 TaskStackBuilder taskStackBuilder = navController.createDeepLink() 374 .setDestination(R.id.second_test) 375 .createTaskStackBuilder(); 376 assertThat(taskStackBuilder, is(notNullValue(TaskStackBuilder.class))); 377 assertThat(taskStackBuilder.getIntentCount(), is(1)); 378 } 379 380 @Test 381 public void testDeepLinkIntent() { 382 NavController navController = createNavController(); 383 navController.setGraph(R.navigation.nav_simple); 384 385 Bundle args = new Bundle(); 386 args.putString("test", "test"); 387 TaskStackBuilder taskStackBuilder = navController.createDeepLink() 388 .setDestination(R.id.second_test) 389 .setArguments(args) 390 .createTaskStackBuilder(); 391 392 Intent intent = taskStackBuilder.editIntentAt(0); 393 assertThat(intent, is(notNullValue())); 394 navController.onHandleDeepLink(intent); 395 396 // The original Intent should be untouched and safely writable to a Parcel 397 Parcel p = Parcel.obtain(); 398 intent.writeToParcel(p, 0); 399 } 400 401 private NavController createNavController() { 402 NavController navController = new NavController(InstrumentationRegistry.getTargetContext()); 403 TestNavigator navigator = new TestNavigator(); 404 navController.getNavigatorProvider().addNavigator(navigator); 405 return navController; 406 } 407} 408