зеркало из https://github.com/mozilla/gecko-dev.git
Bug 883609 - Porting tests to new Session Restore backup mechanism;r=ttaubert
--HG-- rename : browser/components/sessionstore/test/browser_833286_atomic_backup.js => browser/components/sessionstore/test/browser_backup_recovery.js
This commit is contained in:
Родитель
dda55660fc
Коммит
409d9e236e
|
@ -60,6 +60,7 @@ support-files =
|
|||
[browser_aboutPrivateBrowsing.js]
|
||||
[browser_aboutSessionRestore.js]
|
||||
[browser_attributes.js]
|
||||
[browser_backup_recovery.js]
|
||||
[browser_broadcast.js]
|
||||
[browser_capabilities.js]
|
||||
[browser_cleaner.js]
|
||||
|
@ -180,7 +181,6 @@ skip-if = true # Needs to be rewritten as Marionette test, bug 995916
|
|||
[browser_739805.js]
|
||||
[browser_819510_perwindowpb.js]
|
||||
skip-if = os == "linux" # Intermittent failures, bug 894063
|
||||
[browser_833286_atomic_backup.js]
|
||||
|
||||
# Disabled for frequent intermittent failures
|
||||
[browser_464620_a.js]
|
||||
|
|
|
@ -3,63 +3,56 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/** Private Browsing Test for Bug 394759 **/
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let windowsToClose = [];
|
||||
let closedWindowCount = 0;
|
||||
// Prevent VM timers issues, cache now and increment it manually.
|
||||
let now = Date.now();
|
||||
const TESTS = [
|
||||
{ url: "about:config",
|
||||
key: "bug 394759 Non-PB",
|
||||
value: "uniq" + (++now) },
|
||||
{ url: "about:mozilla",
|
||||
key: "bug 394759 PB",
|
||||
value: "uniq" + (++now) },
|
||||
];
|
||||
let closedWindowCount = 0;
|
||||
// Prevent VM timers issues, cache now and increment it manually.
|
||||
let now = Date.now();
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref("browser.sessionstore.interval");
|
||||
windowsToClose.forEach(function(win) {
|
||||
win.close();
|
||||
});
|
||||
const TESTS = [
|
||||
{ url: "about:config",
|
||||
key: "bug 394759 Non-PB",
|
||||
value: "uniq" + (++now) },
|
||||
{ url: "about:mozilla",
|
||||
key: "bug 394759 PB",
|
||||
value: "uniq" + (++now) },
|
||||
];
|
||||
|
||||
function promiseTestOpenCloseWindow(aIsPrivate, aTest) {
|
||||
return Task.spawn(function*() {
|
||||
let win = yield promiseNewWindowLoaded({ "private": aIsPrivate });
|
||||
win.gBrowser.selectedBrowser.loadURI(aTest.url);
|
||||
yield promiseBrowserLoaded(win.gBrowser.selectedBrowser);
|
||||
yield Promise.resolve();
|
||||
// Mark the window with some unique data to be restored later on.
|
||||
ss.setWindowValue(win, aTest.key, aTest.value);
|
||||
// Close.
|
||||
yield promiseWindowClosed(win);
|
||||
});
|
||||
}
|
||||
|
||||
function testOpenCloseWindow(aIsPrivate, aTest, aCallback) {
|
||||
whenNewWindowLoaded({ private: aIsPrivate }, function(win) {
|
||||
whenBrowserLoaded(win.gBrowser.selectedBrowser, function() {
|
||||
executeSoon(function() {
|
||||
// Mark the window with some unique data to be restored later on.
|
||||
ss.setWindowValue(win, aTest.key, aTest.value);
|
||||
// Close.
|
||||
win.close();
|
||||
aCallback();
|
||||
});
|
||||
});
|
||||
win.gBrowser.selectedBrowser.loadURI(aTest.url);
|
||||
});
|
||||
}
|
||||
function promiseTestOnWindow(aIsPrivate, aValue) {
|
||||
return Task.spawn(function*() {
|
||||
let win = yield promiseNewWindowLoaded({ "private": aIsPrivate });
|
||||
yield promiseCheckClosedWindows(aIsPrivate, aValue);
|
||||
registerCleanupFunction(() => promiseWindowClosed(win));
|
||||
});
|
||||
}
|
||||
|
||||
function testOnWindow(aIsPrivate, aValue, aCallback) {
|
||||
whenNewWindowLoaded({ private: aIsPrivate }, function(win) {
|
||||
windowsToClose.push(win);
|
||||
executeSoon(function() checkClosedWindows(aIsPrivate, aValue, aCallback));
|
||||
});
|
||||
}
|
||||
|
||||
function checkClosedWindows(aIsPrivate, aValue, aCallback) {
|
||||
function promiseCheckClosedWindows(aIsPrivate, aValue) {
|
||||
return Task.spawn(function*() {
|
||||
let data = JSON.parse(ss.getClosedWindowData())[0];
|
||||
is(ss.getClosedWindowCount(), 1, "Check the closed window count");
|
||||
is(ss.getClosedWindowCount(), 1, "Check that the closed window count hasn't changed");
|
||||
ok(JSON.stringify(data).indexOf(aValue) > -1,
|
||||
"Check the closed window data was stored correctly");
|
||||
aCallback();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setupBlankState(aCallback) {
|
||||
function promiseBlankState() {
|
||||
return Task.spawn(function*() {
|
||||
// Set interval to a large time so state won't be written while we setup
|
||||
// environment.
|
||||
Services.prefs.setIntPref("browser.sessionstore.interval", 100000);
|
||||
registerCleanupFunction(() => Services.prefs.clearUserPref("browser.sessionstore.interval"));
|
||||
|
||||
// Set up the browser in a blank state. Popup windows in previous tests
|
||||
// result in different states on different platforms.
|
||||
|
@ -70,40 +63,39 @@ function test() {
|
|||
}],
|
||||
_closedWindows: []
|
||||
});
|
||||
|
||||
ss.setBrowserState(blankState);
|
||||
|
||||
// Wait for the sessionstore.js file to be written before going on.
|
||||
// Note: we don't wait for the complete event, since if asyncCopy fails we
|
||||
// would timeout.
|
||||
waitForSaveState(function(writing) {
|
||||
ok(writing, "sessionstore.js is being written");
|
||||
closedWindowCount = ss.getClosedWindowCount();
|
||||
is(closedWindowCount, 0, "Correctly set window count");
|
||||
|
||||
executeSoon(aCallback);
|
||||
});
|
||||
yield forceSaveState();
|
||||
closedWindowCount = ss.getClosedWindowCount();
|
||||
is(closedWindowCount, 0, "Correctly set window count");
|
||||
|
||||
// Remove the sessionstore.js file before setting the interval to 0
|
||||
let profilePath = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
let sessionStoreJS = profilePath.clone();
|
||||
sessionStoreJS.append("sessionstore.js");
|
||||
if (sessionStoreJS.exists())
|
||||
sessionStoreJS.remove(false);
|
||||
info("sessionstore.js was correctly removed: " + (!sessionStoreJS.exists()));
|
||||
yield SessionFile.wipe();
|
||||
|
||||
// Make sure that sessionstore.js can be forced to be created by setting
|
||||
// the interval pref to 0.
|
||||
Services.prefs.setIntPref("browser.sessionstore.interval", 0);
|
||||
}
|
||||
|
||||
setupBlankState(function() {
|
||||
testOpenCloseWindow(false, TESTS[0], function() {
|
||||
testOpenCloseWindow(true, TESTS[1], function() {
|
||||
testOnWindow(false, TESTS[0].value, function() {
|
||||
testOnWindow(true, TESTS[0].value, finish);
|
||||
});
|
||||
});
|
||||
});
|
||||
yield forceSaveState();
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* init() {
|
||||
while (ss.getClosedWindowCount() > 0) {
|
||||
ss.forgetClosedWindow(0);
|
||||
}
|
||||
while (ss.getClosedTabCount(window) > 0) {
|
||||
ss.forgetClosedTab(window, 0);
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* main() {
|
||||
yield promiseTestOpenCloseWindow(false, TESTS[0]);
|
||||
yield promiseTestOpenCloseWindow(true, TESTS[1]);
|
||||
yield promiseTestOnWindow(false, TESTS[0].value);
|
||||
yield promiseTestOnWindow(true, TESTS[0].value);
|
||||
});
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ const PASS = "pwd-" + Math.random();
|
|||
/**
|
||||
* Bug 454908 - Don't save/restore values of password fields.
|
||||
*/
|
||||
add_task(function test_dont_save_passwords() {
|
||||
add_task(function* test_dont_save_passwords() {
|
||||
// Make sure we do save form data.
|
||||
Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
|
||||
|
||||
|
@ -40,13 +40,12 @@ add_task(function test_dont_save_passwords() {
|
|||
is(passwd, "", "password wasn't saved/restored");
|
||||
|
||||
// Write to disk and read our file.
|
||||
yield SessionSaver.run();
|
||||
let path = OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js");
|
||||
let data = yield OS.File.read(path);
|
||||
let state = new TextDecoder().decode(data);
|
||||
yield forceSaveState();
|
||||
yield promiseForEachSessionRestoreFile((state, key) =>
|
||||
// Ensure that we have not saved our password.
|
||||
ok(!state.contains(PASS), "password has not been written to file " + key)
|
||||
);
|
||||
|
||||
// Ensure that sessionstore.js doesn't contain our password.
|
||||
is(state.indexOf(PASS), -1, "password has not been written to disk");
|
||||
|
||||
// Cleanup.
|
||||
gBrowser.removeTab(tab);
|
||||
|
|
|
@ -36,7 +36,7 @@ add_task(function* new_window() {
|
|||
yield promiseWindowClosed(newWin);
|
||||
newWin = null;
|
||||
|
||||
let state = JSON.parse((yield promiseSaveFileContents()));
|
||||
let state = JSON.parse((yield promiseRecoveryFileContents()));
|
||||
is(state.windows.length, 2,
|
||||
"observe1: 2 windows in data written to disk");
|
||||
is(state._closedWindows.length, 0,
|
||||
|
@ -60,7 +60,7 @@ add_task(function* new_tab() {
|
|||
try {
|
||||
newTab = gBrowser.addTab("about:mozilla");
|
||||
|
||||
let state = JSON.parse((yield promiseSaveFileContents()));
|
||||
let state = JSON.parse((yield promiseRecoveryFileContents()));
|
||||
is(state.windows.length, 1,
|
||||
"observe2: 1 window in data being written to disk");
|
||||
is(state._closedWindows.length, 1,
|
||||
|
|
|
@ -164,7 +164,7 @@ function waitForWindowClose(aWin, aCallback) {
|
|||
}
|
||||
|
||||
function forceWriteState(aCallback) {
|
||||
return promiseSaveFileContents().then(function(data) {
|
||||
return promiseRecoveryFileContents().then(function(data) {
|
||||
aCallback(JSON.parse(data));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// This tests are for a sessionstore.js atomic backup.
|
||||
// Each test will wait for a write to the Session Store
|
||||
// before executing.
|
||||
|
||||
let tmp = {};
|
||||
Cu.import("resource://gre/modules/osfile.jsm", tmp);
|
||||
Cu.import("resource:///modules/sessionstore/SessionFile.jsm", tmp);
|
||||
|
||||
const {OS, SessionFile} = tmp;
|
||||
|
||||
const PREF_SS_INTERVAL = "browser.sessionstore.interval";
|
||||
// Full paths for sessionstore.js and sessionstore.bak.
|
||||
const path = OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js");
|
||||
const backupPath = OS.Path.join(OS.Constants.Path.profileDir,
|
||||
"sessionstore.bak");
|
||||
|
||||
// A text decoder.
|
||||
let gDecoder = new TextDecoder();
|
||||
// Global variables that contain sessionstore.js and sessionstore.bak data for
|
||||
// comparison between tests.
|
||||
let gSSData;
|
||||
let gSSBakData;
|
||||
|
||||
|
||||
|
||||
add_task(function* testAfterFirstWrite() {
|
||||
// Ensure sessionstore.bak is not created. We start with a clean
|
||||
// profile so there was nothing to move to sessionstore.bak before
|
||||
// initially writing sessionstore.js
|
||||
let ssExists = yield OS.File.exists(path);
|
||||
let ssBackupExists = yield OS.File.exists(backupPath);
|
||||
ok(ssExists, "sessionstore.js should exist.");
|
||||
ok(!ssBackupExists, "sessionstore.bak should not have been created, yet");
|
||||
|
||||
// Save sessionstore.js data to compare to the sessionstore.bak data in the
|
||||
// next test.
|
||||
let array = yield OS.File.read(path);
|
||||
gSSData = gDecoder.decode(array);
|
||||
|
||||
// Manually move to the backup since the first write has already happened
|
||||
// and a backup would not be triggered again.
|
||||
yield OS.File.move(path, backupPath);
|
||||
|
||||
yield forceSaveState();
|
||||
});
|
||||
|
||||
add_task(function* testReadBackup() {
|
||||
// Ensure sessionstore.bak is finally created.
|
||||
let ssExists = yield OS.File.exists(path);
|
||||
let ssBackupExists = yield OS.File.exists(backupPath);
|
||||
ok(ssExists, "sessionstore.js exists.");
|
||||
ok(ssBackupExists, "sessionstore.bak should now be created.");
|
||||
|
||||
// Read sessionstore.bak data.
|
||||
let array = yield OS.File.read(backupPath);
|
||||
gSSBakData = gDecoder.decode(array);
|
||||
|
||||
// Make sure that the sessionstore.bak is identical to the last
|
||||
// sessionstore.js.
|
||||
is(gSSBakData, gSSData, "sessionstore.js is backed up correctly.");
|
||||
|
||||
// Read latest sessionstore.js.
|
||||
array = yield OS.File.read(path);
|
||||
gSSData = gDecoder.decode(array);
|
||||
|
||||
// Read sessionstore.js with SessionFile.read.
|
||||
let ssDataRead = yield SessionFile.read();
|
||||
is(ssDataRead, gSSData, "SessionFile.read read sessionstore.js correctly.");
|
||||
|
||||
// Remove sessionstore.js to test fallback onto sessionstore.bak.
|
||||
yield OS.File.remove(path);
|
||||
ssExists = yield OS.File.exists(path);
|
||||
ok(!ssExists, "sessionstore.js should be removed now.");
|
||||
|
||||
// Read sessionstore.bak with SessionFile.read.
|
||||
ssDataRead = yield SessionFile.read();
|
||||
is(ssDataRead, gSSBakData,
|
||||
"SessionFile.read read sessionstore.bak correctly.");
|
||||
|
||||
yield forceSaveState();
|
||||
});
|
||||
|
||||
add_task(function* testBackupUnchanged() {
|
||||
// Ensure sessionstore.bak is backed up only once.
|
||||
|
||||
// Read sessionstore.bak data.
|
||||
let array = yield OS.File.read(backupPath);
|
||||
let ssBakData = gDecoder.decode(array);
|
||||
// Ensure the sessionstore.bak did not change.
|
||||
is(ssBakData, gSSBakData, "sessionstore.bak is unchanged.");
|
||||
});
|
||||
|
||||
add_task(function* cleanup() {
|
||||
// Cleaning up after the test: removing the sessionstore.bak file.
|
||||
yield OS.File.remove(backupPath);
|
||||
});
|
|
@ -0,0 +1,132 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// This tests are for a sessionstore.js atomic backup.
|
||||
// Each test will wait for a write to the Session Store
|
||||
// before executing.
|
||||
|
||||
let OS = Cu.import("resource://gre/modules/osfile.jsm", {}).OS;
|
||||
let {File, Constants, Path} = OS;
|
||||
|
||||
const PREF_SS_INTERVAL = "browser.sessionstore.interval";
|
||||
const Paths = SessionFile.Paths;
|
||||
|
||||
// A text decoder.
|
||||
let gDecoder = new TextDecoder();
|
||||
// Global variables that contain sessionstore.js and sessionstore.bak data for
|
||||
// comparison between tests.
|
||||
let gSSData;
|
||||
let gSSBakData;
|
||||
|
||||
function promiseRead(path) {
|
||||
return File.read(path, {encoding: "utf-8"});
|
||||
}
|
||||
|
||||
add_task(function* init() {
|
||||
// Make sure that we are not racing with SessionSaver's time based
|
||||
// saves.
|
||||
Services.prefs.setIntPref(PREF_SS_INTERVAL, 10000000);
|
||||
registerCleanupFunction(() => Services.prefs.clearUserPref(PREF_SS_INTERVAL));
|
||||
});
|
||||
|
||||
add_task(function* test_creation() {
|
||||
|
||||
let OLD_BACKUP = Path.join(Constants.Path.profileDir, "sessionstore.bak");
|
||||
let OLD_UPGRADE_BACKUP = Path.join(Constants.Path.profileDir, "sessionstore.bak-0000000");
|
||||
|
||||
yield File.writeAtomic(OLD_BACKUP, "sessionstore.bak");
|
||||
yield File.writeAtomic(OLD_UPGRADE_BACKUP, "sessionstore upgrade backup");
|
||||
|
||||
yield SessionFile.wipe();
|
||||
yield SessionFile.read(); // Reinitializes SessionFile
|
||||
for (let k of Paths.loadOrder) {
|
||||
ok(!(yield File.exists(Paths[k])), "After wipe " + k + " sessionstore file doesn't exist");
|
||||
}
|
||||
ok(!(yield File.exists(OLD_BACKUP)), "After wipe, old backup doesn't exist");
|
||||
ok(!(yield File.exists(OLD_UPGRADE_BACKUP)), "After wipe, old upgrade backup doesn't exist");
|
||||
|
||||
let URL_BASE = "http://example.com/?atomic_backup_test_creation=" + Math.random();
|
||||
let URL = URL_BASE + "?first_write";
|
||||
let tab = gBrowser.addTab(URL);
|
||||
|
||||
info("Testing situation after a single write");
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
SyncHandlers.get(tab.linkedBrowser).flush();
|
||||
yield SessionSaver.run();
|
||||
|
||||
ok((yield File.exists(Paths.recovery)), "After write, recovery sessionstore file exists again");
|
||||
ok(!(yield File.exists(Paths.recoveryBackup)), "After write, recoveryBackup sessionstore doesn't exist");
|
||||
ok((yield promiseRead(Paths.recovery)).indexOf(URL) != -1, "Recovery sessionstore file contains the required tab");
|
||||
ok(!(yield File.exists(Paths.clean)), "After first write, clean shutdown sessionstore doesn't exist, since we haven't shutdown yet");
|
||||
|
||||
info("Testing situation after a second write");
|
||||
let URL2 = URL_BASE + "?second_write";
|
||||
tab.linkedBrowser.loadURI(URL2);
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
SyncHandlers.get(tab.linkedBrowser).flush();
|
||||
yield SessionSaver.run();
|
||||
|
||||
ok((yield File.exists(Paths.recovery)), "After second write, recovery sessionstore file still exists");
|
||||
ok((yield promiseRead(Paths.recovery)).indexOf(URL2) != -1, "Recovery sessionstore file contains the latest url");
|
||||
ok((yield File.exists(Paths.recoveryBackup)), "After write, recoveryBackup sessionstore now exists");
|
||||
let backup = yield promiseRead(Paths.recoveryBackup);
|
||||
ok(backup.indexOf(URL2) == -1, "Recovery backup doesn't contain the latest url");
|
||||
ok(backup.indexOf(URL) != -1, "Recovery backup contains the original url");
|
||||
ok(!(yield File.exists(Paths.clean)), "After first write, clean shutdown sessinstore doesn't exist, since we haven't shutdown yet");
|
||||
|
||||
info("Reinitialize, ensure that we haven't leaked sensitive files");
|
||||
yield SessionFile.read(); // Reinitializes SessionFile
|
||||
yield SessionSaver.run();
|
||||
ok(!(yield File.exists(Paths.clean)), "After second write, clean shutdown sessonstore doesn't exist, since we haven't shutdown yet");
|
||||
ok(!(yield File.exists(Paths.upgradeBackup)), "After second write, clean shutdwn sessionstore doesn't exist, since we haven't shutdown yet");
|
||||
ok(!(yield File.exists(Paths.nextUpgradeBackup)), "After second write, clean sutdown sessionstore doesn't exist, since we haven't shutdown yet");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
yield SessionFile.wipe();
|
||||
});
|
||||
|
||||
let promiseSource = Task.async(function*(name) {
|
||||
let URL = "http://example.com/?atomic_backup_test_recovery=" + Math.random() + "&name=" + name;
|
||||
let tab = gBrowser.addTab(URL);
|
||||
|
||||
yield promiseBrowserLoaded(tab.linkedBrowser);
|
||||
SyncHandlers.get(tab.linkedBrowser).flush();
|
||||
yield SessionSaver.run();
|
||||
gBrowser.removeTab(tab);
|
||||
|
||||
let SOURCE = yield promiseRead(Paths.recovery);
|
||||
yield SessionFile.wipe();
|
||||
return SOURCE;
|
||||
});
|
||||
|
||||
add_task(function* test_recovery() {
|
||||
yield SessionFile.wipe();
|
||||
info("Attempting to recover from the recovery file");
|
||||
let SOURCE = yield promiseSource("Paths.recovery");
|
||||
// Ensure that we can recover from Paths.recovery
|
||||
yield File.makeDir(Paths.backups);
|
||||
yield File.writeAtomic(Paths.recovery, SOURCE);
|
||||
is((yield SessionFile.read()).source, SOURCE, "Recovered the correct source from the recovery file");
|
||||
yield SessionFile.wipe();
|
||||
|
||||
info("Corrupting recovery file, attempting to recover from recovery backup");
|
||||
SOURCE = yield promiseSource("Paths.recoveryBackup");
|
||||
yield File.makeDir(Paths.backups);
|
||||
yield File.writeAtomic(Paths.recoveryBackup, SOURCE);
|
||||
yield File.writeAtomic(Paths.recovery, "<Invalid JSON>");
|
||||
is((yield SessionFile.read()).source, SOURCE, "Recovered the correct source from the recovery file");
|
||||
});
|
||||
|
||||
add_task(function* test_clean() {
|
||||
yield SessionFile.wipe();
|
||||
let SOURCE = yield promiseSource("Paths.clean");
|
||||
yield File.writeAtomic(Paths.clean, SOURCE);
|
||||
yield SessionFile.read();
|
||||
yield SessionSaver.run();
|
||||
is((yield promiseRead(Paths.cleanBackup)), SOURCE, "After first read/write, clean shutdown file has been moved to cleanBackup");
|
||||
});
|
||||
|
||||
add_task(function* cleanup() {
|
||||
yield SessionFile.wipe();
|
||||
});
|
||||
|
|
@ -34,10 +34,8 @@ add_task(function() {
|
|||
SyncHandlers.get(tab2.linkedBrowser).flush();
|
||||
|
||||
info("Checking out state");
|
||||
yield SessionSaver.run();
|
||||
let path = OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js");
|
||||
let data = yield OS.File.read(path);
|
||||
let state = new TextDecoder().decode(data);
|
||||
let state = yield promiseRecoveryFileContents();
|
||||
|
||||
info("State: " + state);
|
||||
// Ensure that sessionstore.js only knows about the public tab
|
||||
ok(state.indexOf(URL_PUBLIC) != -1, "State contains public tab");
|
||||
|
|
|
@ -5,45 +5,36 @@ Cu.import("resource://gre/modules/Services.jsm", this);
|
|||
Cu.import("resource://gre/modules/osfile.jsm", this);
|
||||
Cu.import("resource://gre/modules/Task.jsm", this);
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
const Paths = SessionFile.Paths;
|
||||
|
||||
Task.spawn(function task() {
|
||||
try {
|
||||
// Wait until initialization is complete
|
||||
yield SessionStore.promiseInitialized;
|
||||
add_task(function* init() {
|
||||
// Wait until initialization is complete
|
||||
yield SessionStore.promiseInitialized;
|
||||
yield SessionFile.wipe();
|
||||
});
|
||||
|
||||
const PREF_UPGRADE = "browser.sessionstore.upgradeBackup.latestBuildID";
|
||||
let buildID = Services.appinfo.platformBuildID;
|
||||
add_task(function* test_upgrade_backup() {
|
||||
const PREF_UPGRADE = "browser.sessionstore.upgradeBackup.latestBuildID";
|
||||
let buildID = Services.appinfo.platformBuildID;
|
||||
info("Let's check if we create an upgrade backup");
|
||||
Services.prefs.setCharPref(PREF_UPGRADE, "");
|
||||
let contents = JSON.stringify({"browser_upgrade_backup.js": Math.random()});
|
||||
yield OS.File.writeAtomic(Paths.clean, contents);
|
||||
yield SessionFile.read(); // First call to read() initializes the SessionWorker
|
||||
yield SessionFile.write(""); // First call to write() triggers the backup
|
||||
|
||||
// Write state once before starting the test to
|
||||
// ensure sessionstore.js writes won't happen in between.
|
||||
yield forceSaveState();
|
||||
is(Services.prefs.getCharPref(PREF_UPGRADE), buildID, "upgrade backup should be set");
|
||||
|
||||
// Force backup to take place with a file decided by us
|
||||
Services.prefs.setCharPref(PREF_UPGRADE, "");
|
||||
let contents = "browser_upgrade_backup.js";
|
||||
let pathStore = OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js");
|
||||
yield OS.File.writeAtomic(pathStore, contents, { tmpPath: pathStore + ".tmp" });
|
||||
yield SessionStore._internal._performUpgradeBackup();
|
||||
is(Services.prefs.getCharPref(PREF_UPGRADE), buildID, "upgrade backup should be set (again)");
|
||||
is((yield OS.File.exists(Paths.upgradeBackup)), true, "upgrade backup file has been created");
|
||||
|
||||
let pathBackup = OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.bak-" + Services.appinfo.platformBuildID);
|
||||
is((yield OS.File.exists(pathBackup)), true, "upgrade backup file has been created");
|
||||
let data = yield OS.File.read(Paths.upgradeBackup);
|
||||
is(contents, (new TextDecoder()).decode(data), "upgrade backup contains the expected contents");
|
||||
|
||||
let data = yield OS.File.read(pathBackup);
|
||||
is(new TextDecoder().decode(data), contents, "upgrade backup contains the expected contents");
|
||||
|
||||
// Ensure that we don't re-backup by accident
|
||||
yield OS.File.writeAtomic(pathStore, "something else entirely", { tmpPath: pathStore + ".tmp" });
|
||||
yield SessionStore._internal._performUpgradeBackup();
|
||||
data = yield OS.File.read(pathBackup);
|
||||
is(new TextDecoder().decode(data), contents, "upgrade backup hasn't changed");
|
||||
|
||||
} catch (ex) {
|
||||
ok(false, "Uncaught error: " + ex + " at " + ex.stack);
|
||||
} finally {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
info("Let's check that we don't overwrite this upgrade backup");
|
||||
let new_contents = JSON.stringify({"something else entirely": Math.random()});
|
||||
yield OS.File.writeAtomic(Paths.clean, new_contents);
|
||||
yield SessionFile.read(); // Reinitialize the SessionWorker
|
||||
yield SessionFile.write(""); // Next call to write() shouldn't trigger the backup
|
||||
data = yield OS.File.read(Paths.upgradeBackup);
|
||||
is(contents, (new TextDecoder()).decode(data), "upgrade backup hasn't changed");
|
||||
});
|
||||
|
|
|
@ -39,9 +39,11 @@ registerCleanupFunction(() => {
|
|||
|
||||
let tmp = {};
|
||||
Cu.import("resource://gre/modules/Promise.jsm", tmp);
|
||||
Cu.import("resource://gre/modules/Task.jsm", tmp);
|
||||
Cu.import("resource:///modules/sessionstore/SessionStore.jsm", tmp);
|
||||
Cu.import("resource:///modules/sessionstore/SessionSaver.jsm", tmp);
|
||||
let {Promise, SessionStore, SessionSaver} = tmp;
|
||||
Cu.import("resource:///modules/sessionstore/SessionFile.jsm", tmp);
|
||||
let {Promise, Task, SessionStore, SessionSaver, SessionFile} = tmp;
|
||||
|
||||
let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
|
||||
|
||||
|
@ -282,13 +284,26 @@ function forceSaveState() {
|
|||
return SessionSaver.run();
|
||||
}
|
||||
|
||||
function promiseSaveFileContents() {
|
||||
function promiseRecoveryFileContents() {
|
||||
let promise = forceSaveState();
|
||||
return promise.then(function() {
|
||||
return OS.File.read(OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js"), { encoding: "utf-8" });
|
||||
return OS.File.read(SessionFile.Paths.recovery, { encoding: "utf-8" });
|
||||
});
|
||||
}
|
||||
|
||||
let promiseForEachSessionRestoreFile = Task.async(function*(cb) {
|
||||
for (let key of SessionFile.Paths.loadOrder) {
|
||||
let data = "";
|
||||
try {
|
||||
data = yield OS.File.read(SessionFile.Paths[key], { encoding: "utf-8" });
|
||||
} catch (ex if ex instanceof OS.File.Error
|
||||
&& ex.becauseNoSuchFile) {
|
||||
// Ignore missing files
|
||||
}
|
||||
cb(data, key);
|
||||
}
|
||||
});
|
||||
|
||||
function whenBrowserLoaded(aBrowser, aCallback = next, ignoreSubFrames = true) {
|
||||
aBrowser.addEventListener("load", function onLoad(event) {
|
||||
if (!ignoreSubFrames || event.target == aBrowser.contentDocument) {
|
||||
|
|
|
@ -5,29 +5,28 @@ let Ci = Components.interfaces;
|
|||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Call a function once initialization of SessionStartup is complete
|
||||
let afterSessionStartupInitialization =
|
||||
function afterSessionStartupInitialization(cb) {
|
||||
do_print("Waiting for session startup initialization");
|
||||
let observer = function() {
|
||||
try {
|
||||
do_print("Session startup initialization observed");
|
||||
Services.obs.removeObserver(observer, "sessionstore-state-finalized");
|
||||
cb();
|
||||
} catch (ex) {
|
||||
do_throw(ex);
|
||||
}
|
||||
};
|
||||
function afterSessionStartupInitialization(cb) {
|
||||
do_print("Waiting for session startup initialization");
|
||||
let observer = function() {
|
||||
try {
|
||||
do_print("Session startup initialization observed");
|
||||
Services.obs.removeObserver(observer, "sessionstore-state-finalized");
|
||||
cb();
|
||||
} catch (ex) {
|
||||
do_throw(ex);
|
||||
}
|
||||
};
|
||||
|
||||
// We need the Crash Monitor initialized for sessionstartup to run
|
||||
// successfully.
|
||||
Components.utils.import("resource://gre/modules/CrashMonitor.jsm");
|
||||
CrashMonitor.init();
|
||||
// We need the Crash Monitor initialized for sessionstartup to run
|
||||
// successfully.
|
||||
Components.utils.import("resource://gre/modules/CrashMonitor.jsm");
|
||||
CrashMonitor.init();
|
||||
|
||||
// Start sessionstartup initialization.
|
||||
let startup = Cc["@mozilla.org/browser/sessionstartup;1"].
|
||||
getService(Ci.nsIObserver);
|
||||
Services.obs.addObserver(startup, "final-ui-startup", false);
|
||||
Services.obs.addObserver(startup, "quit-application", false);
|
||||
Services.obs.notifyObservers(null, "final-ui-startup", "");
|
||||
Services.obs.addObserver(observer, "sessionstore-state-finalized", false);
|
||||
// Start sessionstartup initialization.
|
||||
let startup = Cc["@mozilla.org/browser/sessionstartup;1"].
|
||||
getService(Ci.nsIObserver);
|
||||
Services.obs.addObserver(startup, "final-ui-startup", false);
|
||||
Services.obs.addObserver(startup, "quit-application", false);
|
||||
Services.obs.notifyObservers(null, "final-ui-startup", "");
|
||||
Services.obs.addObserver(observer, "sessionstore-state-finalized", false);
|
||||
};
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
|
||||
let toplevel = this;
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
Cu.import("resource:///modules/sessionstore/SessionFile.jsm", toplevel);
|
||||
pathStore = OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js");
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
let pathStore;
|
||||
function pathBackup(ext) {
|
||||
return OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.bak" + ext);
|
||||
}
|
||||
|
||||
// Ensure that things proceed smoothly if there is no file to back up
|
||||
add_task(function test_nothing_to_backup() {
|
||||
yield SessionFile.createBackupCopy("");
|
||||
});
|
||||
|
||||
// Create a file, back it up, remove it
|
||||
add_task(function test_do_backup() {
|
||||
let content = "test_1";
|
||||
let ext = ".upgrade_test_1";
|
||||
yield OS.File.writeAtomic(pathStore, content, {tmpPath: pathStore + ".tmp"});
|
||||
|
||||
do_print("Ensuring that the backup is created");
|
||||
yield SessionFile.createBackupCopy(ext);
|
||||
do_check_true((yield OS.File.exists(pathBackup(ext))));
|
||||
|
||||
let data = yield OS.File.read(pathBackup(ext));
|
||||
do_check_eq((new TextDecoder()).decode(data), content);
|
||||
|
||||
do_print("Ensuring that we can remove the backup");
|
||||
yield SessionFile.removeBackupCopy(ext);
|
||||
do_check_false((yield OS.File.exists(pathBackup(ext))));
|
||||
});
|
||||
|
|
@ -1,48 +1,151 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
let toplevel = this;
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
"use strict";
|
||||
|
||||
let {OS} = Cu.import("resource://gre/modules/osfile.jsm", {});
|
||||
let {XPCOMUtils} = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
|
||||
let {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
let {SessionWorker} = Cu.import("resource:///modules/sessionstore/SessionWorker.jsm", {});
|
||||
|
||||
let File = OS.File;
|
||||
let Paths;
|
||||
let SessionFile;
|
||||
|
||||
// We need a XULAppInfo to initialize SessionFile
|
||||
let (XULAppInfo = {
|
||||
vendor: "Mozilla",
|
||||
name: "SessionRestoreTest",
|
||||
ID: "{230de50e-4cd1-11dc-8314-0800200c9a66}",
|
||||
version: "1",
|
||||
appBuildID: "2007010101",
|
||||
platformVersion: "",
|
||||
platformBuildID: "2007010101",
|
||||
inSafeMode: false,
|
||||
logConsoleErrors: true,
|
||||
OS: "XPCShell",
|
||||
XPCOMABI: "noarch-spidermonkey",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIXULAppInfo,
|
||||
Ci.nsIXULRuntime,
|
||||
])
|
||||
}) {
|
||||
let XULAppInfoFactory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null)
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
return XULAppInfo.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.registerFactory(Components.ID("{fbfae60b-64a4-44ef-a911-08ceb70b9f31}"),
|
||||
"XULAppInfo", "@mozilla.org/xre/app-info;1",
|
||||
XULAppInfoFactory);
|
||||
};
|
||||
|
||||
function run_test() {
|
||||
let profd = do_get_profile();
|
||||
Cu.import("resource:///modules/sessionstore/SessionFile.jsm", toplevel);
|
||||
decoder = new TextDecoder();
|
||||
pathStore = OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.js");
|
||||
pathBackup = OS.Path.join(OS.Constants.Path.profileDir, "sessionstore.bak");
|
||||
let source = do_get_file("data/sessionstore_valid.js");
|
||||
source.copyTo(profd, "sessionstore.js");
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* init() {
|
||||
// Make sure that we have a profile before initializing SessionFile
|
||||
let profd = do_get_profile();
|
||||
SessionFile = Cu.import("resource:///modules/sessionstore/SessionFile.jsm", {}).SessionFile;
|
||||
Paths = SessionFile.Paths;
|
||||
|
||||
|
||||
let source = do_get_file("data/sessionstore_valid.js");
|
||||
source.copyTo(profd, "sessionstore.js");
|
||||
|
||||
// Finish initialization of SessionFile
|
||||
yield SessionFile.read();
|
||||
});
|
||||
|
||||
let pathStore;
|
||||
let pathBackup;
|
||||
let decoder;
|
||||
|
||||
// Write to the store, and check that a backup is created first
|
||||
add_task(function test_first_write_backup() {
|
||||
let content = "test_1";
|
||||
let initial_content = decoder.decode(yield OS.File.read(pathStore));
|
||||
function promise_check_exist(path, shouldExist) {
|
||||
return Task.spawn(function*() {
|
||||
do_print("Ensuring that " + path + (shouldExist?" exists":" does not exist"));
|
||||
if ((yield OS.File.exists(path)) != shouldExist) {
|
||||
throw new Error("File " + path + " should " + (shouldExist?"exist":"not exist"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
do_check_true(!(yield OS.File.exists(pathBackup)));
|
||||
yield SessionFile.write(content);
|
||||
do_check_true(yield OS.File.exists(pathBackup));
|
||||
function promise_check_contents(path, expect) {
|
||||
return Task.spawn(function*() {
|
||||
do_print("Checking whether " + path + " has the right contents");
|
||||
let actual = yield OS.File.read(path, { encoding: "utf-8"});
|
||||
if (actual != expect) {
|
||||
throw new Error("File " + path + " should contain\n\t" + expect + "\nbut contains " + actual);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let backup_content = decoder.decode(yield OS.File.read(pathBackup));
|
||||
do_check_eq(initial_content, backup_content);
|
||||
// Write to the store, and check that it creates:
|
||||
// - $Path.recovery with the new data
|
||||
// - $Path.nextUpgradeBackup with the old data
|
||||
add_task(function* test_first_write_backup() {
|
||||
let initial_content = "initial content " + Math.random();
|
||||
let new_content = "test_1 " + Math.random();
|
||||
|
||||
do_print("Before the first write, none of the files should exist");
|
||||
yield promise_check_exist(Paths.backups, false);
|
||||
|
||||
yield File.makeDir(Paths.backups);
|
||||
yield File.writeAtomic(Paths.clean, initial_content, { encoding: "utf-8" });
|
||||
yield SessionFile.write(new_content);
|
||||
|
||||
do_print("After first write, a few files should have been created");
|
||||
yield promise_check_exist(Paths.backups, true);
|
||||
yield promise_check_exist(Paths.clean, false);
|
||||
yield promise_check_exist(Paths.cleanBackup, true);
|
||||
yield promise_check_exist(Paths.recovery, true);
|
||||
yield promise_check_exist(Paths.recoveryBackup, false);
|
||||
yield promise_check_exist(Paths.nextUpgradeBackup, true);
|
||||
|
||||
yield promise_check_contents(Paths.recovery, new_content);
|
||||
yield promise_check_contents(Paths.nextUpgradeBackup, initial_content);
|
||||
});
|
||||
|
||||
// Write to the store again, and check that the backup is not updated
|
||||
add_task(function test_second_write_no_backup() {
|
||||
let content = "test_2";
|
||||
let initial_content = decoder.decode(yield OS.File.read(pathStore));
|
||||
let initial_backup_content = decoder.decode(yield OS.File.read(pathBackup));
|
||||
// Write to the store again, and check that
|
||||
// - $Path.clean is not written
|
||||
// - $Path.recovery contains the new data
|
||||
// - $Path.recoveryBackup contains the previous data
|
||||
add_task(function* test_second_write_no_backup() {
|
||||
let new_content = "test_2 " + Math.random();
|
||||
let previous_backup_content = yield File.read(Paths.recovery, { encoding: "utf-8" });
|
||||
|
||||
yield SessionFile.write(content);
|
||||
yield OS.File.remove(Paths.cleanBackup);
|
||||
|
||||
let written_content = decoder.decode(yield OS.File.read(pathStore));
|
||||
do_check_eq(content, written_content);
|
||||
yield SessionFile.write(new_content);
|
||||
|
||||
yield promise_check_exist(Paths.backups, true);
|
||||
yield promise_check_exist(Paths.clean, false);
|
||||
yield promise_check_exist(Paths.cleanBackup, false);
|
||||
yield promise_check_exist(Paths.recovery, true);
|
||||
yield promise_check_exist(Paths.nextUpgradeBackup, true);
|
||||
|
||||
yield promise_check_contents(Paths.recovery, new_content);
|
||||
yield promise_check_contents(Paths.recoveryBackup, previous_backup_content);
|
||||
});
|
||||
|
||||
// Make sure that we create $Paths.clean and remove $Paths.recovery*
|
||||
// upon shutdown
|
||||
add_task(function* test_shutdown() {
|
||||
let output = "test_3 " + Math.random();
|
||||
|
||||
yield File.writeAtomic(Paths.recovery, "I should disappear");
|
||||
yield File.writeAtomic(Paths.recoveryBackup, "I should also disappear");
|
||||
|
||||
yield SessionWorker.post("write", [output, { isFinalWrite: true, performShutdownCleanup: true}]);
|
||||
|
||||
do_check_false((yield File.exists(Paths.recovery)));
|
||||
do_check_false((yield File.exists(Paths.recoveryBackup)));
|
||||
let input = yield File.read(Paths.clean, { encoding: "utf-8"});
|
||||
do_check_eq(input, output);
|
||||
|
||||
let backup_content = decoder.decode(yield OS.File.read(pathBackup));
|
||||
do_check_eq(initial_backup_content, backup_content);
|
||||
});
|
||||
|
|
|
@ -7,7 +7,6 @@ support-files =
|
|||
data/sessionstore_invalid.js
|
||||
data/sessionstore_valid.js
|
||||
|
||||
[test_backup.js]
|
||||
[test_backup_once.js]
|
||||
[test_startup_nosession_async.js]
|
||||
[test_startup_session_async.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче