summaryrefslogtreecommitdiffstats
path: root/browser/components/sessionstore/test/unit
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/sessionstore/test/unit')
-rw-r--r--browser/components/sessionstore/test/unit/data/sessionCheckpoints_all.json11
-rw-r--r--browser/components/sessionstore/test/unit/data/sessionstore_invalid.js3
-rw-r--r--browser/components/sessionstore/test/unit/data/sessionstore_valid.js3
-rw-r--r--browser/components/sessionstore/test/unit/head.js36
-rw-r--r--browser/components/sessionstore/test/unit/test_backup_once.js137
-rw-r--r--browser/components/sessionstore/test/unit/test_final_write_cleanup.js118
-rw-r--r--browser/components/sessionstore/test/unit/test_histogram_corrupt_files.js117
-rw-r--r--browser/components/sessionstore/test/unit/test_migration_lz4compression.js151
-rw-r--r--browser/components/sessionstore/test/unit/test_startup_invalid_session.js27
-rw-r--r--browser/components/sessionstore/test/unit/test_startup_nosession_async.js19
-rw-r--r--browser/components/sessionstore/test/unit/test_startup_session_async.js32
-rw-r--r--browser/components/sessionstore/test/unit/xpcshell.ini21
12 files changed, 675 insertions, 0 deletions
diff --git a/browser/components/sessionstore/test/unit/data/sessionCheckpoints_all.json b/browser/components/sessionstore/test/unit/data/sessionCheckpoints_all.json
new file mode 100644
index 0000000000..e02c421c3b
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/data/sessionCheckpoints_all.json
@@ -0,0 +1,11 @@
+{
+ "profile-after-change": true,
+ "final-ui-startup": true,
+ "sessionstore-windows-restored": true,
+ "quit-application-granted": true,
+ "quit-application": true,
+ "sessionstore-final-state-write-complete": true,
+ "profile-change-net-teardown": true,
+ "profile-change-teardown": true,
+ "profile-before-change": true
+}
diff --git a/browser/components/sessionstore/test/unit/data/sessionstore_invalid.js b/browser/components/sessionstore/test/unit/data/sessionstore_invalid.js
new file mode 100644
index 0000000000..a8c3ff2ff9
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/data/sessionstore_invalid.js
@@ -0,0 +1,3 @@
+{
+ "windows": // invalid json
+}
diff --git a/browser/components/sessionstore/test/unit/data/sessionstore_valid.js b/browser/components/sessionstore/test/unit/data/sessionstore_valid.js
new file mode 100644
index 0000000000..f9511f29f6
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/data/sessionstore_valid.js
@@ -0,0 +1,3 @@
+{
+ "windows": []
+} \ No newline at end of file
diff --git a/browser/components/sessionstore/test/unit/head.js b/browser/components/sessionstore/test/unit/head.js
new file mode 100644
index 0000000000..b342a886fb
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/head.js
@@ -0,0 +1,36 @@
+ChromeUtils.defineESModuleGetters(this, {
+ SessionStartup: "resource:///modules/sessionstore/SessionStartup.sys.mjs",
+});
+
+// Call a function once initialization of SessionStartup is complete
+function afterSessionStartupInitialization(cb) {
+ info("Waiting for session startup initialization");
+ let observer = function () {
+ try {
+ info("Session startup initialization observed");
+ Services.obs.removeObserver(observer, "sessionstore-state-finalized");
+ cb();
+ } catch (ex) {
+ do_throw(ex);
+ }
+ };
+ Services.obs.addObserver(observer, "sessionstore-state-finalized");
+
+ // We need the Crash Monitor initialized for sessionstartup to run
+ // successfully.
+ const { CrashMonitor } = ChromeUtils.importESModule(
+ "resource://gre/modules/CrashMonitor.sys.mjs"
+ );
+ CrashMonitor.init();
+
+ // Start sessionstartup initialization.
+ SessionStartup.init();
+}
+
+// Compress the source file using lz4 and put the result to destination file.
+// After that, source file is deleted.
+async function writeCompressedFile(source, destination) {
+ let s = await IOUtils.read(source);
+ await IOUtils.write(destination, s, { compress: true });
+ await IOUtils.remove(source);
+}
diff --git a/browser/components/sessionstore/test/unit/test_backup_once.js b/browser/components/sessionstore/test/unit/test_backup_once.js
new file mode 100644
index 0000000000..db566491d5
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/test_backup_once.js
@@ -0,0 +1,137 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { SessionWriter } = ChromeUtils.importESModule(
+ "resource:///modules/sessionstore/SessionWriter.sys.mjs"
+);
+
+// Make sure that we have a profile before initializing SessionFile.
+const profd = do_get_profile();
+const { SessionFile } = ChromeUtils.importESModule(
+ "resource:///modules/sessionstore/SessionFile.sys.mjs"
+);
+const Paths = SessionFile.Paths;
+
+// We need a XULAppInfo to initialize SessionFile
+const { updateAppInfo } = ChromeUtils.importESModule(
+ "resource://testing-common/AppInfo.sys.mjs"
+);
+updateAppInfo({
+ name: "SessionRestoreTest",
+ ID: "{230de50e-4cd1-11dc-8314-0800200c9a66}",
+ version: "1",
+ platformVersion: "",
+});
+
+add_setup(async function () {
+ let source = do_get_file("data/sessionstore_valid.js");
+ source.copyTo(profd, "sessionstore.js");
+ await writeCompressedFile(Paths.clean.replace("jsonlz4", "js"), Paths.clean);
+
+ // Finish initialization of SessionFile
+ await SessionFile.read();
+});
+
+function promise_check_exist(path, shouldExist) {
+ return (async function () {
+ info(
+ "Ensuring that " + path + (shouldExist ? " exists" : " does not exist")
+ );
+ if ((await IOUtils.exists(path)) != shouldExist) {
+ throw new Error(
+ "File" + path + " should " + (shouldExist ? "exist" : "not exist")
+ );
+ }
+ })();
+}
+
+function promise_check_contents(path, expect) {
+ return (async function () {
+ info("Checking whether " + path + " has the right contents");
+ let actual = await IOUtils.readJSON(path, {
+ decompress: true,
+ });
+ Assert.deepEqual(
+ actual,
+ expect,
+ `File ${path} contains the expected data.`
+ );
+ })();
+}
+
+function generateFileContents(id) {
+ let url = `http://example.com/test_backup_once#${id}_${Math.random()}`;
+ return { windows: [{ tabs: [{ entries: [{ url }], index: 1 }] }] };
+}
+
+// Write to the store, and check that it creates:
+// - $Path.recovery with the new data
+// - $Path.nextUpgradeBackup with the old data
+add_task(async function test_first_write_backup() {
+ let initial_content = generateFileContents("initial");
+ let new_content = generateFileContents("test_1");
+
+ info("Before the first write, none of the files should exist");
+ await promise_check_exist(Paths.backups, false);
+
+ await IOUtils.makeDirectory(Paths.backups);
+ await IOUtils.writeJSON(Paths.clean, initial_content, {
+ compress: true,
+ });
+ await SessionFile.write(new_content);
+
+ info("After first write, a few files should have been created");
+ await promise_check_exist(Paths.backups, true);
+ await promise_check_exist(Paths.clean, false);
+ await promise_check_exist(Paths.cleanBackup, true);
+ await promise_check_exist(Paths.recovery, true);
+ await promise_check_exist(Paths.recoveryBackup, false);
+ await promise_check_exist(Paths.nextUpgradeBackup, true);
+
+ await promise_check_contents(Paths.recovery, new_content);
+ await promise_check_contents(Paths.nextUpgradeBackup, initial_content);
+});
+
+// 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(async function test_second_write_no_backup() {
+ let new_content = generateFileContents("test_2");
+ let previous_backup_content = await IOUtils.readJSON(Paths.recovery, {
+ decompress: true,
+ });
+
+ await IOUtils.remove(Paths.cleanBackup);
+
+ await SessionFile.write(new_content);
+
+ await promise_check_exist(Paths.backups, true);
+ await promise_check_exist(Paths.clean, false);
+ await promise_check_exist(Paths.cleanBackup, false);
+ await promise_check_exist(Paths.recovery, true);
+ await promise_check_exist(Paths.nextUpgradeBackup, true);
+
+ await promise_check_contents(Paths.recovery, new_content);
+ await promise_check_contents(Paths.recoveryBackup, previous_backup_content);
+});
+
+// Make sure that we create $Paths.clean and remove $Paths.recovery*
+// upon shutdown
+add_task(async function test_shutdown() {
+ let output = generateFileContents("test_3");
+
+ await IOUtils.writeUTF8(Paths.recovery, "I should disappear");
+ await IOUtils.writeUTF8(Paths.recoveryBackup, "I should also disappear");
+
+ await SessionWriter.write(output, {
+ isFinalWrite: true,
+ performShutdownCleanup: true,
+ });
+
+ Assert.ok(!(await IOUtils.exists(Paths.recovery)));
+ Assert.ok(!(await IOUtils.exists(Paths.recoveryBackup)));
+ await promise_check_contents(Paths.clean, output);
+});
diff --git a/browser/components/sessionstore/test/unit/test_final_write_cleanup.js b/browser/components/sessionstore/test/unit/test_final_write_cleanup.js
new file mode 100644
index 0000000000..503dd71420
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/test_final_write_cleanup.js
@@ -0,0 +1,118 @@
+"use strict";
+
+/**
+ * This test ensures that we correctly clean up the session state when
+ * writing with isFinalWrite, which is used on shutdown. It tests that each
+ * tab's shistory is capped to a maximum number of preceding and succeeding
+ * entries.
+ */
+
+const { SessionWriter } = ChromeUtils.importESModule(
+ "resource:///modules/sessionstore/SessionWriter.sys.mjs"
+);
+
+// Make sure that we have a profile before initializing SessionFile.
+do_get_profile();
+const {
+ SessionFile: { Paths },
+} = ChromeUtils.importESModule(
+ "resource:///modules/sessionstore/SessionFile.sys.mjs"
+);
+
+const MAX_ENTRIES = 9;
+const URL = "http://example.com/#";
+
+Cu.importGlobalProperties(["structuredClone"]);
+
+async function prepareWithLimit(back, fwd) {
+ SessionWriter.init("empty", false, Paths, {
+ maxSerializeBack: back,
+ maxSerializeForward: fwd,
+ maxUpgradeBackups: 3,
+ });
+ await SessionWriter.wipe();
+}
+
+add_setup(async function () {
+ registerCleanupFunction(() => SessionWriter.wipe());
+});
+
+function createSessionState(index) {
+ // Generate the tab state entries and set the one-based
+ // tab-state index to the middle session history entry.
+ let tabState = { entries: [], index };
+ for (let i = 0; i < MAX_ENTRIES; i++) {
+ tabState.entries.push({ url: URL + i });
+ }
+
+ return { windows: [{ tabs: [tabState] }] };
+}
+
+async function writeAndParse(state, path, options = {}) {
+ // We clone here because `write` can change the data passed.
+ let data = structuredClone(state);
+ await SessionWriter.write(data, options);
+ return IOUtils.readJSON(path, { decompress: true });
+}
+
+add_task(async function test_shistory_cap_none() {
+ let state = createSessionState(5);
+
+ // Don't limit the number of shistory entries.
+ await prepareWithLimit(-1, -1);
+
+ // Check that no caps are applied.
+ let diskState = await writeAndParse(state, Paths.clean, {
+ isFinalWrite: true,
+ });
+ Assert.deepEqual(state, diskState, "no cap applied");
+});
+
+add_task(async function test_shistory_cap_middle() {
+ let state = createSessionState(5);
+ await prepareWithLimit(2, 3);
+
+ // Cap is only applied on clean shutdown.
+ let diskState = await writeAndParse(state, Paths.recovery);
+ Assert.deepEqual(state, diskState, "no cap applied");
+
+ // Check that the right number of shistory entries was discarded
+ // and the shistory index updated accordingly.
+ diskState = await writeAndParse(state, Paths.clean, { isFinalWrite: true });
+ let tabState = state.windows[0].tabs[0];
+ tabState.entries = tabState.entries.slice(2, 8);
+ tabState.index = 3;
+ Assert.deepEqual(state, diskState, "cap applied");
+});
+
+add_task(async function test_shistory_cap_lower_bound() {
+ let state = createSessionState(1);
+ await prepareWithLimit(5, 5);
+
+ // Cap is only applied on clean shutdown.
+ let diskState = await writeAndParse(state, Paths.recovery);
+ Assert.deepEqual(state, diskState, "no cap applied");
+
+ // Check that the right number of shistory entries was discarded.
+ diskState = await writeAndParse(state, Paths.clean, { isFinalWrite: true });
+ let tabState = state.windows[0].tabs[0];
+ tabState.entries = tabState.entries.slice(0, 6);
+ Assert.deepEqual(state, diskState, "cap applied");
+});
+
+add_task(async function test_shistory_cap_upper_bound() {
+ let state = createSessionState(MAX_ENTRIES);
+ await prepareWithLimit(5, 5);
+
+ // Cap is only applied on clean shutdown.
+ let diskState = await writeAndParse(state, Paths.recovery);
+ Assert.deepEqual(state, diskState, "no cap applied");
+
+ // Check that the right number of shistory entries was discarded
+ // and the shistory index updated accordingly.
+ diskState = await writeAndParse(state, Paths.clean, { isFinalWrite: true });
+ let tabState = state.windows[0].tabs[0];
+ tabState.entries = tabState.entries.slice(3);
+ tabState.index = 6;
+ Assert.deepEqual(state, diskState, "cap applied");
+});
diff --git a/browser/components/sessionstore/test/unit/test_histogram_corrupt_files.js b/browser/components/sessionstore/test/unit/test_histogram_corrupt_files.js
new file mode 100644
index 0000000000..2c469ed3b4
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/test_histogram_corrupt_files.js
@@ -0,0 +1,117 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * The primary purpose of this test is to ensure that
+ * the sessionstore component records information about
+ * corrupted backup files into a histogram.
+ */
+
+"use strict";
+
+const Telemetry = Services.telemetry;
+const HistogramId = "FX_SESSION_RESTORE_ALL_FILES_CORRUPT";
+
+// Prepare the session file.
+do_get_profile();
+const { SessionFile } = ChromeUtils.importESModule(
+ "resource:///modules/sessionstore/SessionFile.sys.mjs"
+);
+
+/**
+ * A utility function for resetting the histogram and the contents
+ * of the backup directory. This will also compress the file using lz4 compression.
+ */
+function promise_reset_session(backups = {}) {
+ return (async function () {
+ // Reset the histogram.
+ Telemetry.getHistogramById(HistogramId).clear();
+
+ // Reset the contents of the backups directory
+ await IOUtils.makeDirectory(SessionFile.Paths.backups);
+ let basePath = do_get_cwd().path;
+ for (let key of SessionFile.Paths.loadOrder) {
+ if (backups.hasOwnProperty(key)) {
+ let path = backups[key];
+ const fullPath = PathUtils.join(basePath, ...path);
+ let s = await IOUtils.read(fullPath);
+ await IOUtils.write(SessionFile.Paths[key], s, {
+ compress: true,
+ });
+ } else {
+ await IOUtils.remove(SessionFile.Paths[key]);
+ }
+ }
+ })();
+}
+
+/**
+ * In order to use FX_SESSION_RESTORE_ALL_FILES_CORRUPT histogram
+ * it has to be registered in "toolkit/components/telemetry/Histograms.json".
+ * This test ensures that the histogram is registered and empty.
+ */
+add_task(async function test_ensure_histogram_exists_and_empty() {
+ let s = Telemetry.getHistogramById(HistogramId).snapshot();
+ Assert.equal(s.sum, 0, "Initially, the sum of probes is 0");
+});
+
+/**
+ * Makes sure that the histogram is negatively updated when no
+ * backup files are present.
+ */
+add_task(async function test_no_files_exist() {
+ // No session files are available to SessionFile.
+ await promise_reset_session();
+
+ await SessionFile.read();
+ // Checking if the histogram is updated negatively
+ let h = Telemetry.getHistogramById(HistogramId);
+ let s = h.snapshot();
+ Assert.equal(s.values[0], 1, "One probe for the 'false' bucket.");
+ Assert.equal(s.values[1], 0, "No probes in the 'true' bucket.");
+});
+
+/**
+ * Makes sure that the histogram is negatively updated when at least one
+ * backup file is not corrupted.
+ */
+add_task(async function test_one_file_valid() {
+ // Corrupting some backup files.
+ let invalidSession = ["data", "sessionstore_invalid.js"];
+ let validSession = ["data", "sessionstore_valid.js"];
+ await promise_reset_session({
+ clean: invalidSession,
+ cleanBackup: validSession,
+ recovery: invalidSession,
+ recoveryBackup: invalidSession,
+ });
+
+ await SessionFile.read();
+ // Checking if the histogram is updated negatively.
+ let h = Telemetry.getHistogramById(HistogramId);
+ let s = h.snapshot();
+ Assert.equal(s.values[0], 1, "One probe for the 'false' bucket.");
+ Assert.equal(s.values[1], 0, "No probes in the 'true' bucket.");
+});
+
+/**
+ * Makes sure that the histogram is positively updated when all
+ * backup files are corrupted.
+ */
+add_task(async function test_all_files_corrupt() {
+ // Corrupting all backup files.
+ let invalidSession = ["data", "sessionstore_invalid.js"];
+ await promise_reset_session({
+ clean: invalidSession,
+ cleanBackup: invalidSession,
+ recovery: invalidSession,
+ recoveryBackup: invalidSession,
+ });
+
+ await SessionFile.read();
+ // Checking if the histogram is positively updated.
+ let h = Telemetry.getHistogramById(HistogramId);
+ let s = h.snapshot();
+ Assert.equal(s.values[1], 1, "One probe for the 'true' bucket.");
+ Assert.equal(s.values[0], 0, "No probes in the 'false' bucket.");
+});
diff --git a/browser/components/sessionstore/test/unit/test_migration_lz4compression.js b/browser/components/sessionstore/test/unit/test_migration_lz4compression.js
new file mode 100644
index 0000000000..4d9b700d8b
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/test_migration_lz4compression.js
@@ -0,0 +1,151 @@
+"use strict";
+
+const { SessionWriter } = ChromeUtils.importESModule(
+ "resource:///modules/sessionstore/SessionWriter.sys.mjs"
+);
+
+// Make sure that we have a profile before initializing SessionFile.
+const profd = do_get_profile();
+const { SessionFile } = ChromeUtils.importESModule(
+ "resource:///modules/sessionstore/SessionFile.sys.mjs"
+);
+const Paths = SessionFile.Paths;
+
+// We need a XULAppInfo to initialize SessionFile
+const { updateAppInfo } = ChromeUtils.importESModule(
+ "resource://testing-common/AppInfo.sys.mjs"
+);
+updateAppInfo({
+ name: "SessionRestoreTest",
+ ID: "{230de50e-4cd1-11dc-8314-0800200c9a66}",
+ version: "1",
+ platformVersion: "",
+});
+
+function promise_check_exist(path, shouldExist) {
+ return (async function () {
+ info(
+ "Ensuring that " + path + (shouldExist ? " exists" : " does not exist")
+ );
+ if ((await IOUtils.exists(path)) != shouldExist) {
+ throw new Error(
+ "File " + path + " should " + (shouldExist ? "exist" : "not exist")
+ );
+ }
+ })();
+}
+
+function promise_check_contents(path, expect) {
+ return (async function () {
+ info("Checking whether " + path + " has the right contents");
+ let actual = await IOUtils.readJSON(path, {
+ decompress: true,
+ });
+ Assert.deepEqual(
+ actual,
+ expect,
+ `File ${path} contains the expected data.`
+ );
+ })();
+}
+
+// Check whether the migration from .js to .jslz4 is correct.
+add_task(async function test_migration() {
+ let source = do_get_file("data/sessionstore_valid.js");
+ source.copyTo(profd, "sessionstore.js");
+
+ // Read the content of the session store file.
+ let parsed = await IOUtils.readJSON(Paths.clean.replace("jsonlz4", "js"));
+
+ // Read the session file with .js extension.
+ let result = await SessionFile.read();
+
+ // Check whether the result is what we wanted.
+ equal(result.origin, "clean");
+ equal(result.useOldExtension, true);
+ Assert.deepEqual(
+ result.parsed,
+ parsed,
+ "result.parsed contains expected data"
+ );
+
+ // Initiate a write to ensure we write the compressed version.
+ await SessionFile.write(parsed);
+ await promise_check_exist(Paths.backups, true);
+ await promise_check_exist(Paths.clean, false);
+ await promise_check_exist(Paths.cleanBackup, true);
+ await promise_check_exist(Paths.recovery, true);
+ await promise_check_exist(Paths.recoveryBackup, false);
+ await promise_check_exist(Paths.nextUpgradeBackup, true);
+ // The deprecated $Path.clean should exist.
+ await promise_check_exist(Paths.clean.replace("jsonlz4", "js"), true);
+
+ await promise_check_contents(Paths.recovery, parsed);
+});
+
+add_task(async function test_startup_with_compressed_clean() {
+ let state = { windows: [] };
+
+ // Mare sure we have an empty profile dir.
+ await SessionFile.wipe();
+
+ // Populate session files to profile dir.
+ await IOUtils.writeJSON(Paths.clean, state, {
+ compress: true,
+ });
+ await IOUtils.makeDirectory(Paths.backups);
+ await IOUtils.writeJSON(Paths.cleanBackup, state, {
+ compress: true,
+ });
+
+ // Initiate a read.
+ let result = await SessionFile.read();
+
+ // Make sure we read correct session file and its content.
+ equal(result.origin, "clean");
+ equal(result.useOldExtension, false);
+ Assert.deepEqual(
+ state,
+ result.parsed,
+ "result.parsed contains expected data"
+ );
+});
+
+add_task(async function test_empty_profile_dir() {
+ // Make sure that we have empty profile dir.
+ await SessionFile.wipe();
+ await promise_check_exist(Paths.backups, false);
+ await promise_check_exist(Paths.clean, false);
+ await promise_check_exist(Paths.cleanBackup, false);
+ await promise_check_exist(Paths.recovery, false);
+ await promise_check_exist(Paths.recoveryBackup, false);
+ await promise_check_exist(Paths.nextUpgradeBackup, false);
+ await promise_check_exist(Paths.backups.replace("jsonlz4", "js"), false);
+ await promise_check_exist(Paths.clean.replace("jsonlz4", "js"), false);
+ await promise_check_exist(Paths.cleanBackup.replace("lz4", ""), false);
+ await promise_check_exist(Paths.recovery.replace("jsonlz4", "js"), false);
+ await promise_check_exist(
+ Paths.recoveryBackup.replace("jsonlz4", "js"),
+ false
+ );
+ await promise_check_exist(
+ Paths.nextUpgradeBackup.replace("jsonlz4", "js"),
+ false
+ );
+
+ // Initiate a read and make sure that we are in empty state.
+ let result = await SessionFile.read();
+ equal(result.origin, "empty");
+ equal(result.noFilesFound, true);
+
+ // Create a state to store.
+ let state = { windows: [] };
+ await SessionWriter.write(state, { isFinalWrite: true });
+
+ // Check session files are created, but not deprecated ones.
+ await promise_check_exist(Paths.clean, true);
+ await promise_check_exist(Paths.clean.replace("jsonlz4", "js"), false);
+
+ // Check session file' content is correct.
+ await promise_check_contents(Paths.clean, state);
+});
diff --git a/browser/components/sessionstore/test/unit/test_startup_invalid_session.js b/browser/components/sessionstore/test/unit/test_startup_invalid_session.js
new file mode 100644
index 0000000000..50960b1d43
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/test_startup_invalid_session.js
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function run_test() {
+ let profd = do_get_profile();
+ var SessionFile = ChromeUtils.importESModule(
+ "resource:///modules/sessionstore/SessionFile.sys.mjs"
+ ).SessionFile;
+
+ let sourceSession = do_get_file("data/sessionstore_invalid.js");
+ sourceSession.copyTo(profd, "sessionstore.js");
+
+ let sourceCheckpoints = do_get_file("data/sessionCheckpoints_all.json");
+ sourceCheckpoints.copyTo(profd, "sessionCheckpoints.json");
+
+ // Compress sessionstore.js to sessionstore.jsonlz4
+ // and remove sessionstore.js
+ let oldExtSessionFile = SessionFile.Paths.clean.replace("jsonlz4", "js");
+ writeCompressedFile(oldExtSessionFile, SessionFile.Paths.clean).then(() => {
+ afterSessionStartupInitialization(function cb() {
+ Assert.equal(SessionStartup.sessionType, SessionStartup.NO_SESSION);
+ do_test_finished();
+ });
+ });
+
+ do_test_pending();
+}
diff --git a/browser/components/sessionstore/test/unit/test_startup_nosession_async.js b/browser/components/sessionstore/test/unit/test_startup_nosession_async.js
new file mode 100644
index 0000000000..259c393e63
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/test_startup_nosession_async.js
@@ -0,0 +1,19 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test SessionStartup.sessionType in the following scenario:
+// - no sessionstore.js;
+// - the session store has been loaded, so no need to go
+// through the synchronous fallback
+
+function run_test() {
+ // Initialize the profile (the session startup uses it)
+ do_get_profile();
+
+ do_test_pending();
+
+ afterSessionStartupInitialization(function cb() {
+ Assert.equal(SessionStartup.sessionType, SessionStartup.NO_SESSION);
+ do_test_finished();
+ });
+}
diff --git a/browser/components/sessionstore/test/unit/test_startup_session_async.js b/browser/components/sessionstore/test/unit/test_startup_session_async.js
new file mode 100644
index 0000000000..a61c9fe422
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/test_startup_session_async.js
@@ -0,0 +1,32 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test SessionStartup.sessionType in the following scenario:
+// - valid sessionstore.js;
+// - valid sessionCheckpoints.json with all checkpoints;
+// - the session store has been loaded
+
+function run_test() {
+ let profd = do_get_profile();
+ var SessionFile = ChromeUtils.importESModule(
+ "resource:///modules/sessionstore/SessionFile.sys.mjs"
+ ).SessionFile;
+
+ let sourceSession = do_get_file("data/sessionstore_valid.js");
+ sourceSession.copyTo(profd, "sessionstore.js");
+
+ let sourceCheckpoints = do_get_file("data/sessionCheckpoints_all.json");
+ sourceCheckpoints.copyTo(profd, "sessionCheckpoints.json");
+
+ // Compress sessionstore.js to sessionstore.jsonlz4
+ // and remove sessionstore.js
+ let oldExtSessionFile = SessionFile.Paths.clean.replace("jsonlz4", "js");
+ writeCompressedFile(oldExtSessionFile, SessionFile.Paths.clean).then(() => {
+ afterSessionStartupInitialization(function cb() {
+ Assert.equal(SessionStartup.sessionType, SessionStartup.DEFER_SESSION);
+ do_test_finished();
+ });
+ });
+
+ do_test_pending();
+}
diff --git a/browser/components/sessionstore/test/unit/xpcshell.ini b/browser/components/sessionstore/test/unit/xpcshell.ini
new file mode 100644
index 0000000000..b5fadb609d
--- /dev/null
+++ b/browser/components/sessionstore/test/unit/xpcshell.ini
@@ -0,0 +1,21 @@
+[DEFAULT]
+head = head.js
+tags = condprof
+firefox-appdir = browser
+skip-if = toolkit == 'android' # bug 1730213
+support-files =
+ data/sessionCheckpoints_all.json
+ data/sessionstore_invalid.js
+ data/sessionstore_valid.js
+
+[test_backup_once.js]
+skip-if = condprof # 1769154
+[test_final_write_cleanup.js]
+[test_histogram_corrupt_files.js]
+[test_migration_lz4compression.js]
+skip-if = condprof # 1769154
+[test_startup_nosession_async.js]
+skip-if = condprof # 1769154
+[test_startup_session_async.js]
+[test_startup_invalid_session.js]
+skip-if = condprof # 1769154