diff options
Diffstat (limited to 'toolkit/modules/tests/xpcshell/test_JSONFile.js')
-rw-r--r-- | toolkit/modules/tests/xpcshell/test_JSONFile.js | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/toolkit/modules/tests/xpcshell/test_JSONFile.js b/toolkit/modules/tests/xpcshell/test_JSONFile.js new file mode 100644 index 000000000..77e8c55b9 --- /dev/null +++ b/toolkit/modules/tests/xpcshell/test_JSONFile.js @@ -0,0 +1,242 @@ +/** + * 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, {}); +}); |