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:
David Rajchenbach-Teller 2014-06-24 16:51:22 +02:00
Родитель dda55660fc
Коммит 409d9e236e
14 изменённых файлов: 402 добавлений и 315 удалений

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

@ -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]