Files
tubestation/toolkit/modules/tests/xpcshell/test_JSONFile.js

243 lines
6.3 KiB
JavaScript

/**
* Tests the JSONFile object.
*/
"use strict";
// Globals
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths",
"resource://gre/modules/DownloadPaths.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "JSONFile",
"resource://gre/modules/JSONFile.jsm");
let gFileCounter = Math.floor(Math.random() * 1000000);
/**
* Returns a reference to a temporary file, that is guaranteed not to exist, and
* to have never been created before.
*
* @param aLeafName
* Suggested leaf name for the file to be created.
*
* @return nsIFile pointing to a non-existent file in a temporary directory.
*
* @note It is not enough to delete the file if it exists, or to delete the file
* after calling nsIFile.createUnique, because on Windows the delete
* operation in the file system may still be pending, preventing a new
* file with the same name to be created.
*/
function getTempFile(aLeafName)
{
// Prepend a serial number to the extension in the suggested leaf name.
let [base, ext] = DownloadPaths.splitBaseNameAndExtension(aLeafName);
let leafName = base + "-" + gFileCounter + ext;
gFileCounter++;
// Get a file reference under the temporary directory for this test file.
let file = FileUtils.getFile("TmpD", [leafName]);
do_check_false(file.exists());
do_register_cleanup(function () {
if (file.exists()) {
file.remove(false);
}
});
return file;
}
const TEST_STORE_FILE_NAME = "test-store.json";
const TEST_DATA = {
number: 123,
string: "test",
object: {
prop1: 1,
prop2: 2,
},
};
// Tests
add_task(function* test_save_reload()
{
let storeForSave = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path,
});
yield storeForSave.load();
do_check_true(storeForSave.dataReady);
do_check_matches(storeForSave.data, {});
Object.assign(storeForSave.data, TEST_DATA);
yield new Promise((resolve) => {
let save = storeForSave._save.bind(storeForSave);
storeForSave._save = () => {
save();
resolve();
};
storeForSave.saveSoon();
});
let storeForLoad = new JSONFile({
path: storeForSave.path,
});
yield storeForLoad.load();
Assert.deepEqual(storeForLoad.data, TEST_DATA);
});
add_task(function* test_load_sync()
{
let storeForSave = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path
});
yield storeForSave.load();
Object.assign(storeForSave.data, TEST_DATA);
yield storeForSave._save();
let storeForLoad = new JSONFile({
path: storeForSave.path,
});
storeForLoad.ensureDataReady();
Assert.deepEqual(storeForLoad.data, TEST_DATA);
});
add_task(function* test_load_with_dataPostProcessor()
{
let storeForSave = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path
});
yield storeForSave.load();
Object.assign(storeForSave.data, TEST_DATA);
yield storeForSave._save();
let random = Math.random();
let storeForLoad = new JSONFile({
path: storeForSave.path,
dataPostProcessor: (data) => {
Assert.deepEqual(data, TEST_DATA);
data.test = random;
return data;
},
});
yield storeForLoad.load();
do_check_eq(storeForLoad.data.test, random);
});
add_task(function* test_load_with_dataPostProcessor_fails()
{
let store = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path,
dataPostProcessor: () => {
throw new Error("dataPostProcessor fails.");
},
});
yield Assert.rejects(store.load(), /dataPostProcessor fails\./);
do_check_false(store.dataReady);
});
add_task(function* test_load_sync_with_dataPostProcessor_fails()
{
let store = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path,
dataPostProcessor: () => {
throw new Error("dataPostProcessor fails.");
},
});
Assert.throws(() => store.ensureDataReady(), /dataPostProcessor fails\./);
do_check_false(store.dataReady);
});
/**
* Loads data from a string in a predefined format. The purpose of this test is
* to verify that the JSON format used in previous versions can be loaded.
*/
add_task(function* test_load_string_predefined()
{
let store = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path,
});
let string =
"{\"number\":123,\"string\":\"test\",\"object\":{\"prop1\":1,\"prop2\":2}}";
yield OS.File.writeAtomic(store.path, new TextEncoder().encode(string),
{ tmpPath: store.path + ".tmp" });
yield store.load();
Assert.deepEqual(store.data, TEST_DATA);
});
/**
* Loads data from a malformed JSON string.
*/
add_task(function* test_load_string_malformed()
{
let store = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path,
});
let string = "{\"number\":123,\"string\":\"test\",\"object\":{\"prop1\":1,";
yield OS.File.writeAtomic(store.path, new TextEncoder().encode(string),
{ tmpPath: store.path + ".tmp" });
yield store.load();
// A backup file should have been created.
do_check_true(yield OS.File.exists(store.path + ".corrupt"));
yield OS.File.remove(store.path + ".corrupt");
// The store should be ready to accept new data.
do_check_true(store.dataReady);
do_check_matches(store.data, {});
});
/**
* Loads data from a malformed JSON string, using the synchronous initialization
* path.
*/
add_task(function* test_load_string_malformed_sync()
{
let store = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path,
});
let string = "{\"number\":123,\"string\":\"test\",\"object\":{\"prop1\":1,";
yield OS.File.writeAtomic(store.path, new TextEncoder().encode(string),
{ tmpPath: store.path + ".tmp" });
store.ensureDataReady();
// A backup file should have been created.
do_check_true(yield OS.File.exists(store.path + ".corrupt"));
yield OS.File.remove(store.path + ".corrupt");
// The store should be ready to accept new data.
do_check_true(store.dataReady);
do_check_matches(store.data, {});
});