Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'android' OR os == 'android'
- Manifest: toolkit/components/passwordmgr/test/unit/xpcshell.toml
/* Any copyright is dedicated to the Public Domain.
/**
* Tests if logins-backup.json is used correctly in the event that logins.json is missing or corrupt.
*/
"use strict";
ChromeUtils.defineESModuleGetters(this, {
LoginStore: "resource://gre/modules/LoginStore.sys.mjs",
});
const { TestUtils } = ChromeUtils.importESModule(
);
const { TelemetryTestUtils } = ChromeUtils.importESModule(
);
const rawLogin1 = {
id: 1,
httpRealm: null,
usernameField: "field_" + String.fromCharCode(533, 537, 7570, 345),
passwordField: "field_" + String.fromCharCode(421, 259, 349, 537),
encryptedUsername: "(test)",
encryptedPassword: "(test)",
guid: "(test)",
encType: Ci.nsILoginManagerCrypto.ENCTYPE_SDR,
timeCreated: Date.now(),
timeLastUsed: Date.now(),
timePasswordChanged: Date.now(),
timesUsed: 1,
};
const rawLogin2 = {
id: 2,
httpRealm: null,
usernameField: "field_2" + String.fromCharCode(533, 537, 7570, 345),
passwordField: "field_2" + String.fromCharCode(421, 259, 349, 537),
encryptedUsername: "(test2)",
encryptedPassword: "(test2)",
guid: "(test2)",
encType: Ci.nsILoginManagerCrypto.ENCTYPE_SDR,
timeCreated: Date.now(),
timeLastUsed: Date.now(),
timePasswordChanged: Date.now(),
timesUsed: 1,
};
// Enable the collection (during test) for all products so even products
// that don't collect the data will be able to run the test without failure.
Services.prefs.setBoolPref(
"toolkit.telemetry.testing.overrideProductsCheck",
true
);
/**
* Tests that logins-backup.json can be used by JSONFile.load() when logins.json is missing or cannot be read.
*/
add_task(async function test_logins_store_missing_or_corrupt_with_backup() {
const loginsStorePath = PathUtils.join(PathUtils.profileDir, "logins.json");
const loginsStoreBackup = PathUtils.join(
PathUtils.profileDir,
"logins-backup.json"
);
// Get store.data ready.
let store = new LoginStore(loginsStorePath, loginsStoreBackup);
await store.load();
// Files should not exist at start up.
Assert.ok(!(await IOUtils.exists(store.path)), "No store file at start up");
Assert.ok(
!(await IOUtils.exists(store._options.backupTo)),
"No backup file at start up"
);
// Add logins to create logins.json and logins-backup.json.
store.data.logins.push(rawLogin1);
await store._save();
Assert.ok(await IOUtils.exists(store.path));
store.data.logins.push(rawLogin2);
await store._save();
Assert.ok(await IOUtils.exists(store._options.backupTo));
// Remove logins.json and see if logins-backup.json will be used.
await IOUtils.remove(store.path);
store.data.logins = [];
store.dataReady = false;
Assert.ok(!(await IOUtils.exists(store.path)));
Assert.ok(await IOUtils.exists(store._options.backupTo));
// Clear any telemetry events recorded in the jsonfile category previously.
Services.telemetry.clearEvents();
await store.load();
Assert.ok(
await IOUtils.exists(store.path),
"logins.json is restored as expected after it went missing"
);
Assert.ok(await IOUtils.exists(store._options.backupTo));
Assert.equal(
store.data.logins.length,
1,
"Logins backup was used successfully when logins.json was missing"
);
TelemetryTestUtils.assertEvents(
[
["jsonfile", "load", "logins"],
["jsonfile", "load", "logins", "used_backup"],
],
{},
{ clear: true }
);
info(
"Telemetry was recorded accurately when logins-backup.json is used when logins.json was missing"
);
// Corrupt the logins.json file.
await IOUtils.writeUTF8(store.path, string, {
tmpPath: `${store.path}.tmp`,
});
// Clear events recorded in the jsonfile category previously.
Services.telemetry.clearEvents();
// Try to load the corrupt file.
store.data.logins = [];
store.dataReady = false;
await store.load();
Assert.ok(
await IOUtils.exists(`${store.path}.corrupt`),
"logins.json.corrupt created"
);
Assert.ok(
await IOUtils.exists(store.path),
"logins.json is restored after it was corrupted"
);
// Data should be loaded from logins-backup.json.
Assert.ok(await IOUtils.exists(store._options.backupTo));
Assert.equal(
store.data.logins.length,
1,
"Logins backup was used successfully when logins.json was corrupt"
);
TelemetryTestUtils.assertEvents(
[
["jsonfile", "load", "logins", ""],
["jsonfile", "load", "logins", "invalid_json"],
["jsonfile", "load", "logins", "used_backup"],
],
{},
{ clear: true }
);
info(
"Telemetry was recorded accurately when logins-backup.json is used when logins.json was corrupt"
);
// Clean up before we start the second part of the test.
await IOUtils.remove(`${store.path}.corrupt`);
// Test that the backup file can be used by JSONFile.ensureDataReady() correctly when logins.json is missing.
// Remove logins.json
await IOUtils.remove(store.path);
store.data.logins = [];
store.dataReady = false;
Assert.ok(!(await IOUtils.exists(store.path)));
Assert.ok(await IOUtils.exists(store._options.backupTo));
store.ensureDataReady();
// Important to check here if logins.json is restored as expected
// after it went missing.
await IOUtils.exists(store.path);
Assert.ok(await IOUtils.exists(store._options.backupTo));
await TestUtils.waitForCondition(() => {
return store.data.logins.length == 1;
});
// Test that the backup file is used by JSONFile.ensureDataReady() when logins.json is corrupt.
// Corrupt the logins.json file.
await IOUtils.writeUTF8(store.path, string, {
tmpPath: `${store.path}.tmp`,
});
// Try to load the corrupt file.
store.data.logins = [];
store.dataReady = false;
store.ensureDataReady();
Assert.ok(
await IOUtils.exists(`${store.path}.corrupt`),
"logins.json.corrupt created"
);
Assert.ok(
await IOUtils.exists(store.path),
"logins.json is restored after it was corrupted"
);
// Data should be loaded from logins-backup.json.
Assert.ok(await IOUtils.exists(store._options.backupTo));
await TestUtils.waitForCondition(() => {
return store.data.logins.length == 1;
});
});