Bug 1286798 - Part 32: Add a test for snapshotting verification in multi-e10s setup; r=asuth

This commit is contained in:
Jan Varga 2018-11-29 21:48:57 +01:00
Родитель d87888fe25
Коммит 7941181395
5 изменённых файлов: 514 добавлений и 74 удалений

Просмотреть файл

@ -4,6 +4,7 @@ support-files =
browser_frame_elements.html
page_privatestorageevent.html
page_localstorage_e10s.html
page_localstorage_snapshotting_e10s.html
position.html
test-console-api.html
test_bug1004814.html
@ -16,6 +17,7 @@ support-files =
test_largeAllocation2.html^headers^
test_largeAllocationFormSubmit.sjs
helper_largeAllocation.js
helper_localStorage_e10s.js
!/dom/tests/mochitest/geolocation/network_geolocation.sjs
[browser_allocateGigabyte.js]
@ -56,6 +58,8 @@ skip-if = !e10s || (os == "win" && processor == "x86") || (verify && debug && (o
[browser_localStorage_e10s.js]
skip-if = !e10s || verify # This is a test of e10s functionality.
[browser_localStorage_privatestorageevent.js]
[browser_localStorage_snapshotting_e10s.js]
skip-if = !e10s # This is a test of e10s functionality.
[browser_persist_cookies.js]
support-files =
set-samesite-cookies-and-redirect.sjs

Просмотреть файл

@ -2,74 +2,9 @@ const HELPER_PAGE_URL =
"http://example.com/browser/dom/tests/browser/page_localstorage_e10s.html";
const HELPER_PAGE_ORIGIN = "http://example.com/";
// Simple tab wrapper abstracting our messaging mechanism;
class KnownTab {
constructor(name, tab) {
this.name = name;
this.tab = tab;
}
cleanup() {
this.tab = null;
}
}
// Simple data structure class to help us track opened tabs and their pids.
class KnownTabs {
constructor() {
this.byPid = new Map();
this.byName = new Map();
}
cleanup() {
this.byPid = null;
this.byName = null;
}
}
/**
* Open our helper page in a tab in its own content process, asserting that it
* really is in its own process. We initially load and wait for about:blank to
* load, and only then loadURI to our actual page. This is to ensure that
* LocalStorageManager has had an opportunity to be created and populate
* mOriginsHavingData.
*
* (nsGlobalWindow will reliably create LocalStorageManager as a side-effect of
* the unconditional call to nsGlobalWindow::PreloadLocalStorage. This will
* reliably create the StorageDBChild instance, and its corresponding
* StorageDBParent will send the set of origins when it is constructed.)
*/
async function openTestTabInOwnProcess(name, knownTabs) {
let realUrl = HELPER_PAGE_URL + '?' + encodeURIComponent(name);
// Load and wait for about:blank.
let tab = await BrowserTestUtils.openNewForegroundTab({
gBrowser, opening: 'about:blank', forceNewProcess: true
});
let pid = tab.linkedBrowser.frameLoader.tabParent.osPid;
ok(!knownTabs.byName.has(name), "tab needs its own name: " + name);
ok(!knownTabs.byPid.has(pid), "tab needs to be in its own process: " + pid);
let knownTab = new KnownTab(name, tab);
knownTabs.byPid.set(pid, knownTab);
knownTabs.byName.set(name, knownTab);
// Now trigger the actual load of our page.
BrowserTestUtils.loadURI(tab.linkedBrowser, realUrl);
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
is(tab.linkedBrowser.frameLoader.tabParent.osPid, pid, "still same pid");
return knownTab;
}
/**
* Close all the tabs we opened.
*/
async function cleanupTabs(knownTabs) {
for (let knownTab of knownTabs.byName.values()) {
BrowserTestUtils.removeTab(knownTab.tab);
knownTab.cleanup();
}
knownTabs.cleanup();
}
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "/helper_localStorage_e10s.js",
this);
/**
* Wait for a LocalStorage flush to occur. This notification can occur as a
@ -340,10 +275,13 @@ add_task(async function() {
// - Open tabs. Don't configure any of them yet.
const knownTabs = new KnownTabs();
const writerTab = await openTestTabInOwnProcess("writer", knownTabs);
const listenerTab = await openTestTabInOwnProcess("listener", knownTabs);
const readerTab = await openTestTabInOwnProcess("reader", knownTabs);
const lateWriteThenListenTab = await openTestTabInOwnProcess(
const writerTab = await openTestTabInOwnProcess(HELPER_PAGE_URL, "writer",
knownTabs);
const listenerTab = await openTestTabInOwnProcess(HELPER_PAGE_URL, "listener",
knownTabs);
const readerTab = await openTestTabInOwnProcess(HELPER_PAGE_URL, "reader",
knownTabs);
const lateWriteThenListenTab = await openTestTabInOwnProcess(HELPER_PAGE_URL,
"lateWriteThenListen", knownTabs);
// Sanity check that preloading did not occur in the tabs.
@ -484,8 +422,8 @@ add_task(async function() {
// - Open a fresh tab and make sure it sees the precache/preload
info("late open preload check");
const lateOpenSeesPreload =
await openTestTabInOwnProcess("lateOpenSeesPreload", knownTabs);
const lateOpenSeesPreload = await openTestTabInOwnProcess(HELPER_PAGE_URL,
"lateOpenSeesPreload", knownTabs);
await verifyTabPreload(lateOpenSeesPreload, true);
// - Clean up.

Просмотреть файл

@ -0,0 +1,371 @@
const HELPER_PAGE_URL =
"http://example.com/browser/dom/tests/browser/page_localstorage_snapshotting_e10s.html";
const HELPER_PAGE_ORIGIN = "http://example.com/";
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "/helper_localStorage_e10s.js",
this);
function clearOrigin() {
let principal =
Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(
HELPER_PAGE_ORIGIN);
let request =
Services.qms.clearStoragesForPrincipal(principal, "default", "ls");
let promise = new Promise(resolve => {
request.callback = () => {
resolve();
};
});
return promise;
}
async function applyMutations(knownTab, mutations) {
await ContentTask.spawn(
knownTab.tab.linkedBrowser,
mutations,
function(mutations) {
return content.wrappedJSObject.applyMutations(Cu.cloneInto(mutations,
content));
});
}
async function verifyState(knownTab, expectedState) {
let actualState = await ContentTask.spawn(
knownTab.tab.linkedBrowser,
{},
function() {
return content.wrappedJSObject.getState();
});
for (let [expectedKey, expectedValue] of Object.entries(expectedState)) {
ok(actualState.hasOwnProperty(expectedKey), "key present: " + expectedKey);
is(actualState[expectedKey], expectedValue, "value correct");
}
for (let actualKey of Object.keys(actualState)) {
if (!expectedState.hasOwnProperty(actualKey)) {
ok(false, "actual state has key it shouldn't have: " + actualKey);
}
}
}
async function getKeys(knownTab) {
let keys = await ContentTask.spawn(
knownTab.tab.linkedBrowser,
null,
function() {
return content.wrappedJSObject.getKeys();
});
return keys;
}
async function beginExplicitSnapshot(knownTab) {
await ContentTask.spawn(
knownTab.tab.linkedBrowser,
null,
function() {
return content.wrappedJSObject.beginExplicitSnapshot();
});
}
async function endExplicitSnapshot(knownTab) {
await ContentTask.spawn(
knownTab.tab.linkedBrowser,
null,
function() {
return content.wrappedJSObject.endExplicitSnapshot();
});
}
// We spin up a ton of child processes.
requestLongerTimeout(4);
/**
* Verify snapshotting of our localStorage implementation in multi-e10s setup.
*/
add_task(async function() {
if (!Services.lsm.nextGenLocalStorageEnabled) {
ok(true, "Test ignored when the next gen local storage is not enabled.");
return;
}
await SpecialPowers.pushPrefEnv({
set: [
// Enable LocalStorage's testing API so we can explicitly create
// snapshots when needed.
["dom.storage.testing", true],
]
});
// Ensure that there is no localstorage data by forcing the origin to be
// cleared prior to the start of our test..
await clearOrigin();
// - Open tabs. Don't configure any of them yet.
const knownTabs = new KnownTabs();
const writerTab1 = await openTestTabInOwnProcess(HELPER_PAGE_URL, "writer1",
knownTabs);
const writerTab2 = await openTestTabInOwnProcess(HELPER_PAGE_URL, "writer2",
knownTabs);
const readerTab1 = await openTestTabInOwnProcess(HELPER_PAGE_URL, "reader1",
knownTabs);
const readerTab2 = await openTestTabInOwnProcess(HELPER_PAGE_URL, "reader2",
knownTabs);
const initialMutations = [
[null, null],
["key1", "initial1"],
["key2", "initial2"],
["key3", "initial3"],
["key5", "initial5"],
["key6", "initial6"],
["key7", "initial7"],
["key8", "initial8"]
];
const initialState = {
key1: "initial1",
key2: "initial2",
key3: "initial3",
key5: "initial5",
key6: "initial6",
key7: "initial7",
key8: "initial8"
};
function getPartialPrefill()
{
let size = 0;
let entries = Object.entries(initialState);
for (let i = 0; i < entries.length / 2; i++) {
let entry = entries[i];
size += entry[0].length + entry[1].length;
}
return size;
}
const prefillValues = [
0, // no prefill
getPartialPrefill(), // partial prefill
-1 // full prefill
];
for (let prefillValue of prefillValues) {
info("Setting prefill value");
await SpecialPowers.pushPrefEnv({
set: [
["dom.storage.snapshot_prefill", prefillValue]
]
});
info("Stage 1");
const setRemoveMutations1 = [
["key0", "setRemove10"],
["key1", "setRemove11"],
["key2", null],
["key3", "setRemove13"],
["key4", "setRemove14"],
["key5", "setRemove15"],
["key6", "setRemove16"],
["key7", "setRemove17"],
["key8", null],
["key9", "setRemove19"]
];
const setRemoveState1 = {
key0: "setRemove10",
key1: "setRemove11",
key3: "setRemove13",
key4: "setRemove14",
key5: "setRemove15",
key6: "setRemove16",
key7: "setRemove17",
key9: "setRemove19"
};
const setRemoveMutations2 = [
["key0", "setRemove20"],
["key1", null],
["key2", "setRemove22"],
["key3", "setRemove23"],
["key4", "setRemove24"],
["key5", "setRemove25"],
["key6", "setRemove26"],
["key7", null],
["key8", "setRemove28"],
["key9", "setRemove29"]
];
const setRemoveState2 = {
key0: "setRemove20",
key2: "setRemove22",
key3: "setRemove23",
key4: "setRemove24",
key5: "setRemove25",
key6: "setRemove26",
key8: "setRemove28",
key9: "setRemove29"
};
// Apply initial mutations using an explicit snapshot. The explicit
// snapshot here ensures that the parent process have received the changes.
await beginExplicitSnapshot(writerTab1);
await applyMutations(writerTab1, initialMutations);
await endExplicitSnapshot(writerTab1);
// Begin explicit snapshots in all tabs except readerTab2. All these tabs
// should see the initial state regardless what other tabs are doing.
await beginExplicitSnapshot(writerTab1);
await beginExplicitSnapshot(writerTab2);
await beginExplicitSnapshot(readerTab1);
// Apply first array of set/remove mutations in writerTab1 and end the
// explicit snapshot. This will trigger saving of values in other active
// snapshots.
await applyMutations(writerTab1, setRemoveMutations1);
await endExplicitSnapshot(writerTab1);
// Begin an explicit snapshot in readerTab2. writerTab1 already ended its
// explicit snapshot, so readerTab2 should see mutations done by
// writerTab1.
await beginExplicitSnapshot(readerTab2);
// Apply second array of set/remove mutations in writerTab2 and end the
// explicit snapshot. This will trigger saving of values in other active
// snapshots, but only if they haven't been saved already.
await applyMutations(writerTab2, setRemoveMutations2);
await endExplicitSnapshot(writerTab2);
// Verify state in readerTab1, it should match the initial state.
await verifyState(readerTab1, initialState);
await endExplicitSnapshot(readerTab1);
// Verify state in readerTab2, it should match the state after the first
// array of set/remove mutatations have been applied and "commited".
await verifyState(readerTab2, setRemoveState1);
await endExplicitSnapshot(readerTab2);
// Verify final state, it should match the state after the second array of
// set/remove mutation have been applied and "commited". An explicit
// snapshot is used.
await beginExplicitSnapshot(readerTab1);
await verifyState(readerTab1, setRemoveState2);
await endExplicitSnapshot(readerTab1);
info("Stage 2");
const setRemoveClearMutations1 = [
["key0", "setRemoveClear10"],
["key1", null],
[null, null]
];
const setRemoveClearState1 = {
};
const setRemoveClearMutations2 = [
["key8", null],
["key9", "setRemoveClear29"],
[null, null]
];
const setRemoveClearState2 = {
};
// This is very similar to previous stage except that in addition to
// set/remove, the clear operation is involved too.
await beginExplicitSnapshot(writerTab1);
await applyMutations(writerTab1, initialMutations);
await endExplicitSnapshot(writerTab1);
await beginExplicitSnapshot(writerTab1);
await beginExplicitSnapshot(writerTab2);
await beginExplicitSnapshot(readerTab1);
await applyMutations(writerTab1, setRemoveClearMutations1);
await endExplicitSnapshot(writerTab1);
await beginExplicitSnapshot(readerTab2);
await applyMutations(writerTab2, setRemoveClearMutations2);
await endExplicitSnapshot(writerTab2);
await verifyState(readerTab1, initialState);
await endExplicitSnapshot(readerTab1);
await verifyState(readerTab2, setRemoveClearState1);
await endExplicitSnapshot(readerTab2);
await beginExplicitSnapshot(readerTab1);
await verifyState(readerTab1, setRemoveClearState2);
await endExplicitSnapshot(readerTab1);
info("Stage 3");
const changeOrderMutations = [
["key1", null],
["key2", null],
["key3", null],
["key5", null],
["key6", null],
["key7", null],
["key8", null],
["key8", "initial8"],
["key7", "initial7"],
["key6", "initial6"],
["key5", "initial5"],
["key3", "initial3"],
["key2", "initial2"],
["key1", "initial1"]
];
// Apply initial mutations using an explicit snapshot. The explicit
// snapshot here ensures that the parent process have received the changes.
await beginExplicitSnapshot(writerTab1);
await applyMutations(writerTab1, initialMutations);
await endExplicitSnapshot(writerTab1);
// Begin explicit snapshots in all tabs except writerTab2 which is not used
// in this stage. All these tabs should see the initial order regardless
// what other tabs are doing.
await beginExplicitSnapshot(readerTab1);
await beginExplicitSnapshot(writerTab1);
await beginExplicitSnapshot(readerTab2);
// Get all keys in readerTab1 and end the explicit snapshot. No mutations
// have been applied yet.
let tab1Keys = await getKeys(readerTab1);
await endExplicitSnapshot(readerTab1);
// Apply mutations that change the order of keys and end the explicit
// snapshot. The state is unchanged. This will trigger saving of key order
// in other active snapshots, but only if the order hasn't been saved
// already.
await applyMutations(writerTab1, changeOrderMutations);
await endExplicitSnapshot(writerTab1);
// Get all keys in readerTab2 and end the explicit snapshot. Change order
// mutations have been applied, but the order should stay unchanged.
let tab2Keys = await getKeys(readerTab2);
await endExplicitSnapshot(readerTab2);
// Verify the key order is the same.
is(tab2Keys.length, tab1Keys.length, "Correct keys length");
for (let i = 0; i < tab2Keys.length; i++) {
is(tab2Keys[i], tab1Keys[i], "Correct key");
}
// Verify final state, it should match the initial state since applied
// mutations only changed the key order. An explicit snapshot is used.
await beginExplicitSnapshot(readerTab1);
await verifyState(readerTab1, initialState);
await endExplicitSnapshot(readerTab1);
}
// - Clean up.
await cleanupTabs(knownTabs);
clearOrigin();
});

Просмотреть файл

@ -0,0 +1,72 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// Simple tab wrapper abstracting our messaging mechanism;
class KnownTab {
constructor(name, tab) {
this.name = name;
this.tab = tab;
}
cleanup() {
this.tab = null;
}
}
// Simple data structure class to help us track opened tabs and their pids.
class KnownTabs {
constructor() {
this.byPid = new Map();
this.byName = new Map();
}
cleanup() {
this.byPid = null;
this.byName = null;
}
}
/**
* Open our helper page in a tab in its own content process, asserting that it
* really is in its own process. We initially load and wait for about:blank to
* load, and only then loadURI to our actual page. This is to ensure that
* LocalStorageManager has had an opportunity to be created and populate
* mOriginsHavingData.
*
* (nsGlobalWindow will reliably create LocalStorageManager as a side-effect of
* the unconditional call to nsGlobalWindow::PreloadLocalStorage. This will
* reliably create the StorageDBChild instance, and its corresponding
* StorageDBParent will send the set of origins when it is constructed.)
*/
async function openTestTabInOwnProcess(helperPageUrl, name, knownTabs) {
let realUrl = helperPageUrl + '?' + encodeURIComponent(name);
// Load and wait for about:blank.
let tab = await BrowserTestUtils.openNewForegroundTab({
gBrowser, opening: 'about:blank', forceNewProcess: true
});
let pid = tab.linkedBrowser.frameLoader.tabParent.osPid;
ok(!knownTabs.byName.has(name), "tab needs its own name: " + name);
ok(!knownTabs.byPid.has(pid), "tab needs to be in its own process: " + pid);
let knownTab = new KnownTab(name, tab);
knownTabs.byPid.set(pid, knownTab);
knownTabs.byName.set(name, knownTab);
// Now trigger the actual load of our page.
BrowserTestUtils.loadURI(tab.linkedBrowser, realUrl);
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
is(tab.linkedBrowser.frameLoader.tabParent.osPid, pid, "still same pid");
return knownTab;
}
/**
* Close all the tabs we opened.
*/
async function cleanupTabs(knownTabs) {
for (let knownTab of knownTabs.byName.values()) {
BrowserTestUtils.removeTab(knownTab.tab);
knownTab.cleanup();
}
knownTabs.cleanup();
}

Просмотреть файл

@ -0,0 +1,55 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script>
/**
* Helper page used by browser_localStorage_snapshotting.js.
*
* We expose methods to be invoked by ContentTask.spawn() calls.
*
**/
var pageName = document.location.search.substring(1);
window.addEventListener(
"load",
() => { document.getElementById("pageNameH").textContent = pageName; });
function applyMutations(mutations) {
mutations.forEach(function([key, value]) {
if (key !== null) {
if (value === null) {
localStorage.removeItem(key);
} else {
localStorage.setItem(key, value);
}
} else {
localStorage.clear();
}
});
}
function getState() {
let state = {};
let length = localStorage.length;
for (let index = 0; index < length; index++) {
let key = localStorage.key(index);
state[key] = localStorage.getItem(key);
}
return state;
}
function getKeys() {
return Object.keys(localStorage);
}
function beginExplicitSnapshot() {
localStorage.beginExplicitSnapshot();
}
function endExplicitSnapshot() {
localStorage.endExplicitSnapshot();
}
</script>
</head>
<body><h2 id="pageNameH"></h2></body>
</html>