forked from mirrors/gecko-dev
		
	 918ed6c474
			
		
	
	
		918ed6c474
		
	
	
	
	
		
			
			This was done using the following script:
37e3803c7a/processors/chromeutils-import.jsm
MozReview-Commit-ID: 1Nc3XDu0wGl
--HG--
extra : source : 12fc4dee861c812fd2bd032c63ef17af61800c70
extra : intermediate-source : 34c999fa006bffe8705cf50c54708aa21a962e62
extra : histedit_source : b2be2c5e5d226e6c347312456a6ae339c1e634b0
		
	
			
		
			
				
	
	
		
			330 lines
		
	
	
	
		
			9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			330 lines
		
	
	
	
		
			9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * Tests the JSONFile object.
 | |
|  */
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| // Globals
 | |
| ChromeUtils.defineModuleGetter(this, "AsyncShutdown",
 | |
|                                "resource://gre/modules/AsyncShutdown.jsm");
 | |
| ChromeUtils.defineModuleGetter(this, "DownloadPaths",
 | |
|                                "resource://gre/modules/DownloadPaths.jsm");
 | |
| ChromeUtils.defineModuleGetter(this, "FileUtils",
 | |
|                                "resource://gre/modules/FileUtils.jsm");
 | |
| ChromeUtils.defineModuleGetter(this, "OS",
 | |
|                                "resource://gre/modules/osfile.jsm");
 | |
| ChromeUtils.defineModuleGetter(this, "JSONFile",
 | |
|                                "resource://gre/modules/JSONFile.jsm");
 | |
| ChromeUtils.defineModuleGetter(this, "FileTestUtils",
 | |
|                                "resource://testing-common/FileTestUtils.jsm");
 | |
| 
 | |
| /**
 | |
|  * Returns a reference to a temporary file that is guaranteed not to exist and
 | |
|  * is cleaned up later. See FileTestUtils.getTempFile for details.
 | |
|  */
 | |
| function getTempFile(leafName) {
 | |
|   return FileTestUtils.getTempFile(leafName);
 | |
| }
 | |
| 
 | |
| const TEST_STORE_FILE_NAME = "test-store.json";
 | |
| 
 | |
| const TEST_DATA = {
 | |
|   number: 123,
 | |
|   string: "test",
 | |
|   object: {
 | |
|     prop1: 1,
 | |
|     prop2: 2,
 | |
|   },
 | |
| };
 | |
| 
 | |
| // Tests
 | |
| 
 | |
| add_task(async function test_save_reload() {
 | |
|   let storeForSave = new JSONFile({
 | |
|     path: getTempFile(TEST_STORE_FILE_NAME).path,
 | |
|   });
 | |
| 
 | |
|   await storeForSave.load();
 | |
| 
 | |
|   Assert.ok(storeForSave.dataReady);
 | |
|   Assert.deepEqual(storeForSave.data, {});
 | |
| 
 | |
|   Object.assign(storeForSave.data, TEST_DATA);
 | |
| 
 | |
|   await new Promise((resolve) => {
 | |
|     let save = storeForSave._save.bind(storeForSave);
 | |
|     storeForSave._save = () => {
 | |
|       save();
 | |
|       resolve();
 | |
|     };
 | |
|     storeForSave.saveSoon();
 | |
|   });
 | |
| 
 | |
|   let storeForLoad = new JSONFile({
 | |
|     path: storeForSave.path,
 | |
|   });
 | |
| 
 | |
|   await storeForLoad.load();
 | |
| 
 | |
|   Assert.deepEqual(storeForLoad.data, TEST_DATA);
 | |
| });
 | |
| 
 | |
| add_task(async function test_load_sync() {
 | |
|   let storeForSave = new JSONFile({
 | |
|     path: getTempFile(TEST_STORE_FILE_NAME).path
 | |
|   });
 | |
|   await storeForSave.load();
 | |
|   Object.assign(storeForSave.data, TEST_DATA);
 | |
|   await storeForSave._save();
 | |
| 
 | |
|   let storeForLoad = new JSONFile({
 | |
|     path: storeForSave.path,
 | |
|   });
 | |
|   storeForLoad.ensureDataReady();
 | |
| 
 | |
|   Assert.deepEqual(storeForLoad.data, TEST_DATA);
 | |
| });
 | |
| 
 | |
| add_task(async function test_load_with_dataPostProcessor() {
 | |
|   let storeForSave = new JSONFile({
 | |
|     path: getTempFile(TEST_STORE_FILE_NAME).path
 | |
|   });
 | |
|   await storeForSave.load();
 | |
|   Object.assign(storeForSave.data, TEST_DATA);
 | |
|   await 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;
 | |
|     },
 | |
|   });
 | |
| 
 | |
|   await storeForLoad.load();
 | |
| 
 | |
|   Assert.equal(storeForLoad.data.test, random);
 | |
| });
 | |
| 
 | |
| add_task(async function test_load_with_dataPostProcessor_fails() {
 | |
|   let store = new JSONFile({
 | |
|     path: getTempFile(TEST_STORE_FILE_NAME).path,
 | |
|     dataPostProcessor: () => {
 | |
|       throw new Error("dataPostProcessor fails.");
 | |
|     },
 | |
|   });
 | |
| 
 | |
|   await Assert.rejects(store.load(), /dataPostProcessor fails\./);
 | |
| 
 | |
|   Assert.ok(!store.dataReady);
 | |
| });
 | |
| 
 | |
| add_task(async 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\./);
 | |
| 
 | |
|   Assert.ok(!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(async 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}}";
 | |
| 
 | |
|   await OS.File.writeAtomic(store.path, new TextEncoder().encode(string),
 | |
|                             { tmpPath: store.path + ".tmp" });
 | |
| 
 | |
|   await store.load();
 | |
| 
 | |
|   Assert.deepEqual(store.data, TEST_DATA);
 | |
| });
 | |
| 
 | |
| /**
 | |
|  * Loads data from a malformed JSON string.
 | |
|  */
 | |
| add_task(async 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,";
 | |
| 
 | |
|   await OS.File.writeAtomic(store.path, new TextEncoder().encode(string),
 | |
|                             { tmpPath: store.path + ".tmp" });
 | |
| 
 | |
|   await store.load();
 | |
| 
 | |
|   // A backup file should have been created.
 | |
|   Assert.ok(await OS.File.exists(store.path + ".corrupt"));
 | |
|   await OS.File.remove(store.path + ".corrupt");
 | |
| 
 | |
|   // The store should be ready to accept new data.
 | |
|   Assert.ok(store.dataReady);
 | |
|   Assert.deepEqual(store.data, {});
 | |
| });
 | |
| 
 | |
| /**
 | |
|  * Loads data from a malformed JSON string, using the synchronous initialization
 | |
|  * path.
 | |
|  */
 | |
| add_task(async 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,";
 | |
| 
 | |
|   await OS.File.writeAtomic(store.path, new TextEncoder().encode(string),
 | |
|                             { tmpPath: store.path + ".tmp" });
 | |
| 
 | |
|   store.ensureDataReady();
 | |
| 
 | |
|   // A backup file should have been created.
 | |
|   Assert.ok(await OS.File.exists(store.path + ".corrupt"));
 | |
|   await OS.File.remove(store.path + ".corrupt");
 | |
| 
 | |
|   // The store should be ready to accept new data.
 | |
|   Assert.ok(store.dataReady);
 | |
|   Assert.deepEqual(store.data, {});
 | |
| });
 | |
| 
 | |
| add_task(async function test_overwrite_data() {
 | |
|   let storeForSave = new JSONFile({
 | |
|     path: getTempFile(TEST_STORE_FILE_NAME).path,
 | |
|   });
 | |
| 
 | |
|   let string = `{"number":456,"string":"tset","object":{"prop1":3,"prop2":4}}`;
 | |
| 
 | |
|   await OS.File.writeAtomic(storeForSave.path, new TextEncoder().encode(string),
 | |
|                             { tmpPath: storeForSave.path + ".tmp" });
 | |
| 
 | |
|   Assert.ok(!storeForSave.dataReady);
 | |
|   storeForSave.data = TEST_DATA;
 | |
|   Assert.ok(storeForSave.dataReady);
 | |
|   Assert.equal(storeForSave.data, TEST_DATA);
 | |
| 
 | |
|   await new Promise((resolve) => {
 | |
|     let save = storeForSave._save.bind(storeForSave);
 | |
|     storeForSave._save = () => {
 | |
|       save();
 | |
|       resolve();
 | |
|     };
 | |
|     storeForSave.saveSoon();
 | |
|   });
 | |
| 
 | |
|   let storeForLoad = new JSONFile({
 | |
|     path: storeForSave.path,
 | |
|   });
 | |
| 
 | |
|   await storeForLoad.load();
 | |
| 
 | |
|   Assert.deepEqual(storeForLoad.data, TEST_DATA);
 | |
| });
 | |
| 
 | |
| add_task(async function test_beforeSave() {
 | |
|   let store;
 | |
|   let promiseBeforeSave = new Promise((resolve) => {
 | |
|     store = new JSONFile({
 | |
|       path: getTempFile(TEST_STORE_FILE_NAME).path,
 | |
|       beforeSave: resolve,
 | |
|       saveDelayMs: 250,
 | |
|     });
 | |
|   });
 | |
| 
 | |
|   store.saveSoon();
 | |
| 
 | |
|   await promiseBeforeSave;
 | |
| });
 | |
| 
 | |
| add_task(async function test_beforeSave_rejects() {
 | |
|   let storeForSave = new JSONFile({
 | |
|     path: getTempFile(TEST_STORE_FILE_NAME).path,
 | |
|     beforeSave() {
 | |
|       return Promise.reject(new Error("oops"));
 | |
|     },
 | |
|     saveDelayMs: 250,
 | |
|   });
 | |
| 
 | |
|   let promiseSave = new Promise((resolve, reject) => {
 | |
|     let save = storeForSave._save.bind(storeForSave);
 | |
|     storeForSave._save = () => {
 | |
|       save().then(resolve, reject);
 | |
|     };
 | |
|     storeForSave.saveSoon();
 | |
|   });
 | |
| 
 | |
|   await Assert.rejects(promiseSave, function(ex) {
 | |
|     return ex.message == "oops";
 | |
|   });
 | |
| });
 | |
| 
 | |
| add_task(async function test_finalize() {
 | |
|   let path = getTempFile(TEST_STORE_FILE_NAME).path;
 | |
| 
 | |
|   let barrier = new AsyncShutdown.Barrier("test-auto-finalize");
 | |
|   let storeForSave = new JSONFile({
 | |
|     path,
 | |
|     saveDelayMs: 2000,
 | |
|     finalizeAt: barrier.client,
 | |
|   });
 | |
|   await storeForSave.load();
 | |
|   storeForSave.data = TEST_DATA;
 | |
|   storeForSave.saveSoon();
 | |
| 
 | |
|   let promiseFinalize = storeForSave.finalize();
 | |
|   await Assert.rejects(storeForSave.finalize(), /has already been finalized$/);
 | |
|   await promiseFinalize;
 | |
|   Assert.ok(!storeForSave.dataReady);
 | |
| 
 | |
|   // Finalization removes the blocker, so waiting should not log an unhandled
 | |
|   // error even though the object has been explicitly finalized.
 | |
|   await barrier.wait();
 | |
| 
 | |
|   let storeForLoad = new JSONFile({ path });
 | |
|   await storeForLoad.load();
 | |
|   Assert.deepEqual(storeForLoad.data, TEST_DATA);
 | |
| });
 | |
| 
 | |
| add_task(async function test_finalize_on_shutdown() {
 | |
|   let path = getTempFile(TEST_STORE_FILE_NAME).path;
 | |
| 
 | |
|   let barrier = new AsyncShutdown.Barrier("test-finalize-shutdown");
 | |
|   let storeForSave = new JSONFile({
 | |
|     path,
 | |
|     saveDelayMs: 2000,
 | |
|     finalizeAt: barrier.client,
 | |
|   });
 | |
|   await storeForSave.load();
 | |
|   storeForSave.data = TEST_DATA;
 | |
|   // Arm the saver, then simulate shutdown and ensure the file is
 | |
|   // automatically finalized.
 | |
|   storeForSave.saveSoon();
 | |
| 
 | |
|   await barrier.wait();
 | |
|   // It's possible for `finalize` to reject when called concurrently with
 | |
|   // shutdown. We don't distinguish between explicit `finalize` calls and
 | |
|   // finalization on shutdown because we expect most consumers to rely on the
 | |
|   // latter. However, this behavior can be safely changed if needed.
 | |
|   await Assert.rejects(storeForSave.finalize(), /has already been finalized$/);
 | |
|   Assert.ok(!storeForSave.dataReady);
 | |
| 
 | |
|   let storeForLoad = new JSONFile({ path });
 | |
|   await storeForLoad.load();
 | |
|   Assert.deepEqual(storeForLoad.data, TEST_DATA);
 | |
| });
 |