michael@0: /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm"); michael@0: michael@0: /** michael@0: * Tests the bookmarks-restore-* nsIObserver notifications after restoring michael@0: * bookmarks from JSON and HTML. See bug 470314. michael@0: */ michael@0: michael@0: // The topics and data passed to nsIObserver.observe() on bookmarks restore michael@0: const NSIOBSERVER_TOPIC_BEGIN = "bookmarks-restore-begin"; michael@0: const NSIOBSERVER_TOPIC_SUCCESS = "bookmarks-restore-success"; michael@0: const NSIOBSERVER_TOPIC_FAILED = "bookmarks-restore-failed"; michael@0: const NSIOBSERVER_DATA_JSON = "json"; michael@0: const NSIOBSERVER_DATA_HTML = "html"; michael@0: const NSIOBSERVER_DATA_HTML_INIT = "html-initial"; michael@0: michael@0: // Bookmarks are added for these URIs michael@0: var uris = [ michael@0: "http://example.com/1", michael@0: "http://example.com/2", michael@0: "http://example.com/3", michael@0: "http://example.com/4", michael@0: "http://example.com/5", michael@0: ]; michael@0: michael@0: // Add tests here. Each is an object with these properties: michael@0: // desc: description printed before test is run michael@0: // currTopic: the next expected topic that should be observed for the test; michael@0: // set to NSIOBSERVER_TOPIC_BEGIN to begin michael@0: // finalTopic: the last expected topic that should be observed for the test, michael@0: // which then causes the next test to be run michael@0: // data: the data passed to nsIObserver.observe() corresponding to the michael@0: // test michael@0: // file: the nsILocalFile that the test creates michael@0: // folderId: for HTML restore into a folder, the folder ID to restore into; michael@0: // otherwise, set it to null michael@0: // run: a method that actually runs the test michael@0: var tests = [ michael@0: { michael@0: desc: "JSON restore: normal restore should succeed", michael@0: currTopic: NSIOBSERVER_TOPIC_BEGIN, michael@0: finalTopic: NSIOBSERVER_TOPIC_SUCCESS, michael@0: data: NSIOBSERVER_DATA_JSON, michael@0: folderId: null, michael@0: run: function () { michael@0: Task.spawn(function () { michael@0: this.file = yield promiseFile("bookmarks-test_restoreNotification.json"); michael@0: addBookmarks(); michael@0: michael@0: yield BookmarkJSONUtils.exportToFile(this.file); michael@0: remove_all_bookmarks(); michael@0: try { michael@0: yield BookmarkJSONUtils.importFromFile(this.file, true); michael@0: } michael@0: catch (e) { michael@0: do_throw(" Restore should not have failed"); michael@0: } michael@0: }.bind(this)); michael@0: } michael@0: }, michael@0: michael@0: { michael@0: desc: "JSON restore: empty file should succeed", michael@0: currTopic: NSIOBSERVER_TOPIC_BEGIN, michael@0: finalTopic: NSIOBSERVER_TOPIC_SUCCESS, michael@0: data: NSIOBSERVER_DATA_JSON, michael@0: folderId: null, michael@0: run: function () { michael@0: Task.spawn(function() { michael@0: this.file = yield promiseFile("bookmarks-test_restoreNotification.json"); michael@0: try { michael@0: yield BookmarkJSONUtils.importFromFile(this.file, true); michael@0: } michael@0: catch (e) { michael@0: do_throw(" Restore should not have failed" + e); michael@0: } michael@0: }.bind(this)); michael@0: } michael@0: }, michael@0: michael@0: { michael@0: desc: "JSON restore: nonexistent file should fail", michael@0: currTopic: NSIOBSERVER_TOPIC_BEGIN, michael@0: finalTopic: NSIOBSERVER_TOPIC_FAILED, michael@0: data: NSIOBSERVER_DATA_JSON, michael@0: folderId: null, michael@0: run: function () { michael@0: this.file = Services.dirsvc.get("ProfD", Ci.nsILocalFile); michael@0: this.file.append("this file doesn't exist because nobody created it"); michael@0: Task.spawn(function() { michael@0: try { michael@0: yield BookmarkJSONUtils.importFromFile(this.file, true); michael@0: do_throw(" Restore should have failed"); michael@0: } michael@0: catch (e) { michael@0: } michael@0: }.bind(this)); michael@0: } michael@0: }, michael@0: michael@0: { michael@0: desc: "HTML restore: normal restore should succeed", michael@0: currTopic: NSIOBSERVER_TOPIC_BEGIN, michael@0: finalTopic: NSIOBSERVER_TOPIC_SUCCESS, michael@0: data: NSIOBSERVER_DATA_HTML, michael@0: folderId: null, michael@0: run: function () { michael@0: Task.spawn(function() { michael@0: this.file = yield promiseFile("bookmarks-test_restoreNotification.html"); michael@0: addBookmarks(); michael@0: yield BookmarkHTMLUtils.exportToFile(this.file); michael@0: remove_all_bookmarks(); michael@0: try { michael@0: BookmarkHTMLUtils.importFromFile(this.file, false) michael@0: .then(null, do_report_unexpected_exception); michael@0: } michael@0: catch (e) { michael@0: do_throw(" Restore should not have failed"); michael@0: } michael@0: }.bind(this)); michael@0: } michael@0: }, michael@0: michael@0: { michael@0: desc: "HTML restore: empty file should succeed", michael@0: currTopic: NSIOBSERVER_TOPIC_BEGIN, michael@0: finalTopic: NSIOBSERVER_TOPIC_SUCCESS, michael@0: data: NSIOBSERVER_DATA_HTML, michael@0: folderId: null, michael@0: run: function () { michael@0: Task.spawn(function (){ michael@0: this.file = yield promiseFile("bookmarks-test_restoreNotification.init.html"); michael@0: try { michael@0: BookmarkHTMLUtils.importFromFile(this.file, false) michael@0: .then(null, do_report_unexpected_exception); michael@0: } michael@0: catch (e) { michael@0: do_throw(" Restore should not have failed"); michael@0: } michael@0: }.bind(this)); michael@0: } michael@0: }, michael@0: michael@0: { michael@0: desc: "HTML restore: nonexistent file should fail", michael@0: currTopic: NSIOBSERVER_TOPIC_BEGIN, michael@0: finalTopic: NSIOBSERVER_TOPIC_FAILED, michael@0: data: NSIOBSERVER_DATA_HTML, michael@0: folderId: null, michael@0: run: function () { michael@0: this.file = Services.dirsvc.get("ProfD", Ci.nsILocalFile); michael@0: this.file.append("this file doesn't exist because nobody created it"); michael@0: try { michael@0: BookmarkHTMLUtils.importFromFile(this.file, false) michael@0: .then(function onSuccess() do_throw("Should fail!"), michael@0: null); michael@0: } michael@0: catch (e) {} michael@0: } michael@0: }, michael@0: michael@0: { michael@0: desc: "HTML initial restore: normal restore should succeed", michael@0: currTopic: NSIOBSERVER_TOPIC_BEGIN, michael@0: finalTopic: NSIOBSERVER_TOPIC_SUCCESS, michael@0: data: NSIOBSERVER_DATA_HTML_INIT, michael@0: folderId: null, michael@0: run: function () { michael@0: Task.spawn(function () { michael@0: this.file = yield promiseFile("bookmarks-test_restoreNotification.init.html"); michael@0: addBookmarks(); michael@0: yield BookmarkHTMLUtils.exportToFile(this.file); michael@0: remove_all_bookmarks(); michael@0: try { michael@0: BookmarkHTMLUtils.importFromFile(this.file, true) michael@0: .then(null, do_report_unexpected_exception); michael@0: } michael@0: catch (e) { michael@0: do_throw(" Restore should not have failed"); michael@0: } michael@0: }.bind(this)); michael@0: } michael@0: }, michael@0: michael@0: { michael@0: desc: "HTML initial restore: empty file should succeed", michael@0: currTopic: NSIOBSERVER_TOPIC_BEGIN, michael@0: finalTopic: NSIOBSERVER_TOPIC_SUCCESS, michael@0: data: NSIOBSERVER_DATA_HTML_INIT, michael@0: folderId: null, michael@0: run: function () { michael@0: Task.spawn(function () { michael@0: this.file = yield promiseFile("bookmarks-test_restoreNotification.init.html"); michael@0: try { michael@0: BookmarkHTMLUtils.importFromFile(this.file, true) michael@0: .then(null, do_report_unexpected_exception); michael@0: } michael@0: catch (e) { michael@0: do_throw(" Restore should not have failed"); michael@0: } michael@0: }.bind(this)); michael@0: } michael@0: }, michael@0: michael@0: { michael@0: desc: "HTML initial restore: nonexistent file should fail", michael@0: currTopic: NSIOBSERVER_TOPIC_BEGIN, michael@0: finalTopic: NSIOBSERVER_TOPIC_FAILED, michael@0: data: NSIOBSERVER_DATA_HTML_INIT, michael@0: folderId: null, michael@0: run: function () { michael@0: this.file = Services.dirsvc.get("ProfD", Ci.nsILocalFile); michael@0: this.file.append("this file doesn't exist because nobody created it"); michael@0: try { michael@0: BookmarkHTMLUtils.importFromFile(this.file, true) michael@0: .then(function onSuccess() do_throw("Should fail!"), michael@0: null); michael@0: } michael@0: catch (e) {} michael@0: } michael@0: } michael@0: ]; michael@0: michael@0: // nsIObserver that observes bookmarks-restore-begin. michael@0: var beginObserver = { michael@0: observe: function _beginObserver(aSubject, aTopic, aData) { michael@0: var test = tests[currTestIndex]; michael@0: michael@0: print(" Observed " + aTopic); michael@0: print(" Topic for current test should be what is expected"); michael@0: do_check_eq(aTopic, test.currTopic); michael@0: michael@0: print(" Data for current test should be what is expected"); michael@0: do_check_eq(aData, test.data); michael@0: michael@0: // Update current expected topic to the next expected one. michael@0: test.currTopic = test.finalTopic; michael@0: } michael@0: }; michael@0: michael@0: // nsIObserver that observes bookmarks-restore-success/failed. This starts michael@0: // the next test. michael@0: var successAndFailedObserver = { michael@0: observe: function _successAndFailedObserver(aSubject, aTopic, aData) { michael@0: var test = tests[currTestIndex]; michael@0: michael@0: print(" Observed " + aTopic); michael@0: print(" Topic for current test should be what is expected"); michael@0: do_check_eq(aTopic, test.currTopic); michael@0: michael@0: print(" Data for current test should be what is expected"); michael@0: do_check_eq(aData, test.data); michael@0: michael@0: // On restore failed, file may not exist, so wrap in try-catch. michael@0: try { michael@0: test.file.remove(false); michael@0: } michael@0: catch (exc) {} michael@0: michael@0: // Make sure folder ID is what is expected. For importing HTML into a michael@0: // folder, this will be an integer, otherwise null. michael@0: if (aSubject) { michael@0: do_check_eq(aSubject.QueryInterface(Ci.nsISupportsPRInt64).data, michael@0: test.folderId); michael@0: } michael@0: else michael@0: do_check_eq(test.folderId, null); michael@0: michael@0: remove_all_bookmarks(); michael@0: do_execute_soon(doNextTest); michael@0: } michael@0: }; michael@0: michael@0: // Index of the currently running test. See doNextTest(). michael@0: var currTestIndex = -1; michael@0: michael@0: var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. michael@0: getService(Ci.nsINavBookmarksService); michael@0: michael@0: var obssvc = Cc["@mozilla.org/observer-service;1"]. michael@0: getService(Ci.nsIObserverService); michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: /** michael@0: * Adds some bookmarks for the URIs in |uris|. michael@0: */ michael@0: function addBookmarks() { michael@0: uris.forEach(function (u) bmsvc.insertBookmark(bmsvc.bookmarksMenuFolder, michael@0: uri(u), michael@0: bmsvc.DEFAULT_INDEX, michael@0: u)); michael@0: checkBookmarksExist(); michael@0: } michael@0: michael@0: /** michael@0: * Checks that all of the bookmarks created for |uris| exist. It works by michael@0: * creating one query per URI and then ORing all the queries. The number of michael@0: * results returned should be uris.length. michael@0: */ michael@0: function checkBookmarksExist() { michael@0: var hs = Cc["@mozilla.org/browser/nav-history-service;1"]. michael@0: getService(Ci.nsINavHistoryService); michael@0: var queries = uris.map(function (u) { michael@0: var q = hs.getNewQuery(); michael@0: q.uri = uri(u); michael@0: return q; michael@0: }); michael@0: var options = hs.getNewQueryOptions(); michael@0: options.queryType = options.QUERY_TYPE_BOOKMARKS; michael@0: var root = hs.executeQueries(queries, uris.length, options).root; michael@0: root.containerOpen = true; michael@0: do_check_eq(root.childCount, uris.length); michael@0: root.containerOpen = false; michael@0: } michael@0: michael@0: /** michael@0: * Creates an file in the profile directory. michael@0: * michael@0: * @param aBasename michael@0: * e.g., "foo.txt" in the path /some/long/path/foo.txt michael@0: * @return {Promise} michael@0: * @resolves to an OS.File path michael@0: */ michael@0: function promiseFile(aBasename) { michael@0: let path = OS.Path.join(OS.Constants.Path.profileDir, aBasename); michael@0: dump("\n\nopening " + path + "\n\n"); michael@0: return OS.File.open(path, { truncate: true }).then(aFile => { aFile.close(); return path; }); michael@0: } michael@0: michael@0: /** michael@0: * Runs the next test or if all tests have been run, finishes. michael@0: */ michael@0: function doNextTest() { michael@0: currTestIndex++; michael@0: if (currTestIndex >= tests.length) { michael@0: obssvc.removeObserver(beginObserver, NSIOBSERVER_TOPIC_BEGIN); michael@0: obssvc.removeObserver(successAndFailedObserver, NSIOBSERVER_TOPIC_SUCCESS); michael@0: obssvc.removeObserver(successAndFailedObserver, NSIOBSERVER_TOPIC_FAILED); michael@0: do_test_finished(); michael@0: } michael@0: else { michael@0: var test = tests[currTestIndex]; michael@0: print("Running test: " + test.desc); michael@0: test.run(); michael@0: } michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: function run_test() { michael@0: do_test_pending(); michael@0: obssvc.addObserver(beginObserver, NSIOBSERVER_TOPIC_BEGIN, false); michael@0: obssvc.addObserver(successAndFailedObserver, NSIOBSERVER_TOPIC_SUCCESS, false); michael@0: obssvc.addObserver(successAndFailedObserver, NSIOBSERVER_TOPIC_FAILED, false); michael@0: doNextTest(); michael@0: }