/*
* Copyright 2017 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.navigation.fragment;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.NavigationRes;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.rule.ActivityTestRule;
import android.support.v4.app.TaskStackBuilder;
import androidx.navigation.NavController;
import androidx.navigation.NavDeepLinkBuilder;
import androidx.navigation.fragment.test.BaseNavigationActivity;
import androidx.navigation.fragment.test.R;
import androidx.navigation.testing.TestNavigator;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@SmallTest
public abstract class BaseNavControllerTest {
private static final String TEST_ARG = "test";
private static final String TEST_ARG_VALUE = "value";
private static final String TEST_DEEP_LINK_ACTION = "deep_link";
/**
* @return The concrete Activity class under test
*/
protected abstract Class getActivityClass();
@Rule
public ActivityTestRule mActivityRule =
new ActivityTestRule<>(getActivityClass(), false, false);
private Instrumentation mInstrumentation;
@Before
public void getInstrumentation() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
}
@Test
public void testStartDestinationDeeplink() throws Throwable {
assertDeeplink(R.id.start_test, 1);
}
@Test
public void testDeeplink() throws Throwable {
assertDeeplink(R.id.deep_link_test, 2);
}
@Test
public void testNestedStartDestinationDeeplink() throws Throwable {
assertDeeplink(R.id.nested_start_test, 2);
}
@Test
public void testNestedDeeplink() throws Throwable {
assertDeeplink(R.id.nested_deep_link_test, 3);
}
@Test
public void testDoubleNestedStartDestinationDeeplink() throws Throwable {
assertDeeplink(R.id.double_nested_start_test, 2);
}
@Test
public void testDoubleNestedDeeplink() throws Throwable {
assertDeeplink(R.id.double_nested_deep_link_test, 3);
}
private void assertDeeplink(@IdRes int destId, int expectedStackSize) throws Throwable {
BaseNavigationActivity activity = launchDeepLink(R.navigation.nav_deep_link,
destId, null);
NavController navController = activity.getNavController();
assertThat(navController.getCurrentDestination().getId(), is(destId));
TestNavigator navigator = navController.getNavigatorProvider()
.getNavigator(TestNavigator.class);
assertThat(navigator.mBackStack.size(), is(expectedStackSize));
// Test that the deep link Intent was passed through even though we don't pass in any args
//noinspection ConstantConditions
Intent deepLinkIntent = navigator.mBackStack.peekLast().second
.getParcelable(NavController.KEY_DEEP_LINK_INTENT);
assertThat(deepLinkIntent, is(notNullValue(Intent.class)));
assertThat(deepLinkIntent.getAction(), is(TEST_DEEP_LINK_ACTION));
}
@Test
public void testStartDestinationDeeplinkWithArgs() throws Throwable {
assertDeepLinkWithArgs(R.id.start_test, 1);
}
@Test
public void testDeeplinkWithArgs() throws Throwable {
assertDeepLinkWithArgs(R.id.deep_link_test, 2);
}
@Test
public void testNestedStartDestinationDeeplinkWithArgs() throws Throwable {
assertDeepLinkWithArgs(R.id.nested_start_test, 2);
}
@Test
public void testNestedDeeplinkWithArgs() throws Throwable {
assertDeepLinkWithArgs(R.id.nested_deep_link_test, 3);
}
@Test
public void testDoubleNestedStartDestinationDeeplinkWithArgs() throws Throwable {
assertDeepLinkWithArgs(R.id.double_nested_start_test, 2);
}
@Test
public void testDoubleNestedDeeplinkWithArgs() throws Throwable {
assertDeepLinkWithArgs(R.id.double_nested_deep_link_test, 3);
}
private void assertDeepLinkWithArgs(@IdRes int destId, int expectedStackSize) throws Throwable {
Bundle args = new Bundle();
args.putString(TEST_ARG, TEST_ARG_VALUE);
BaseNavigationActivity activity = launchDeepLink(R.navigation.nav_deep_link,
destId, args);
NavController navController = activity.getNavController();
assertThat(navController.getCurrentDestination().getId(), is(destId));
TestNavigator navigator = navController.getNavigatorProvider()
.getNavigator(TestNavigator.class);
assertThat(navigator.mBackStack.size(), is(expectedStackSize));
//noinspection ConstantConditions
assertThat(navigator.mBackStack.peekLast().second.getString(TEST_ARG), is(TEST_ARG_VALUE));
// Test that the deep link Intent was passed in alongside our args
//noinspection ConstantConditions
Intent deepLinkIntent = navigator.mBackStack.peekLast().second
.getParcelable(NavController.KEY_DEEP_LINK_INTENT);
assertThat(deepLinkIntent, is(notNullValue(Intent.class)));
assertThat(deepLinkIntent.getAction(), is(TEST_DEEP_LINK_ACTION));
}
@Test
public void testStartDestinationUriDeepLink() throws Throwable {
assertUriDeepLink("start",
R.id.start_test, 1);
}
@Test
public void testUriDeepLink() throws Throwable {
assertUriDeepLink("deep_link", R.id.deep_link_test, 2);
}
@Test
public void testNestedStartDestinationUriDeepLink() throws Throwable {
assertUriDeepLink("nested_start", R.id.nested_start_test, 2);
}
@Test
public void testNestedUriDeepLink() throws Throwable {
assertUriDeepLink("nested_deep_link", R.id.nested_deep_link_test, 3);
}
@Test
public void testDoubleNestedStartDestinationUriDeepLink() throws Throwable {
assertUriDeepLink("double_nested_start", R.id.double_nested_start_test, 2);
}
@Test
public void testDoubleNestedUriDeepLink() throws Throwable {
assertUriDeepLink("double_nested_deep_link", R.id.double_nested_deep_link_test, 3);
}
private void assertUriDeepLink(String path, @IdRes int destId, int expectedStackSize)
throws Throwable {
Uri deepLinkUri = Uri.parse("http://www.example.com/" + path + "/" + TEST_ARG_VALUE);
Intent intent = new Intent(Intent.ACTION_VIEW, deepLinkUri)
.setComponent(new ComponentName(mInstrumentation.getContext(),
getActivityClass()))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
BaseNavigationActivity activity = launchActivity(intent);
NavController navController = activity.getNavController();
navController.setGraph(R.navigation.nav_deep_link);
assertThat(navController.getCurrentDestination().getId(), is(destId));
TestNavigator navigator = navController.getNavigatorProvider()
.getNavigator(TestNavigator.class);
assertThat(navigator.mBackStack.size(), is(expectedStackSize));
//noinspection ConstantConditions
assertThat(navigator.mBackStack.peekLast().second.getString(TEST_ARG), is(TEST_ARG_VALUE));
// Test that the deep link Intent was passed in alongside our args
//noinspection ConstantConditions
Intent deepLinkIntent = navigator.mBackStack.peekLast().second
.getParcelable(NavController.KEY_DEEP_LINK_INTENT);
assertThat(deepLinkIntent, is(notNullValue(Intent.class)));
assertThat(deepLinkIntent.getData(), is(deepLinkUri));
}
private BaseNavigationActivity launchActivity(Intent intent) throws Throwable {
BaseNavigationActivity activity = mActivityRule.launchActivity(intent);
mInstrumentation.waitForIdleSync();
NavController navController = activity.getNavController();
assertThat(navController, is(notNullValue(NavController.class)));
TestNavigator navigator = new TestNavigator();
navController.getNavigatorProvider().addNavigator(navigator);
return activity;
}
private BaseNavigationActivity launchDeepLink(@NavigationRes int graphId, @IdRes int destId,
Bundle args) throws Throwable {
TaskStackBuilder intents = new NavDeepLinkBuilder(mInstrumentation.getTargetContext())
.setGraph(graphId)
.setDestination(destId)
.setArguments(args)
.createTaskStackBuilder();
Intent intent = intents.editIntentAt(0);
intent.setAction(TEST_DEEP_LINK_ACTION);
// Now launch the deeplink Intent
BaseNavigationActivity deeplinkActivity = launchActivity(intent);
NavController navController = deeplinkActivity.getNavController();
navController.setGraph(graphId);
return deeplinkActivity;
}
}