mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 874985 - Part 3: Session restore tests. r=gbrown
This commit is contained in:
parent
48bffd0f06
commit
f1d4235ba7
@ -25,14 +25,18 @@ import android.util.DisplayMetrics;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@ -110,6 +114,17 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
|
||||
String rootPath = FennecInstrumentationTestRunner.getFennecArguments().getString("deviceroot");
|
||||
String configFile = FennecNativeDriver.getFile(rootPath + "/robotium.config");
|
||||
HashMap config = FennecNativeDriver.convertTextToTable(configFile);
|
||||
mLogFile = (String)config.get("logfile");
|
||||
mBaseUrl = ((String)config.get("host")).replaceAll("(/$)", "");
|
||||
mRawBaseUrl = ((String)config.get("rawhost")).replaceAll("(/$)", "");
|
||||
// Initialize the asserter
|
||||
if (getTestType() == TEST_TALOS) {
|
||||
mAsserter = new FennecTalosAssert();
|
||||
} else {
|
||||
mAsserter = new FennecMochitestAssert();
|
||||
}
|
||||
mAsserter.setLogFile(mLogFile);
|
||||
mAsserter.setTestName(this.getClass().getName());
|
||||
// Create the intent to be used with all the important arguments.
|
||||
Intent i = new Intent(Intent.ACTION_MAIN);
|
||||
mProfile = (String)config.get("profile");
|
||||
@ -124,17 +139,6 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
|
||||
// Start the activity
|
||||
setActivityIntent(i);
|
||||
mActivity = getActivity();
|
||||
mLogFile = (String)config.get("logfile");
|
||||
mBaseUrl = ((String)config.get("host")).replaceAll("(/$)", "");
|
||||
mRawBaseUrl = ((String)config.get("rawhost")).replaceAll("(/$)", "");
|
||||
// Initialize the asserter
|
||||
if (getTestType() == TEST_TALOS) {
|
||||
mAsserter = new FennecTalosAssert();
|
||||
} else {
|
||||
mAsserter = new FennecMochitestAssert();
|
||||
}
|
||||
mAsserter.setLogFile(mLogFile);
|
||||
mAsserter.setTestName(this.getClass().getName());
|
||||
// Set up Robotium.solo and Driver objects
|
||||
mSolo = new Solo(getInstrumentation(), mActivity);
|
||||
mDriver = new FennecNativeDriver(mActivity, mSolo, rootPath);
|
||||
@ -548,7 +552,7 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
|
||||
imm.toggleSoftInput(InputMethodManager.HIDE_IMPLICIT_ONLY, 0);
|
||||
}
|
||||
|
||||
public void addTab(String url) {
|
||||
public void addTab() {
|
||||
mSolo.clickOnView(mSolo.getView("tabs"));
|
||||
// wait for addTab to appear (this is usually immediate)
|
||||
boolean success = waitForCondition(new Condition() {
|
||||
@ -564,11 +568,92 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
|
||||
mAsserter.ok(success, "waiting for add tab view", "add tab view available");
|
||||
final View addTabView = mSolo.getView("add_tab");
|
||||
mSolo.clickOnView(mSolo.getView("add_tab"));
|
||||
}
|
||||
|
||||
public void addTab(String url) {
|
||||
addTab();
|
||||
|
||||
// Adding a new tab opens about:home, so now we just need to load the url in it.
|
||||
inputAndLoadUrl(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the AdapterView of the tabs list.
|
||||
*
|
||||
* @return List view in the tabs tray
|
||||
*/
|
||||
private final AdapterView<ListAdapter> getTabsList() {
|
||||
Element tabs = mDriver.findElement(getActivity(), "tabs");
|
||||
tabs.click();
|
||||
Element listElem = mDriver.findElement(getActivity(), "normal_tabs");
|
||||
int listId = listElem.getId();
|
||||
return (AdapterView<ListAdapter>) getActivity().findViewById(listId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the view in the tabs tray at the specified index.
|
||||
*
|
||||
* @return View at index
|
||||
*/
|
||||
private View getTabViewAt(final int index) {
|
||||
final View[] childView = { null };
|
||||
|
||||
final AdapterView<ListAdapter> view = getTabsList();
|
||||
|
||||
runOnUiThreadSync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
view.setSelection(index);
|
||||
|
||||
// The selection isn't updated synchronously; posting a
|
||||
// runnable to the view's queue guarantees we'll run after the
|
||||
// layout pass.
|
||||
view.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// getChildAt() is relative to the list of visible
|
||||
// views, but our index is relative to all views in the
|
||||
// list. Subtract the first visible list position for
|
||||
// the correct offset.
|
||||
childView[0] = view.getChildAt(index - view.getFirstVisiblePosition());
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
boolean result = waitForCondition(new Condition() {
|
||||
@Override
|
||||
public boolean isSatisfied() {
|
||||
return childView[0] != null;
|
||||
}
|
||||
}, MAX_WAIT_MS);
|
||||
|
||||
mAsserter.ok(result, "list item at index " + index + " exists", null);
|
||||
|
||||
return childView[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the tab at the specified index.
|
||||
*
|
||||
* @param index Index of tab to select
|
||||
*/
|
||||
public void selectTabAt(final int index) {
|
||||
mSolo.clickOnView(getTabViewAt(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the tab at the specified index.
|
||||
*
|
||||
* @param index Index of tab to close
|
||||
*/
|
||||
public void closeTabAt(final int index) {
|
||||
Element close = mDriver.findElement(getActivity(), "close");
|
||||
View closeButton = getTabViewAt(index).findViewById(close.getId());
|
||||
|
||||
mSolo.clickOnView(closeButton);
|
||||
}
|
||||
|
||||
public final void runOnUiThreadSync(Runnable runnable) {
|
||||
RobocopUtils.runOnUiThreadSync(mActivity, runnable);
|
||||
}
|
||||
@ -752,4 +837,16 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2<Activity> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string representation of a stack trace.
|
||||
*
|
||||
* @param t Throwable to get stack trace for
|
||||
* @return Stack trace as a string
|
||||
*/
|
||||
public static String getStackTraceString(Throwable t) {
|
||||
StringWriter sw = new StringWriter();
|
||||
t.printStackTrace(new PrintWriter(sw));
|
||||
return sw.toString();
|
||||
}
|
||||
}
|
||||
|
404
mobile/android/base/tests/SessionTest.java
Normal file
404
mobile/android/base/tests/SessionTest.java
Normal file
@ -0,0 +1,404 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public abstract class SessionTest extends BaseTest {
|
||||
private File mSessionDir;
|
||||
protected Navigation mNavigation;
|
||||
|
||||
@Override
|
||||
final protected int getTestType() {
|
||||
return TEST_MOCHITEST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
mNavigation = new Navigation(mDevice);
|
||||
}
|
||||
|
||||
/**
|
||||
* A generic session object representing a collection of items that has a
|
||||
* selected index.
|
||||
*/
|
||||
protected abstract class SessionObject<T> {
|
||||
private final int mIndex;
|
||||
private final T[] mItems;
|
||||
|
||||
public SessionObject(int index, T... items) {
|
||||
mIndex = index;
|
||||
mItems = items;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return mIndex;
|
||||
}
|
||||
|
||||
public T[] getItems() {
|
||||
return mItems;
|
||||
}
|
||||
}
|
||||
|
||||
protected class PageInfo {
|
||||
private String url;
|
||||
private String title;
|
||||
|
||||
public PageInfo(String key) {
|
||||
if (key.startsWith("about:")) {
|
||||
url = key;
|
||||
} else {
|
||||
url = getPage(key);
|
||||
}
|
||||
title = key;
|
||||
}
|
||||
}
|
||||
|
||||
protected class SessionTab extends SessionObject<PageInfo> {
|
||||
public SessionTab(int index, PageInfo... items) {
|
||||
super(index, items);
|
||||
}
|
||||
}
|
||||
|
||||
protected class Session extends SessionObject<SessionTab> {
|
||||
public Session(int index, SessionTab... items) {
|
||||
super(index, items);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Walker for visiting items in a browser-like navigation order.
|
||||
*/
|
||||
protected abstract class NavigationWalker<T> {
|
||||
private final T[] mItems;
|
||||
private final int mIndex;
|
||||
|
||||
public NavigationWalker(SessionObject<T> obj) {
|
||||
mItems = obj.getItems();
|
||||
mIndex = obj.getIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks over the list of items, calling the onItem() callback for each.
|
||||
*
|
||||
* The selected item is the first item visited. Each item after the
|
||||
* selected item is then visited in ascending index order. Finally, the
|
||||
* list is iterated in reverse, and each item before the selected item
|
||||
* is visited in descending index order.
|
||||
*/
|
||||
public void walk() {
|
||||
onItem(mItems[mIndex], mIndex);
|
||||
for (int i = mIndex + 1; i < mItems.length; i++) {
|
||||
goForward();
|
||||
onItem(mItems[i], i);
|
||||
}
|
||||
if (mIndex > 0) {
|
||||
for (int i = mItems.length - 2; i >= 0; i--) {
|
||||
goBack();
|
||||
if (i < mIndex) {
|
||||
onItem(mItems[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when an item is visited during a walk.
|
||||
*
|
||||
* Only one callback is executed per item.
|
||||
*/
|
||||
public abstract void onItem(T item, int currentIndex);
|
||||
|
||||
/**
|
||||
* Callback executed for each back step of the walk.
|
||||
*/
|
||||
public void goBack() {}
|
||||
|
||||
/**
|
||||
* Callback executed for each forward step of the walk.
|
||||
*/
|
||||
public void goForward() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a set of tabs in the browser specified by the given session.
|
||||
*
|
||||
* @param session Session to load
|
||||
*/
|
||||
protected void loadSessionTabs(Session session) {
|
||||
// Verify initial about:home tab
|
||||
verifyTabCount(1);
|
||||
verifyUrl("about:home");
|
||||
|
||||
SessionTab[] tabs = session.getItems();
|
||||
for (int i = 0; i < tabs.length; i++) {
|
||||
final SessionTab tab = tabs[i];
|
||||
final PageInfo[] pages = tab.getItems();
|
||||
|
||||
// New tabs always start with about:home, so make sure about:home
|
||||
// is always the first entry.
|
||||
mAsserter.is(pages[0].url, "about:home", "first page in tab is about:home");
|
||||
|
||||
// If this is the first tab, the tab already exists, so no need to
|
||||
// create a new one. Otherwise, create a new tab if we're loading
|
||||
// the first the first page in the set.
|
||||
if (i > 0) {
|
||||
Actions.EventExpecter pageShowExpecter = mActions.expectGeckoEvent("Content:PageShow");
|
||||
addTab();
|
||||
pageShowExpecter.blockForEvent();
|
||||
pageShowExpecter.unregisterListener();
|
||||
}
|
||||
|
||||
for (int j = 1; j < pages.length; j++) {
|
||||
Actions.EventExpecter pageShowExpecter = mActions.expectGeckoEvent("Content:PageShow");
|
||||
|
||||
loadUrl(pages[j].url);
|
||||
|
||||
pageShowExpecter.blockForEvent();
|
||||
pageShowExpecter.unregisterListener();
|
||||
}
|
||||
|
||||
final int index = tab.getIndex();
|
||||
for (int j = pages.length - 1; j > index; j--) {
|
||||
mNavigation.back();
|
||||
}
|
||||
}
|
||||
|
||||
selectTabAt(session.getIndex());
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the set of open tabs matches the given session.
|
||||
*
|
||||
* @param session Session to verify
|
||||
*/
|
||||
protected void verifySessionTabs(Session session) {
|
||||
verifyTabCount(session.getItems().length);
|
||||
|
||||
(new NavigationWalker<SessionTab>(session) {
|
||||
boolean mFirstTabVisited;
|
||||
|
||||
@Override
|
||||
public void onItem(SessionTab tab, int currentIndex) {
|
||||
// The first tab to check should already be selected at startup
|
||||
if (mFirstTabVisited) {
|
||||
selectTabAt(currentIndex);
|
||||
} else {
|
||||
mFirstTabVisited = true;
|
||||
}
|
||||
|
||||
(new NavigationWalker<PageInfo>(tab) {
|
||||
@Override
|
||||
public void onItem(PageInfo page, int currentIndex) {
|
||||
if (page.url.equals("about:home")) {
|
||||
waitForText("Enter Search or Address");
|
||||
verifyUrl(page.url);
|
||||
} else {
|
||||
waitForText(page.title);
|
||||
verifyPageTitle(page.title);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goBack() {
|
||||
mNavigation.back();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void goForward() {
|
||||
mNavigation.forward();
|
||||
}
|
||||
}).walk();
|
||||
}
|
||||
}).walk();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets session restore JSON corresponding to the open session.
|
||||
*
|
||||
* The JSON format follows the format used in Gecko for session restore and
|
||||
* should be interchangeable with the Gecko's generated sessionstore.js.
|
||||
*
|
||||
* @param session Session to serialize
|
||||
* @return JSON string of session
|
||||
*/
|
||||
protected String buildSessionJSON(Session session) {
|
||||
final SessionTab[] sessionTabs = session.getItems();
|
||||
String sessionString = null;
|
||||
|
||||
try {
|
||||
final JSONArray tabs = new JSONArray();
|
||||
|
||||
for (int i = 0; i < sessionTabs.length; i++) {
|
||||
final JSONObject tab = new JSONObject();
|
||||
final JSONArray entries = new JSONArray();
|
||||
final SessionTab sessionTab = sessionTabs[i];
|
||||
final PageInfo[] pages = sessionTab.getItems();
|
||||
|
||||
for (int j = 0; j < pages.length; j++) {
|
||||
final PageInfo page = pages[j];
|
||||
final JSONObject entry = new JSONObject();
|
||||
entry.put("url", page.url);
|
||||
entry.put("title", page.title);
|
||||
entries.put(entry);
|
||||
}
|
||||
|
||||
tab.put("entries", entries);
|
||||
tab.put("index", sessionTab.getIndex() + 1);
|
||||
tabs.put(tab);
|
||||
}
|
||||
|
||||
JSONObject window = new JSONObject();
|
||||
window.put("tabs", tabs);
|
||||
window.put("selected", session.getIndex() + 1);
|
||||
sessionString = new JSONObject().put("windows", new JSONArray().put(window)).toString();
|
||||
} catch (JSONException e) {
|
||||
mAsserter.ok(false, "JSON exception", getStackTraceString(e));
|
||||
}
|
||||
|
||||
return sessionString;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see SessionTest#verifySessionJSON(Session, String, Assert)
|
||||
*/
|
||||
protected void verifySessionJSON(Session session, String sessionString) {
|
||||
verifySessionJSON(session, sessionString, mAsserter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies a session JSON string against the given session.
|
||||
*
|
||||
* @param session Session to verify against
|
||||
* @param sessionString JSON string to verify
|
||||
* @param asserter Assert class to use during verification
|
||||
*/
|
||||
protected void verifySessionJSON(Session session, String sessionString, Assert asserter) {
|
||||
final SessionTab[] sessionTabs = session.getItems();
|
||||
|
||||
try {
|
||||
final JSONObject window = new JSONObject(sessionString).getJSONArray("windows").getJSONObject(0);
|
||||
final JSONArray tabs = window.getJSONArray("tabs");
|
||||
final int optSelected = window.optInt("selected", -1);
|
||||
|
||||
asserter.is(optSelected, session.getIndex() + 1, "selected tab matches");
|
||||
|
||||
for (int i = 0; i < tabs.length(); i++) {
|
||||
final JSONObject tab = tabs.getJSONObject(i);
|
||||
final int index = tab.getInt("index");
|
||||
final JSONArray entries = tab.getJSONArray("entries");
|
||||
final SessionTab sessionTab = sessionTabs[i];
|
||||
final PageInfo[] pages = sessionTab.getItems();
|
||||
|
||||
asserter.is(index, sessionTab.getIndex() + 1, "selected page index matches");
|
||||
|
||||
for (int j = 0; j < entries.length(); j++) {
|
||||
final JSONObject entry = entries.getJSONObject(j);
|
||||
final String url = entry.getString("url");
|
||||
final String title = entry.optString("title");
|
||||
final PageInfo page = pages[j];
|
||||
|
||||
asserter.is(url, page.url, "URL in JSON matches session URL");
|
||||
if (!page.url.startsWith("about:")) {
|
||||
asserter.is(title, page.title, "title in JSON matches session title");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
asserter.ok(false, "JSON exception", getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown by NonFatalAsserter for assertion failures.
|
||||
*/
|
||||
public static class AssertException extends RuntimeException {
|
||||
public AssertException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserter that throws an AssertException on failure instead of aborting
|
||||
* the test.
|
||||
*
|
||||
* This can be used in methods called via waitForCondition() where an assertion
|
||||
* might not immediately succeed.
|
||||
*/
|
||||
public class NonFatalAsserter extends FennecMochitestAssert {
|
||||
@Override
|
||||
public void ok(boolean condition, String name, String diag) {
|
||||
if (!condition) {
|
||||
String details = (diag == null ? "" : " | " + diag);
|
||||
throw new AssertException("Assertion failed: " + name + details);
|
||||
}
|
||||
mAsserter.ok(condition, name, diag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a URL for a dynamically-generated page.
|
||||
*
|
||||
* The page will have a URL unique to the given ID, and the page's title
|
||||
* will match the given ID.
|
||||
*
|
||||
* @param id ID used to generate page URL
|
||||
* @return URL of the page
|
||||
*/
|
||||
protected String getPage(String id) {
|
||||
return getAbsoluteUrl("/robocop/robocop_dynamic.sjs?id=" + id);
|
||||
}
|
||||
|
||||
protected String readProfileFile(String filename) {
|
||||
try {
|
||||
return readFile(new File(mProfile, filename));
|
||||
} catch (IOException e) {
|
||||
mAsserter.ok(false, "Error reading" + filename, getStackTraceString(e));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void writeProfileFile(String filename, String data) {
|
||||
try {
|
||||
writeFile(new File(mProfile, filename), data);
|
||||
} catch (IOException e) {
|
||||
mAsserter.ok(false, "Error writing to " + filename, getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
|
||||
private String readFile(File target) throws IOException {
|
||||
FileReader fr = new FileReader(target);
|
||||
try {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
char[] buf = new char[8192];
|
||||
int read = fr.read(buf);
|
||||
while (read >= 0) {
|
||||
sb.append(buf, 0, read);
|
||||
read = fr.read(buf);
|
||||
}
|
||||
return sb.toString();
|
||||
} finally {
|
||||
fr.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeFile(File target, String data) throws IOException {
|
||||
FileWriter writer = new FileWriter(target);
|
||||
try {
|
||||
writer.write(data);
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -55,6 +55,8 @@ skip-if = processor == "x86"
|
||||
# disabled on x86 only; bug 936224
|
||||
# skip-if = processor == "x86"
|
||||
[testSearchSuggestions]
|
||||
[testSessionOOMSave]
|
||||
[testSessionOOMRestore]
|
||||
[testSettingsMenuItems]
|
||||
[testSharedPreferences]
|
||||
# [testShareLink] # see bug 915897
|
||||
|
18
mobile/android/base/tests/robocop_dynamic.sjs
Normal file
18
mobile/android/base/tests/robocop_dynamic.sjs
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Dynamically generated page whose title matches the given id.
|
||||
*/
|
||||
|
||||
function handleRequest(request, response) {
|
||||
let id = request.queryString.match(/^id=(.*)$/)[1];
|
||||
let key = "dynamic." + id;
|
||||
|
||||
response.setStatusLine(request.httpVersion, 200, null);
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
response.write('<html>');
|
||||
response.write('<head><title>' + id + '</title><meta charset="utf-8"></head>');
|
||||
response.write('<body>');
|
||||
response.write('<h1>' + id + '</h1>');
|
||||
response.write('</body>');
|
||||
response.write('</html>');
|
||||
}
|
56
mobile/android/base/tests/testSessionOOMRestore.java
Normal file
56
mobile/android/base/tests/testSessionOOMRestore.java
Normal file
@ -0,0 +1,56 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import org.mozilla.gecko.*;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Tests session OOM restore behavior.
|
||||
*
|
||||
* Loads a session and tests that it is restored correctly.
|
||||
*/
|
||||
public class testSessionOOMRestore extends SessionTest {
|
||||
private Session mSession;
|
||||
private static final String PREFS_NAME = "GeckoApp";
|
||||
private static final String PREFS_ALLOW_STATE_BUNDLE = "allowStateBundle";
|
||||
|
||||
@Override
|
||||
public void setActivityIntent(Intent intent) {
|
||||
PageInfo home = new PageInfo("about:home");
|
||||
PageInfo page1 = new PageInfo("page1");
|
||||
PageInfo page2 = new PageInfo("page2");
|
||||
PageInfo page3 = new PageInfo("page3");
|
||||
PageInfo page4 = new PageInfo("page4");
|
||||
PageInfo page5 = new PageInfo("page5");
|
||||
PageInfo page6 = new PageInfo("page6");
|
||||
|
||||
SessionTab tab1 = new SessionTab(0, home, page1, page2);
|
||||
SessionTab tab2 = new SessionTab(1, home, page3, page4);
|
||||
SessionTab tab3 = new SessionTab(2, home, page5, page6);
|
||||
|
||||
mSession = new Session(1, tab1, tab2, tab3);
|
||||
|
||||
String sessionString = buildSessionJSON(mSession);
|
||||
writeProfileFile("sessionstore.js", sessionString);
|
||||
|
||||
// This feature is pref-protected to prevent other apps from injecting
|
||||
// a state bundle, so enable it here.
|
||||
SharedPreferences prefs = getInstrumentation().getTargetContext()
|
||||
.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
|
||||
prefs.edit().putBoolean(PREFS_ALLOW_STATE_BUNDLE, true).commit();
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("privateSession", null);
|
||||
intent.putExtra("stateBundle", bundle);
|
||||
|
||||
super.setActivityIntent(intent);
|
||||
}
|
||||
|
||||
public void testSessionOOMRestore() throws Exception {
|
||||
blockForGeckoReady();
|
||||
verifySessionTabs(mSession);
|
||||
}
|
||||
}
|
83
mobile/android/base/tests/testSessionOOMSave.java
Normal file
83
mobile/android/base/tests/testSessionOOMSave.java
Normal file
@ -0,0 +1,83 @@
|
||||
package org.mozilla.gecko.tests;
|
||||
|
||||
import com.jayway.android.robotium.solo.Condition;
|
||||
import org.mozilla.gecko.*;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* Tests session OOM save behavior.
|
||||
*
|
||||
* Builds a session and tests that the saved state is correct.
|
||||
*/
|
||||
public class testSessionOOMSave extends SessionTest {
|
||||
private final static int SESSION_TIMEOUT = 25000;
|
||||
|
||||
public void testSessionOOMSave() {
|
||||
Actions.EventExpecter pageShowExpecter = mActions.expectGeckoEvent("Content:PageShow");
|
||||
pageShowExpecter.blockForEvent();
|
||||
pageShowExpecter.unregisterListener();
|
||||
|
||||
PageInfo home = new PageInfo("about:home");
|
||||
PageInfo page1 = new PageInfo("page1");
|
||||
PageInfo page2 = new PageInfo("page2");
|
||||
PageInfo page3 = new PageInfo("page3");
|
||||
PageInfo page4 = new PageInfo("page4");
|
||||
PageInfo page5 = new PageInfo("page5");
|
||||
PageInfo page6 = new PageInfo("page6");
|
||||
|
||||
SessionTab tab1 = new SessionTab(0, home, page1, page2);
|
||||
SessionTab tab2 = new SessionTab(1, home, page3, page4);
|
||||
SessionTab tab3 = new SessionTab(2, home, page5, page6);
|
||||
|
||||
final Session session = new Session(1, tab1, tab2, tab3);
|
||||
|
||||
// Load the tabs into the browser
|
||||
loadSessionTabs(session);
|
||||
|
||||
// Verify sessionstore.js written by Gecko. The session write is
|
||||
// delayed for certain interactions (such as changing the selected
|
||||
// tab), so the file is repeatedly read until it matches the expected
|
||||
// output. Because of the delay, this part of the test takes ~9 seconds
|
||||
// to pass.
|
||||
VerifyJSONCondition verifyJSONCondition = new VerifyJSONCondition(session);
|
||||
boolean success = waitForCondition(verifyJSONCondition, SESSION_TIMEOUT);
|
||||
if (success) {
|
||||
mAsserter.ok(true, "verified session JSON", null);
|
||||
} else {
|
||||
mAsserter.ok(false, "failed to verify session JSON",
|
||||
getStackTraceString(verifyJSONCondition.getLastException()));
|
||||
}
|
||||
}
|
||||
|
||||
private class VerifyJSONCondition implements Condition {
|
||||
private AssertException mLastException;
|
||||
private final NonFatalAsserter mAsserter = new NonFatalAsserter();
|
||||
private final Session mSession;
|
||||
|
||||
public VerifyJSONCondition(Session session) {
|
||||
mSession = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSatisfied() {
|
||||
try {
|
||||
String sessionString = readProfileFile("sessionstore.js");
|
||||
verifySessionJSON(mSession, sessionString, mAsserter);
|
||||
} catch (AssertException e) {
|
||||
mLastException = e;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last AssertException thrown by verifySessionJSON().
|
||||
*
|
||||
* This is useful to get the stack trace if the test fails.
|
||||
*/
|
||||
public AssertException getLastException() {
|
||||
return mLastException;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user