From 16fef5594739baa6795e44ef746661e953da7b13 Mon Sep 17 00:00:00 2001 From: Shawn Wilsher Date: Thu, 18 Sep 2008 11:10:47 -0400 Subject: [PATCH] Bug 450290 - Sync the temp tables to the permanent tables. This changeset adds code that is run at app-startup that will flush out changes to the temporary tables into the permanent ones. For moz_places, this is done whenever we sync moz_historyvisits and when we add or modify a bookmark. For moz_historyvisits, this is done on a timer controlled by the preference places.syncDBTableIntervalInSecs. Changeset includes full test coverage for the above behaviors. r=dietrich r=Mak77 --HG-- rename : toolkit/components/places/tests/unit/head_bookmarks.js => toolkit/components/places/tests/background/head_background.js --- browser/installer/unix/packages-static | 1 + browser/installer/windows/packages-static | 1 + toolkit/components/places/src/Makefile.in | 1 + .../places/src/PlacesBackground.jsm | 7 + .../components/places/src/nsNavHistory.cpp | 6 + .../components/places/src/nsPlacesDBFlush.js | 277 ++++++++++++++++++ .../components/places/src/nsPlacesTriggers.h | 22 ++ .../tests/background/head_background.js | 275 +++++++++++++++++ .../tests/background/test_background.js | 26 +- .../test_database_sync_after_addBookmark.js | 55 ++++ ...database_sync_after_addBookmark_batched.js | 64 ++++ .../test_database_sync_after_addVisit.js | 71 +++++ ...st_database_sync_after_addVisit_batched.js | 66 +++++ ...test_database_sync_after_modifyBookmark.js | 64 ++++ ...st_database_sync_after_quit_application.js | 69 +++++ .../test_multiple_bookmarks_around_sync.js | 103 +++++++ .../test_multiple_visits_around_sync.js | 127 ++++++++ 17 files changed, 1231 insertions(+), 4 deletions(-) create mode 100644 toolkit/components/places/src/nsPlacesDBFlush.js create mode 100644 toolkit/components/places/tests/background/head_background.js create mode 100644 toolkit/components/places/tests/background/test_database_sync_after_addBookmark.js create mode 100644 toolkit/components/places/tests/background/test_database_sync_after_addBookmark_batched.js create mode 100644 toolkit/components/places/tests/background/test_database_sync_after_addVisit.js create mode 100644 toolkit/components/places/tests/background/test_database_sync_after_addVisit_batched.js create mode 100644 toolkit/components/places/tests/background/test_database_sync_after_modifyBookmark.js create mode 100644 toolkit/components/places/tests/background/test_database_sync_after_quit_application.js create mode 100644 toolkit/components/places/tests/background/test_multiple_bookmarks_around_sync.js create mode 100644 toolkit/components/places/tests/background/test_multiple_visits_around_sync.js diff --git a/browser/installer/unix/packages-static b/browser/installer/unix/packages-static index a0a2ffdc84a..a07464ad74d 100644 --- a/browser/installer/unix/packages-static +++ b/browser/installer/unix/packages-static @@ -234,6 +234,7 @@ bin/components/libbrowsercomps.so bin/components/txEXSLTRegExFunctions.js bin/components/nsLivemarkService.js bin/components/nsTaggingService.js +bin/components/nsPlacesDBFlush.js bin/components/nsDefaultCLH.js bin/components/nsContentPrefService.js bin/components/nsContentDispatchChooser.js diff --git a/browser/installer/windows/packages-static b/browser/installer/windows/packages-static index 4e421d46a34..29b54ec66da 100644 --- a/browser/installer/windows/packages-static +++ b/browser/installer/windows/packages-static @@ -241,6 +241,7 @@ bin\components\brwsrcmp.dll bin\components\txEXSLTRegExFunctions.js bin\components\nsLivemarkService.js bin\components\nsTaggingService.js +bin\components\nsPlacesDBFlush.js bin\components\nsDefaultCLH.js bin\components\nsContentPrefService.js bin\components\nsContentDispatchChooser.js diff --git a/toolkit/components/places/src/Makefile.in b/toolkit/components/places/src/Makefile.in index 89d3805d8ef..6e6b16b3e07 100644 --- a/toolkit/components/places/src/Makefile.in +++ b/toolkit/components/places/src/Makefile.in @@ -105,6 +105,7 @@ LOCAL_INCLUDES += -I$(srcdir)/../../build EXTRA_PP_COMPONENTS = nsLivemarkService.js \ nsTaggingService.js \ + nsPlacesDBFlush.js \ $(NULL) EXTRA_JS_MODULES = \ diff --git a/toolkit/components/places/src/PlacesBackground.jsm b/toolkit/components/places/src/PlacesBackground.jsm index 815f356b879..3e402727c90 100644 --- a/toolkit/components/places/src/PlacesBackground.jsm +++ b/toolkit/components/places/src/PlacesBackground.jsm @@ -49,6 +49,7 @@ const Ci = Components.interfaces; const Cr = Components.results; const kQuitApplication = "quit-application"; +const kPlacesBackgroundShutdown = "places-background-shutdown"; //////////////////////////////////////////////////////////////////////////////// //// nsPlacesBackgound class @@ -84,6 +85,12 @@ nsPlacesBackground.prototype = { observe: function PlacesBackground_observe(aSubject, aTopic, aData) { if (aTopic == kQuitApplication) { + // Notify consumers that we are shutting down. + let os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + os.notifyObservers(null, kPlacesBackgroundShutdown, null); + + // Now shut the thread down. this._thread.shutdown(); this._thread = null; } diff --git a/toolkit/components/places/src/nsNavHistory.cpp b/toolkit/components/places/src/nsNavHistory.cpp index 1413297525f..06e74982dfb 100644 --- a/toolkit/components/places/src/nsNavHistory.cpp +++ b/toolkit/components/places/src/nsNavHistory.cpp @@ -991,6 +991,9 @@ nsNavHistory::InitTempTables() "CREATE INDEX moz_places_temp_frecencyindex ON moz_places_temp (frecency)")); NS_ENSURE_SUCCESS(rv, rv); + rv = mDBConn->ExecuteSimpleSQL(CREATE_MOZ_PLACES_SYNC_TRIGGER); + NS_ENSURE_SUCCESS(rv, rv); + // moz_historyvisits_temp rv = mDBConn->ExecuteSimpleSQL(CREATE_MOZ_HISTORYVISITS_TEMP); @@ -1011,6 +1014,9 @@ nsNavHistory::InitTempTables() "ON moz_historyvisits_temp (visit_date)")); NS_ENSURE_SUCCESS(rv, rv); + rv = mDBConn->ExecuteSimpleSQL(CREATE_MOZ_HISTORYVISITS_SYNC_TRIGGER); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; } diff --git a/toolkit/components/places/src/nsPlacesDBFlush.js b/toolkit/components/places/src/nsPlacesDBFlush.js new file mode 100644 index 00000000000..eaeeb2134b2 --- /dev/null +++ b/toolkit/components/places/src/nsPlacesDBFlush.js @@ -0,0 +1,277 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 sts=2 expandtab + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shawn Wilsher (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); +Components.utils.import("resource://gre/modules/PlacesBackground.jsm"); + +//////////////////////////////////////////////////////////////////////////////// +//// Constants + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cr = Components.results; + +const kPlacesBackgroundShutdown = "places-background-shutdown"; + +const kSyncPrefName = "syncDBTableIntervalInSecs"; +const kDefaultSyncInterval = 120; + +//////////////////////////////////////////////////////////////////////////////// +//// nsPlacesDBFlush class + +function nsPlacesDBFlush() +{ + ////////////////////////////////////////////////////////////////////////////// + //// Smart Getters + + this.__defineGetter__("_db", function() { + delete this._db; + return this._db = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsPIPlacesDatabase). + DBConnection; + }); + + this.__defineGetter__("_bh", function() { + delete this._bh; + return this._bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. + getService(Ci.nsINavBookmarksService); + }); + + + // Get our sync interval + this._prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefService). + getBranch("places."); + try { + // We want to silently fail if the preference does not exist, and use a + // default to fallback to. + this._syncInterval = this._prefs.getIntPref(kSyncPrefName); + if (this._syncInterval <= 0) + this._syncInterval = kDefaultSyncInterval; + } + catch (e) { + // The preference did not exist, so default to two minutes. + this._syncInterval = kDefaultSyncInterval; + } + + // Register observers + this._bh.addObserver(this, false); + + this._prefs.QueryInterface(Ci.nsIPrefBranch2).addObserver("", this, false); + + let os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + os.addObserver(this, kPlacesBackgroundShutdown, false); + + // Create our timer to update everything + this._timer = this._newTimer(); +} + +nsPlacesDBFlush.prototype = { + ////////////////////////////////////////////////////////////////////////////// + //// nsIObserver + + observe: function DBFlush_observe(aSubject, aTopic, aData) + { + if (aTopic == kPlacesBackgroundShutdown) { + this._bh.removeObserver(this); + this._timer.cancel(); + this._timer = null; + this._syncAll(); + } + else if (aTopic == "nsPref:changed" && aData == kSyncPrefName) { + // Get the new pref value, and then update our timer + this._syncInterval = aSubject.getIntPref(kSyncPrefName); + if (this._syncInterval <= 0) + this._syncInterval = kDefaultSyncInterval; + + // We may have canceled the timer already for batch updates, so we want to + // exit early. + if (!this._timer) + return; + + this._timer.cancel(); + this._timer = this._newTimer(); + } + }, + + ////////////////////////////////////////////////////////////////////////////// + //// nsINavBookmarkObserver + + onBeginUpdateBatch: function DBFlush_onBeginUpdateBatch() + { + this._inBatchMode = true; + + // We do not want to sync while we are doing batch work. + this._timer.cancel(); + this._timer = null; + }, + + onEndUpdateBatch: function DBFlush_onEndUpdateBatch() + { + this._inBatchMode = false; + + // We need to sync and restore our timer now. + this._syncAll(); + this._timer = this._newTimer(); + }, + + onItemAdded: function() this._syncMozPlaces(), + + onItemChanged: function DBFlush_onItemChanged(aItemId, aProperty, + aIsAnnotationProperty, + aValue) + { + if (aProperty == "uri") + this._syncMozPlaces(); + }, + + onItemRemoved: function() { }, + onItemVisited: function() { }, + onItemMoved: function() { }, + + ////////////////////////////////////////////////////////////////////////////// + //// nsITimerCallback + + notify: function() this._syncAll(), + + ////////////////////////////////////////////////////////////////////////////// + //// nsPlacesDBFlush + + /** + * Dispatches an event to the background thread to run _doSyncMozX("places"). + */ + _syncMozPlaces: function DBFlush_syncMozPlaces() + { + // No need to do extra work if we are in batch mode + if (this._inBatchMode) + return; + + let self = this; + PlacesBackground.dispatch({ + run: function() self._doSyncMozX("places") + }, Ci.nsIEventTarget.DISPATCH_NORMAL); + }, + + /** + * Dispatches an event to the background thread to sync all temporary tables. + */ + _syncAll: function DBFlush_syncAll() + { + let self = this; + PlacesBackground.dispatch({ + run: function() { + // We try to get a transaction, but if we can't don't worry + let ourTransaction = false; + try { + this._db.beginTransaction(); + ourTransaction = true; + } + catch (e) { } + + try { + // This needs to also sync moz_places in order to maintain data + // integrity + self._doSyncMozX("places"); + self._doSyncMozX("historyvisits"); + } + catch (e) { + if (ourTransaction) + this._db.rollbackTransaction(); + throw e; + } + + if (ourTransaction) + this._db.commitTransaction(); + } + }, Ci.nsIEventTarget.DISPATCH_NORMAL); + }, + + /** + * Synchronizes the moz_{aName} and moz_{aName}_temp by copying all the data + * from the temporary table into the permanent one. It then deletes the data + * in the temporary table. All of this is done in a transaction that is + * rolled back upon failure at any point. + */ + _doSyncMozX: function DBFlush_doSyncMozX(aName) + { + // Delete all the data in the temp table. + // We have triggers setup that ensure that the data is transfered over + // upon deletion. + this._db.executeSimpleSQL("DELETE FROM moz_" + aName + "_temp"); + }, + + /** + * Creates a new timer bases on this._timerInterval. + * + * @returns a REPEATING_SLACK nsITimer that runs every this._timerInterval. + */ + _newTimer: function DBFlush_newTimer() + { + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback(this, this._syncInterval * 1000, + Ci.nsITimer.TYPE_REPEATING_SLACK); + return timer; + }, + + ////////////////////////////////////////////////////////////////////////////// + //// nsISupports + + classDescription: "Used to synchronize the temporary and permanent tables of Places", + classID: Components.ID("c1751cfc-e8f1-4ade-b0bb-f74edfb8ef6a"), + contractID: "@mozilla.org/places/sync;1", + _xpcom_categories: [{ + category: "profile-after-change", + }], + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIObserver, + Ci.nsINavBookmarkObserver, + Ci.nsITimerCallback, + ]) +}; + +//////////////////////////////////////////////////////////////////////////////// +//// Module Registration + +let components = [nsPlacesDBFlush]; +function NSGetModule(compMgr, fileSpec) +{ + return XPCOMUtils.generateModule(components); +} diff --git a/toolkit/components/places/src/nsPlacesTriggers.h b/toolkit/components/places/src/nsPlacesTriggers.h index 58f75a6eab5..fbca63876f7 100644 --- a/toolkit/components/places/src/nsPlacesTriggers.h +++ b/toolkit/components/places/src/nsPlacesTriggers.h @@ -202,4 +202,26 @@ "END" \ ) +/** + * This trigger moves the data out of a temporary table into the permanent one + * before deleting from the temporary table. + * + * Note - it's OK to use an INSERT OR REPLACE here because the only conflict + * that will happen is the primary key. As a result, the row will be deleted, + * and the replacement will be inserted with the same id. + */ +#define CREATE_TEMP_SYNC_TRIGGER_BASE(__table) NS_LITERAL_CSTRING( \ + "CREATE TEMPORARY TRIGGER " __table "_beforedelete_trigger " \ + "BEFORE DELETE ON " __table "_temp FOR EACH ROW " \ + "BEGIN " \ + "INSERT OR REPLACE INTO " __table " " \ + "SELECT * FROM " __table "_temp " \ + "WHERE id = OLD.id;" \ + "END" \ +) +#define CREATE_MOZ_PLACES_SYNC_TRIGGER \ + CREATE_TEMP_SYNC_TRIGGER_BASE("moz_places") +#define CREATE_MOZ_HISTORYVISITS_SYNC_TRIGGER \ + CREATE_TEMP_SYNC_TRIGGER_BASE("moz_historyvisits") + #endif // __nsPlacesTriggers_h__ diff --git a/toolkit/components/places/tests/background/head_background.js b/toolkit/components/places/tests/background/head_background.js new file mode 100644 index 00000000000..91a9e3f0369 --- /dev/null +++ b/toolkit/components/places/tests/background/head_background.js @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Places. + * + * The Initial Developer of the Original Code is + * Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner + * Dietrich Ayala + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const NS_APP_USER_PROFILE_50_DIR = "ProfD"; +const NS_APP_HISTORY_50_FILE = "UHist"; + +const Ci = Components.interfaces; +const Cc = Components.classes; +const Cr = Components.results; + +function LOG(aMsg) { + aMsg = ("*** PLACES TESTS: " + aMsg); + Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService). + logStringMessage(aMsg); + print(aMsg); +} + +// If there's no location registered for the profile direcotry, register one now. +var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); +var profileDir = null; +try { + profileDir = dirSvc.get(NS_APP_USER_PROFILE_50_DIR, Ci.nsIFile); +} catch (e) {} +if (!profileDir) { + // Register our own provider for the profile directory. + // It will simply return the current directory. + var provider = { + getFile: function(prop, persistent) { + persistent.value = true; + if (prop == NS_APP_USER_PROFILE_50_DIR) { + return dirSvc.get("CurProcD", Ci.nsIFile); + } + if (prop == NS_APP_HISTORY_50_FILE) { + var histFile = dirSvc.get("CurProcD", Ci.nsIFile); + histFile.append("history.dat"); + return histFile; + } + throw Cr.NS_ERROR_FAILURE; + }, + QueryInterface: function(iid) { + if (iid.equals(Ci.nsIDirectoryServiceProvider) || + iid.equals(Ci.nsISupports)) { + return this; + } + throw Cr.NS_ERROR_NO_INTERFACE; + } + }; + dirSvc.QueryInterface(Ci.nsIDirectoryService).registerProvider(provider); +} + +var iosvc = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); + +function uri(spec) { + return iosvc.newURI(spec, null, null); +} + +// Delete a previously created sqlite file +function clearDB() { + try { + var file = dirSvc.get('ProfD', Ci.nsIFile); + file.append("places.sqlite"); + if (file.exists()) + file.remove(false); + } catch(ex) { dump("Exception: " + ex); } +} +clearDB(); + +/** + * Dumps the rows of a table out to the console. + * + * @param aName + * The name of the table or view to output. + */ +function dump_table(aName) +{ + let db = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsPIPlacesDatabase). + DBConnection; + let stmt = db.createStatement("SELECT * FROM " + aName); + + dump("\n*** Printing data from " + aName + ":\n"); + let count = 0; + while (stmt.executeStep()) { + let columns = stmt.numEntries; + + if (count == 0) { + // print the column names + for (let i = 0; i < columns; i++) + dump(stmt.getColumnName(i) + "\t"); + dump("\n"); + } + + // print the row + for (let i = 0; i < columns; i++) { + switch (stmt.getTypeOfIndex(i)) { + case Ci.mozIStorageValueArray.VALUE_TYPE_NULL: + dump("NULL\t"); + break; + case Ci.mozIStorageValueArray.VALUE_TYPE_INTEGER: + dump(stmt.getInt64(i) + "\t"); + break; + case Ci.mozIStorageValueArray.VALUE_TYPE_FLOAT: + dump(stmt.getDouble(i) + "\t"); + break; + case Ci.mozIStorageValueArray.VALUE_TYPE_TEXT: + dump(stmt.getString(i) + "\t"); + break; + } + } + dump("\n"); + + count++; + } + dump("*** There were a total of " + count + " rows of data.\n\n"); + + stmt.reset(); + stmt.finalize(); + stmt = null; +} + +/** + * This dispatches the observer topic "quit-application" to clean up the + * background thread. + */ +function finish_test() +{ + // This next bit needs to run on the main thread + let tm = Cc["@mozilla.org/thread-manager;1"]. + getService(Ci.nsIThreadManager); + tm.mainThread.dispatch({ + run: function() + { + // xpcshell doesn't dispatch shutdown-application + let os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + os.notifyObservers(null, "quit-application", null); + do_test_finished(); + } + }, Ci.nsIEventTarget.DISPATCH_NORMAL); +} + +/** + * Function tests to see if the place associated with the bookmark with id + * aBookmarkId has the uri aExpectedURI. The event will call finish_test() if + * aFinish is true. + * + * @param aBookmarkId + * The bookmark to check against. + * @param aExpectedURI + * The URI we expect to be in moz_places. + * @param aExpected + * Indicates if we expect to get a result or not. + * @param [optional] aFinish + * Indicates if the test should be completed or not. + */ +function new_test_bookmark_uri_event(aBookmarkId, aExpectedURI, aExpected, aFinish) +{ + return { + run: function() + { + let db = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsPIPlacesDatabase). + DBConnection; + let stmt = db.createStatement( + "SELECT moz_places.url " + + "FROM moz_bookmarks INNER JOIN moz_places " + + "ON moz_bookmarks.fk = moz_places.id " + + "WHERE moz_bookmarks.id = ?1" + ); + stmt.bindInt64Parameter(0, aBookmarkId); + + if (aExpected) { + do_check_true(stmt.executeStep()); + do_check_eq(stmt.getUTF8String(0), aExpectedURI); + } + else { + do_check_false(stmt.executeStep()); + } + stmt.reset(); + stmt.finalize(); + stmt = null; + + if (aFinish) + finish_test(); + } + }; +} + +/** + * Function tests to see if the place associated with the visit with id aVisitId + * has the uri aExpectedURI. The event will call finish_test() if aFinish is + * true. + * + * @param aVisitId + * The visit to check against. + * @param aExpectedURI + * The URI we expect to be in moz_places. + * @param aExpected + * Indicates if we expect to get a result or not. + * @param [optional] aFinish + * Indicates if the test should be completed or not. + */ +function new_test_visit_uri_event(aVisitId, aExpectedURI, aExpected, aFinish) +{ + return { + run: function() + { + let db = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsPIPlacesDatabase). + DBConnection; + let stmt = db.createStatement( + "SELECT moz_places.url " + + "FROM moz_historyvisits INNER JOIN moz_places " + + "ON moz_historyvisits.place_id = moz_places.id " + + "WHERE moz_historyvisits.id = ?1" + ); + stmt.bindInt64Parameter(0, aVisitId); + + if (aExpected) { + do_check_true(stmt.executeStep()); + do_check_eq(stmt.getUTF8String(0), aExpectedURI); + } + else { + do_check_false(stmt.executeStep()); + } + stmt.reset(); + stmt.finalize(); + stmt = null; + + if (aFinish) + finish_test(); + } + }; +} + +// profile-after-change doesn't create components in xpcshell, so we have to do +// it ourselves +Cc["@mozilla.org/places/sync;1"].getService(Ci.nsISupports); + diff --git a/toolkit/components/places/tests/background/test_background.js b/toolkit/components/places/tests/background/test_background.js index 35cbf1b323d..93790d32b0b 100644 --- a/toolkit/components/places/tests/background/test_background.js +++ b/toolkit/components/places/tests/background/test_background.js @@ -39,10 +39,6 @@ Components.utils.import("resource://gre/modules/PlacesBackground.jsm"); -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; - function test_service_exists() { do_check_neq(PlacesBackground, null); @@ -87,10 +83,32 @@ function test_two_events_same_thread() do_check_eq(event.thread1, event.thread2); } +function test_places_background_shutdown_topic() +{ + // Ensures that the places shutdown topic is dispatched before the thread is + // shutdown. + let os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + os.addObserver({ + observe: function(aSubject, aTopic, aData) + { + // We should still be able to dispatch an event without throwing now! + PlacesBackground.dispatch({ + run: function() + { + do_test_finished(); + } + }, Ci.nsIEventTarget.DISPATCH_NORMAL); + } + }, "places-background-shutdown", false); + do_test_pending(); +} + let tests = [ test_service_exists, test_isOnCurrentThread, test_two_events_same_thread, + test_places_background_shutdown_topic, ]; function run_test() diff --git a/toolkit/components/places/tests/background/test_database_sync_after_addBookmark.js b/toolkit/components/places/tests/background/test_database_sync_after_addBookmark.js new file mode 100644 index 00000000000..1bc18ebec77 --- /dev/null +++ b/toolkit/components/places/tests/background/test_database_sync_after_addBookmark.js @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 sts=2 expandtab + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shawn Wilsher (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +Components.utils.import("resource://gre/modules/PlacesBackground.jsm"); + +const TEST_URI = "http://test.com/"; + +function run_test() +{ + // First insert it + let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. + getService(Ci.nsINavBookmarksService); + let id = bh.insertBookmark(bh.unfiledBookmarksFolder, uri(TEST_URI), + bh.DEFAULT_INDEX, "test"); + + PlacesBackground.dispatch(new_test_bookmark_uri_event(id, TEST_URI, true, true), + Ci.nsIEventTarget.DISPATCH_NORMAL); + do_test_pending(); +} diff --git a/toolkit/components/places/tests/background/test_database_sync_after_addBookmark_batched.js b/toolkit/components/places/tests/background/test_database_sync_after_addBookmark_batched.js new file mode 100644 index 00000000000..3efb861f163 --- /dev/null +++ b/toolkit/components/places/tests/background/test_database_sync_after_addBookmark_batched.js @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 sts=2 expandtab + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shawn Wilsher (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +Components.utils.import("resource://gre/modules/PlacesBackground.jsm"); + +const TEST_URI = "http://test.com/"; + +function run_test() +{ + // First insert it + let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. + getService(Ci.nsINavBookmarksService); + let id = null; + bh.runInBatchMode({ + runBatched: function(aUserData) + { + id = bh.insertBookmark(bh.unfiledBookmarksFolder, uri(TEST_URI), + bh.DEFAULT_INDEX, "test"); + + PlacesBackground.dispatch(new_test_bookmark_uri_event(id, TEST_URI, false), + Ci.nsIEventTarget.DISPATCH_SYNC); + } + }, null); + do_check_neq(id, null); + PlacesBackground.dispatch(new_test_bookmark_uri_event(id, TEST_URI, true, true), + Ci.nsIEventTarget.DISPATCH_NORMAL); + do_test_pending(); +} diff --git a/toolkit/components/places/tests/background/test_database_sync_after_addVisit.js b/toolkit/components/places/tests/background/test_database_sync_after_addVisit.js new file mode 100644 index 00000000000..60add32c9f4 --- /dev/null +++ b/toolkit/components/places/tests/background/test_database_sync_after_addVisit.js @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 sts=2 expandtab + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shawn Wilsher (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +Components.utils.import("resource://gre/modules/PlacesBackground.jsm"); + +const TEST_URI = "http://test.com/"; +const kSyncPrefName = "syncDBTableIntervalInSecs"; +const SYNC_INTERVAL = 1; + +function run_test() +{ + // First set the preference for the timer to a small value + let prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefService). + getBranch("places."); + prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL); + + // Now add the visit + let hs = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService); + let id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null, + hs.TRANSITION_TYPED, false, 0); + + // Check the visit, but after enough time has passed for the DB flush service + // to have fired it's timer. + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback({ + notify: function(aTimer) + { + PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, true, true), + Ci.nsIEventTarget.DISPATCH_NORMAL); + } + }, (SYNC_INTERVAL * 1000) * 2, Ci.nsITimer.TYPE_ONE_SHOT); + do_test_pending(); +} diff --git a/toolkit/components/places/tests/background/test_database_sync_after_addVisit_batched.js b/toolkit/components/places/tests/background/test_database_sync_after_addVisit_batched.js new file mode 100644 index 00000000000..920f93734f4 --- /dev/null +++ b/toolkit/components/places/tests/background/test_database_sync_after_addVisit_batched.js @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 sts=2 expandtab + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shawn Wilsher (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +Components.utils.import("resource://gre/modules/PlacesBackground.jsm"); + +const TEST_URI = "http://test.com/"; + +function run_test() +{ + // Now add the visit in batch mode + let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. + getService(Ci.nsINavBookmarksService); + let id = null; + bh.runInBatchMode({ + runBatched: function(aUserData) + { + let hs = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService); + + id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null, + hs.TRANSITION_TYPED, false, 0); + PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, false), + Ci.nsIEventTarget.DISPATCH_SYNC); + } + }, null); + do_check_neq(id, null); + PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, true, true), + Ci.nsIEventTarget.DISPATCH_NORMAL); + do_test_pending(); +} diff --git a/toolkit/components/places/tests/background/test_database_sync_after_modifyBookmark.js b/toolkit/components/places/tests/background/test_database_sync_after_modifyBookmark.js new file mode 100644 index 00000000000..70034ea0983 --- /dev/null +++ b/toolkit/components/places/tests/background/test_database_sync_after_modifyBookmark.js @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 sts=2 expandtab + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shawn Wilsher (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +Components.utils.import("resource://gre/modules/PlacesBackground.jsm"); + +const TEST_URI = "http://test.com/"; +const MODIFIED_URI = "http://test.com/index.html"; + +function run_test() +{ + // First insert it + let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. + getService(Ci.nsINavBookmarksService); + let id = bh.insertBookmark(bh.unfiledBookmarksFolder, uri(TEST_URI), + bh.DEFAULT_INDEX, "test"); + + // Dispatch the check synchronously so we don't modify the data before this + // test runs + PlacesBackground.dispatch(new_test_bookmark_uri_event(id, TEST_URI, true), + Ci.nsIEventTarget.DISPATCH_SYNC); + + // Now modify the bookmark + bh.changeBookmarkURI(id, uri(MODIFIED_URI)); + + PlacesBackground.dispatch(new_test_bookmark_uri_event(id, MODIFIED_URI, true, true), + Ci.nsIEventTarget.DISPATCH_NORMAL); + do_test_pending(); +} diff --git a/toolkit/components/places/tests/background/test_database_sync_after_quit_application.js b/toolkit/components/places/tests/background/test_database_sync_after_quit_application.js new file mode 100644 index 00000000000..cdf39d157ce --- /dev/null +++ b/toolkit/components/places/tests/background/test_database_sync_after_quit_application.js @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 sts=2 expandtab + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shawn Wilsher (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +Components.utils.import("resource://gre/modules/PlacesBackground.jsm"); + +const TEST_URI = "http://test.com/"; +const kSyncPrefName = "syncDBTableIntervalInSecs"; +const SYNC_INTERVAL = 600; // ten minutes + +function run_test() +{ + // First set the preference for the timer to a really large value so it won't + // run before the test finishes. + let prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefService). + getBranch("places."); + prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL); + + // Now add the visit + let hs = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService); + let id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null, + hs.TRANSITION_TYPED, false, 0); + + // Notify that we are quitting the app - we should sync! + let os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + os.notifyObservers(null, "quit-application", null); + + // Check the visit. The background thread should have joined with the main + // thread by now if everything is working correctly. + new_test_visit_uri_event(id, TEST_URI, true, false).run(); +} diff --git a/toolkit/components/places/tests/background/test_multiple_bookmarks_around_sync.js b/toolkit/components/places/tests/background/test_multiple_bookmarks_around_sync.js new file mode 100644 index 00000000000..444a0ddc976 --- /dev/null +++ b/toolkit/components/places/tests/background/test_multiple_bookmarks_around_sync.js @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 sts=2 expandtab + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shawn Wilsher (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/** + * This test ensures that adding a bookmark (which has an implicit sync), then + * adding another one that has the same place, we end up with only one entry in + * moz_places. + */ + +Components.utils.import("resource://gre/modules/PlacesBackground.jsm"); + +const TEST_URI = "http://test.com/"; + +let db = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsPIPlacesDatabase). + DBConnection; + +function run_test() +{ + // Add the first bookmark + let bh = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. + getService(Ci.nsINavBookmarksService); + let id1 = bh.insertBookmark(bh.unfiledBookmarksFolder, uri(TEST_URI), + bh.DEFAULT_INDEX, "test"); + + // Ensure it was added + PlacesBackground.dispatch(new_test_bookmark_uri_event(id1, TEST_URI, true), + Ci.nsIEventTarget.DISPATCH_SYNC); + + // Get the place_id + let stmt = db.createStatement( + "SELECT fk " + + "FROM moz_bookmarks " + + "WHERE id = ?" + ); + stmt.bindInt64Parameter(0, id1); + do_check_true(stmt.executeStep()); + let place_id = stmt.getInt64(0); + stmt.finalize(); + stmt = null; + + // Now we add another bookmark to a different folder + let id2 = bh.insertBookmark(bh.toolbarFolder, uri(TEST_URI), + bh.DEFAULT_INDEX, "test"); + do_check_neq(id1, id2); + + // Ensure it was added + PlacesBackground.dispatch(new_test_bookmark_uri_event(id2, TEST_URI, true), + Ci.nsIEventTarget.DISPATCH_SYNC); + + // Check to make sure we have the same place_id + stmt = db.createStatement( + "SELECT * " + + "FROM moz_bookmarks " + + "WHERE id = ?1 " + + "AND fk = ?2" + ); + stmt.bindInt64Parameter(0, id2); + stmt.bindInt64Parameter(1, place_id); + do_check_true(stmt.executeStep()); + stmt.finalize(); + stmt = null; + + // finish_test() calls do_test_finished, so we call do_test_pending()... + do_test_pending(); + finish_test(); +} diff --git a/toolkit/components/places/tests/background/test_multiple_visits_around_sync.js b/toolkit/components/places/tests/background/test_multiple_visits_around_sync.js new file mode 100644 index 00000000000..9420c0c9ab5 --- /dev/null +++ b/toolkit/components/places/tests/background/test_multiple_visits_around_sync.js @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 sts=2 expandtab + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shawn Wilsher (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/** + * This test ensures that when adding a visit, then syncing, and adding another + * visit to the same url creates two visits and that we only end up with one + * entry in moz_places. + */ + +Components.utils.import("resource://gre/modules/PlacesBackground.jsm"); + +const TEST_URI = "http://test.com/"; +const kSyncPrefName = "syncDBTableIntervalInSecs"; +const SYNC_INTERVAL = 1; + +function run_test() +{ + // First set the preference for the timer to a small value + let prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefService). + getBranch("places."); + prefs.setIntPref(kSyncPrefName, SYNC_INTERVAL); + + // Now add the first visit + let hs = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService); + let id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null, + hs.TRANSITION_TYPED, false, 0); + + // Check the visit, but after enough time has passed for the DB flush service + // to have fired it's timer. + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback({ + notify: function(aTimer) + { + PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, true), + Ci.nsIEventTarget.DISPATCH_SYNC); + + // Get the place_id and pass it on + let db = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection; + let stmt = db.createStatement( + "SELECT place_id " + + "FROM moz_historyvisits " + + "WHERE id = ?" + ); + stmt.bindInt64Parameter(0, id); + do_check_true(stmt.executeStep()); + continue_test(id, stmt.getInt64(0)); + stmt.finalize(); + stmt = null; + } + }, (SYNC_INTERVAL * 1000) * 2, Ci.nsITimer.TYPE_ONE_SHOT); + do_test_pending(); +} + +function continue_test(aLastVisitId, aPlaceId) +{ + // Now we add another visit + let hs = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService); + let id = hs.addVisit(uri(TEST_URI), Date.now() * 1000, null, + hs.TRANSITION_TYPED, false, 0); + do_check_neq(aLastVisitId, id); + + // Check the visit, but after enough time has passed for the DB flush service + // to have fired it's timer. + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback({ + notify: function(aTimer) + { + PlacesBackground.dispatch(new_test_visit_uri_event(id, TEST_URI, true), + Ci.nsIEventTarget.DISPATCH_SYNC); + + // Check to make sure we have the same place_id + let db = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection; + let stmt = db.createStatement( + "SELECT * " + + "FROM moz_historyvisits " + + "WHERE id = ?1 " + + "AND place_id = ?2" + ); + stmt.bindInt64Parameter(0, id); + stmt.bindInt64Parameter(1, aPlaceId); + do_check_true(stmt.executeStep()); + stmt.finalize(); + stmt = null; + + finish_test(); + } + }, (SYNC_INTERVAL * 1000) * 2, Ci.nsITimer.TYPE_ONE_SHOT); +}