// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.android_webview.test;
import android.os.Bundle;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.UseParametersRunnerFactory;
import org.chromium.android_webview.AwContents;
import org.chromium.android_webview.test.util.CommonResources;
import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.Feature;
import org.chromium.content_public.browser.NavigationController;
import org.chromium.content_public.browser.NavigationHistory;
import org.chromium.net.test.util.TestWebServer;
/**
* Tests for the {@link android.webkit.WebView#saveState} and {@link
* android.webkit.WebView#restoreState} APIs.
*/
@RunWith(Parameterized.class)
@UseParametersRunnerFactory(AwJUnit4ClassRunnerWithParameters.Factory.class)
public class SaveRestoreStateTest extends AwParameterizedTest {
@Rule public AwActivityTestRule mActivityTestRule;
private static class TestVars {
public final TestAwContentsClient contentsClient;
public final AwTestContainerView testView;
public final AwContents awContents;
public final NavigationController navigationController;
public TestVars(TestAwContentsClient contentsClient, AwTestContainerView testView) {
this.contentsClient = contentsClient;
this.testView = testView;
this.awContents = testView.getAwContents();
this.navigationController = this.awContents.getNavigationController();
}
}
private TestVars createNewView() {
TestAwContentsClient contentsClient = new TestAwContentsClient();
AwTestContainerView testView =
mActivityTestRule.createAwTestContainerViewOnMainSync(contentsClient);
return new TestVars(contentsClient, testView);
}
private TestVars mVars;
private TestWebServer mWebServer;
private static final int NUM_NAVIGATIONS = 3;
private static final String TITLES[] = {
"page 1 title foo", "page 2 title bar", "page 3 title baz"
};
private static final String PATHS[] = {
"/p1foo.html", "/p2bar.html", "/p3baz.html",
};
private String mUrls[];
public SaveRestoreStateTest(AwSettingsMutation param) {
this.mActivityTestRule = new AwActivityTestRule(param.getMutation());
}
@Before
public void setUp() throws Exception {
mVars = createNewView();
mUrls = new String[NUM_NAVIGATIONS];
mWebServer = TestWebServer.start();
}
@After
public void tearDown() {
if (mWebServer != null) {
mWebServer.shutdown();
}
}
private void setServerResponseAndLoad(TestVars vars, int upto) throws Throwable {
for (int i = 0; i < upto; ++i) {
String html = CommonResources.makeHtmlPageFrom("<title>" + TITLES[i] + "</title>", "");
mUrls[i] = mWebServer.setResponse(PATHS[i], html, null);
mActivityTestRule.loadUrlSync(
vars.awContents, vars.contentsClient.getOnPageFinishedHelper(), mUrls[i]);
}
}
private NavigationHistory getNavigationHistoryOnUiThread(final TestVars vars) throws Throwable {
return ThreadUtils.runOnUiThreadBlocking(
() -> vars.navigationController.getNavigationHistory());
}
private void checkHistoryItemList(TestVars vars) throws Throwable {
NavigationHistory history = getNavigationHistoryOnUiThread(vars);
Assert.assertEquals(NUM_NAVIGATIONS, history.getEntryCount());
Assert.assertEquals(NUM_NAVIGATIONS - 1, history.getCurrentEntryIndex());
// Note this is not meant to be a thorough test of NavigationHistory,
// but is only meant to test enough to make sure state is restored.
// See NavigationHistoryTest for more thorough tests.
for (int i = 0; i < NUM_NAVIGATIONS; ++i) {
Assert.assertEquals(mUrls[i], history.getEntryAtIndex(i).getOriginalUrl().getSpec());
Assert.assertEquals(mUrls[i], history.getEntryAtIndex(i).getUrl().getSpec());
Assert.assertEquals(TITLES[i], history.getEntryAtIndex(i).getTitle());
}
}
private TestVars saveAndRestoreStateOnUiThread(final TestVars vars) {
final TestVars restoredVars = createNewView();
InstrumentationRegistry.getInstrumentation()
.runOnMainSync(
() -> {
Bundle bundle = new Bundle();
boolean result = vars.awContents.saveState(bundle);
Assert.assertTrue(result);
result = restoredVars.awContents.restoreState(bundle);
Assert.assertTrue(result);
});
return restoredVars;
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSaveRestoreStateWithTitle() throws Throwable {
setServerResponseAndLoad(mVars, 1);
final TestVars restoredVars = saveAndRestoreStateOnUiThread(mVars);
mActivityTestRule.pollUiThread(
() ->
TITLES[0].equals(restoredVars.awContents.getTitle())
&& TITLES[0].equals(restoredVars.contentsClient.getUpdatedTitle()));
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSaveRestoreStateWithHistoryItemList() throws Throwable {
setServerResponseAndLoad(mVars, NUM_NAVIGATIONS);
TestVars restoredVars = saveAndRestoreStateOnUiThread(mVars);
checkHistoryItemList(restoredVars);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testRestoreFromInvalidStateFails() throws Throwable {
final Bundle invalidState = new Bundle();
invalidState.putByteArray(AwContents.SAVE_RESTORE_STATE_KEY, "invalid state".getBytes());
boolean result =
ThreadUtils.runOnUiThreadBlocking(
() -> mVars.awContents.restoreState(invalidState));
Assert.assertFalse(result);
}
@Test
@SmallTest
@Feature({"AndroidWebView"})
public void testSaveStateForNoNavigationFails() throws Throwable {
final Bundle state = new Bundle();
boolean result =
ThreadUtils.runOnUiThreadBlocking(() -> mVars.awContents.restoreState(state));
Assert.assertFalse(result);
}
}