Bug 680550 - Handle removeAllPages more sanely in tests.
authorMarco Bonardo <mbonardo@mozilla.com>
Tue, 23 Aug 2011 14:34:15 +0200
changeset 75710 1e02b2b0d6a6c150d9acd9ea14ed2542be17fa73
parent 75709 d051bc49c30475b0f0f06078bf1139ced70e25f1
child 75711 8f2530ae725a8b5333c2052b7a3d195af74b80cf
child 75738 566f15cb9c5b8e01468f51e2b0f1b6ff692c8d3e
push id3
push userfelipc@gmail.com
push dateFri, 30 Sep 2011 20:09:13 +0000
bugs680550
milestone9.0a1
Bug 680550 - Handle removeAllPages more sanely in tests. r=dietrich
browser/base/content/test/browser_sanitizeDialog.js
browser/base/content/test/browser_sanitizeDialog_treeView.js
browser/components/places/tests/browser/browser_bookmarksProperties.js
browser/components/places/tests/browser/browser_library_infoBox.js
browser/components/places/tests/browser/head.js
browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placestitle.js
browser/components/privatebrowsing/test/browser/head.js
toolkit/components/places/tests/browser/Makefile.in
toolkit/components/places/tests/browser/browser_bug399606.js
toolkit/components/places/tests/browser/browser_settitle.js
toolkit/components/places/tests/browser/browser_visituri.js
toolkit/components/places/tests/browser/browser_visituri_nohistory.js
toolkit/components/places/tests/browser/browser_visituri_privatebrowsing.js
toolkit/components/places/tests/browser/head.js
toolkit/components/places/tests/queries/test_containersQueries_sorting.js
toolkit/components/places/tests/queries/test_transitions.js
toolkit/components/places/tests/unit/test_486978_sort_by_date_queries.js
toolkit/components/places/tests/unit/test_nsINavHistoryViewer.js
--- a/browser/base/content/test/browser_sanitizeDialog.js
+++ b/browser/base/content/test/browser_sanitizeDialog.js
@@ -51,18 +51,16 @@
  */
 
 Cc["@mozilla.org/moz/jssubscript-loader;1"].
   getService(Ci.mozIJSSubScriptLoader).
   loadSubScript("chrome://browser/content/sanitize.js");
 
 const dm = Cc["@mozilla.org/download-manager;1"].
            getService(Ci.nsIDownloadManager);
-const bhist = Cc["@mozilla.org/browser/global-history;2"].
-              getService(Ci.nsIBrowserHistory);
 const formhist = Cc["@mozilla.org/satchel/form-history;1"].
                  getService(Ci.nsIFormHistory2);
 
 // Add tests here.  Each is a function that's called by doNextTest().
 var gAllTests = [
 
   /**
    * Initializes the dialog to its default state.
@@ -578,17 +576,17 @@ WindowHelper.prototype = {
         wh.win = win;
 
         executeSoon(function () {
           // Some exceptions that reach here don't reach the test harness, but
           // ok()/is() do...
           try {
             if (wh.onunload)
               wh.onunload();
-            doNextTest();
+            waitForAsyncUpdates(doNextTest);
           }
           catch (exc) {
             win.close();
             ok(false, "Unexpected exception: " + exc + "\n" + exc.stack);
             finish();
           }
         });
       }, false);
@@ -693,35 +691,70 @@ function addFormEntryWithMinutesAgo(aMin
 /**
  * Adds a history visit to history.
  *
  * @param aMinutesAgo
  *        The visit will be visited this many minutes ago
  */
 function addHistoryWithMinutesAgo(aMinutesAgo) {
   let pURI = makeURI("http://" + aMinutesAgo + "-minutes-ago.com/");
-  bhist.addPageWithDetails(pURI,
-                           aMinutesAgo + " minutes ago",
-                           now_uSec - (aMinutesAgo * 60 * 1000 * 1000));
-  is(bhist.isVisited(pURI), true,
+  PlacesUtils.bhistory
+             .addPageWithDetails(pURI,
+                                 aMinutesAgo + " minutes ago",
+                                 now_uSec - (aMinutesAgo * 60 * 1000 * 1000));
+  is(PlacesUtils.bhistory.isVisited(pURI), true,
      "Sanity check: history visit " + pURI.spec +
      " should exist after creating it");
   return pURI;
 }
 
 /**
  * Removes all history visits, downloads, and form entries.
  */
 function blankSlate() {
-  bhist.removeAllPages();
+  PlacesUtils.bhistory.removeAllPages();
   dm.cleanUp();
   formhist.removeAllEntries();
 }
 
 /**
+ * Waits for all pending async statements on the default connection, before
+ * proceeding with aCallback.
+ *
+ * @param aCallback
+ *        Function to be called when done.
+ * @param aScope
+ *        Scope for the callback.
+ * @param aArguments
+ *        Arguments array for the callback.
+ *
+ * @note The result is achieved by asynchronously executing a query requiring
+ *       a write lock.  Since all statements on the same connection are
+ *       serialized, the end of this write operation means that all writes are
+ *       complete.  Note that WAL makes so that writers don't block readers, but
+ *       this is a problem only across different connections.
+ */
+function waitForAsyncUpdates(aCallback, aScope, aArguments)
+{
+  let scope = aScope || this;
+  let args = aArguments || [];
+  let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
+                              .DBConnection;
+  db.createAsyncStatement("BEGIN EXCLUSIVE").executeAsync();
+  db.createAsyncStatement("COMMIT").executeAsync({
+    handleResult: function() {},
+    handleError: function() {},
+    handleCompletion: function(aReason)
+    {
+      aCallback.apply(scope, args);
+    }
+  });
+}
+
+/**
  * Ensures that the given pref is the expected value.
  *
  * @param aPrefName
  *        The pref's sub-branch under the privacy branch
  * @param aExpectedVal
  *        The pref's expected value
  * @param aMsg
  *        Passed to is()
@@ -753,17 +786,17 @@ function downloadExists(aID)
 
 /**
  * Runs the next test in the gAllTests array.  If all tests have been run,
  * finishes the entire suite.
  */
 function doNextTest() {
   if (gAllTests.length <= gCurrTest) {
     blankSlate();
-    finish();
+    waitForAsyncUpdates(finish);
   }
   else {
     let ct = gCurrTest;
     gCurrTest++;
     gAllTests[ct]();
   }
 }
 
@@ -805,17 +838,17 @@ function ensureFormEntriesClearedState(a
  * @param aURIs
  *        Array of page URIs
  * @param aShouldBeCleared
  *        True if each visit to the URI should be cleared, false otherwise
  */
 function ensureHistoryClearedState(aURIs, aShouldBeCleared) {
   let niceStr = aShouldBeCleared ? "no longer" : "still";
   aURIs.forEach(function (aURI) {
-    is(bhist.isVisited(aURI), !aShouldBeCleared,
+    is(PlacesUtils.bhistory.isVisited(aURI), !aShouldBeCleared,
        "history visit " + aURI.spec + " should " + niceStr + " exist");
   });
 }
 
 /**
  * Ensures that the given pref is the expected value.
  *
  * @param aPrefName
@@ -831,10 +864,10 @@ function intPrefIs(aPrefName, aExpectedV
 
 ///////////////////////////////////////////////////////////////////////////////
 
 function test() {
   requestLongerTimeout(2);
   blankSlate();
   waitForExplicitFinish();
   // Kick off all the tests in the gAllTests array.
-  doNextTest();
+  waitForAsyncUpdates(doNextTest);
 }
--- a/browser/base/content/test/browser_sanitizeDialog_treeView.js
+++ b/browser/base/content/test/browser_sanitizeDialog_treeView.js
@@ -50,18 +50,16 @@
  */
 
 Cc["@mozilla.org/moz/jssubscript-loader;1"].
   getService(Ci.mozIJSSubScriptLoader).
   loadSubScript("chrome://browser/content/sanitize.js");
 
 const dm = Cc["@mozilla.org/download-manager;1"].
            getService(Ci.nsIDownloadManager);
-const bhist = Cc["@mozilla.org/browser/global-history;2"].
-              getService(Ci.nsIBrowserHistory);
 const formhist = Cc["@mozilla.org/satchel/form-history;1"].
                  getService(Ci.nsIFormHistory2);
 
 // Add tests here.  Each is a function that's called by doNextTest().
 var gAllTests = [
 
   /**
    * Moves the grippy around, makes sure it works OK.
@@ -497,35 +495,70 @@ function addFormEntryWithMinutesAgo(aMin
 /**
  * Adds a history visit to history.
  *
  * @param aMinutesAgo
  *        The visit will be visited this many minutes ago
  */
 function addHistoryWithMinutesAgo(aMinutesAgo) {
   let pURI = makeURI("http://" + aMinutesAgo + "-minutes-ago.com/");
-  bhist.addPageWithDetails(pURI,
-                           aMinutesAgo + " minutes ago",
-                           now_uSec - (aMinutesAgo * 60 * 1000 * 1000));
-  is(bhist.isVisited(pURI), true,
+  PlacesUtils.bhistory
+             .addPageWithDetails(pURI,
+                                 aMinutesAgo + " minutes ago",
+                                 now_uSec - (aMinutesAgo * 60 * 1000 * 1000));
+  is(PlacesUtils.bhistory.isVisited(pURI), true,
      "Sanity check: history visit " + pURI.spec +
      " should exist after creating it");
   return pURI;
 }
 
 /**
  * Removes all history visits, downloads, and form entries.
  */
 function blankSlate() {
-  bhist.removeAllPages();
+  PlacesUtils.bhistory.removeAllPages();
   dm.cleanUp();
   formhist.removeAllEntries();
 }
 
 /**
+ * Waits for all pending async statements on the default connection, before
+ * proceeding with aCallback.
+ *
+ * @param aCallback
+ *        Function to be called when done.
+ * @param aScope
+ *        Scope for the callback.
+ * @param aArguments
+ *        Arguments array for the callback.
+ *
+ * @note The result is achieved by asynchronously executing a query requiring
+ *       a write lock.  Since all statements on the same connection are
+ *       serialized, the end of this write operation means that all writes are
+ *       complete.  Note that WAL makes so that writers don't block readers, but
+ *       this is a problem only across different connections.
+ */
+function waitForAsyncUpdates(aCallback, aScope, aArguments)
+{
+  let scope = aScope || this;
+  let args = aArguments || [];
+  let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
+                              .DBConnection;
+  db.createAsyncStatement("BEGIN EXCLUSIVE").executeAsync();
+  db.createAsyncStatement("COMMIT").executeAsync({
+    handleResult: function() {},
+    handleError: function() {},
+    handleCompletion: function(aReason)
+    {
+      aCallback.apply(scope, args);
+    }
+  });
+}
+
+/**
  * Checks to see if the download with the specified ID exists.
  *
  * @param  aID
  *         The ID of the download to check
  * @return True if the download exists, false otherwise
  */
 function downloadExists(aID)
 {
@@ -543,17 +576,17 @@ function downloadExists(aID)
 
 /**
  * Runs the next test in the gAllTests array.  If all tests have been run,
  * finishes the entire suite.
  */
 function doNextTest() {
   if (gAllTests.length <= gCurrTest) {
     blankSlate();
-    finish();
+    waitForAsyncUpdates(finish);
   }
   else {
     let ct = gCurrTest;
     gCurrTest++;
     gAllTests[ct]();
   }
 }
 
@@ -595,17 +628,17 @@ function ensureFormEntriesClearedState(a
  * @param aURIs
  *        Array of page URIs
  * @param aShouldBeCleared
  *        True if each visit to the URI should be cleared, false otherwise
  */
 function ensureHistoryClearedState(aURIs, aShouldBeCleared) {
   let niceStr = aShouldBeCleared ? "no longer" : "still";
   aURIs.forEach(function (aURI) {
-    is(bhist.isVisited(aURI), !aShouldBeCleared,
+    is(PlacesUtils.bhistory.isVisited(aURI), !aShouldBeCleared,
        "history visit " + aURI.spec + " should " + niceStr + " exist");
   });
 }
 
 /**
  * Opens the sanitize dialog and runs a callback once it's finished loading.
  * 
  * @param aOnloadCallback
@@ -620,17 +653,17 @@ function openWindow(aOnloadCallback) {
     let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
     win.addEventListener("load", function onload(event) {
       win.removeEventListener("load", onload, false);
       executeSoon(function () {
         // Some exceptions that reach here don't reach the test harness, but
         // ok()/is() do...
         try {
           aOnloadCallback(win);
-          doNextTest();
+          waitForAsyncUpdates(doNextTest);
         }
         catch (exc) {
           win.close();
           ok(false, "Unexpected exception: " + exc + "\n" + exc.stack);
           finish();
         }
       });
     }, false);
@@ -644,10 +677,10 @@ function openWindow(aOnloadCallback) {
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 function test() {
   blankSlate();
   waitForExplicitFinish();
   // Kick off all the tests in the gAllTests array.
-  doNextTest();
+  waitForAsyncUpdates(doNextTest);
 }
--- a/browser/components/places/tests/browser/browser_bookmarksProperties.js
+++ b/browser/components/places/tests/browser/browser_bookmarksProperties.js
@@ -531,17 +531,17 @@ function test() {
 }
 
 function runNextTest() {
   // Cleanup from previous test.
   if (gCurrentTest) {
     gCurrentTest.cleanup();
     info("End of test: " + gCurrentTest.desc);
     gCurrentTest = null;
-    executeSoon(runNextTest);
+    waitForAsyncUpdates(runNextTest);
     return;
   }
 
   if (gTests.length > 0) {
     // Goto next tests.
     gCurrentTest = gTests.shift();
     info("Start of test: " + gCurrentTest.desc);
     gCurrentTest.setup();
--- a/browser/components/places/tests/browser/browser_library_infoBox.js
+++ b/browser/components/places/tests/browser/browser_library_infoBox.js
@@ -141,18 +141,17 @@ gTests.push({
     checkInfoBoxSelected(PO);
     ok(!infoBoxExpanderWrapper.hidden,
        "Expander button is not hidden for second bookmark item.");
     checkAddInfoFieldsNotCollapsed(PO);
     checkAddInfoFields(PO, "second bookmark item");
 
     menuNode.containerOpen = false;
 
-    bhist.removeAllPages();
-    nextTest();
+    waitForClearHistory(nextTest);
   }
 });
 
 function checkInfoBoxSelected(PO) {
   is(getAndCheckElmtById("detailsDeck").selectedIndex, 1,
      "Selected element in detailsDeck is infoBox.");
 }
 
--- a/browser/components/places/tests/browser/head.js
+++ b/browser/components/places/tests/browser/head.js
@@ -24,15 +24,56 @@ function openLibrary(callback, aLeftPane
                                   aLeftPaneRoot);
   waitForFocus(function () {
     callback(library);
   }, library);
 
   return library;
 }
 
+/**
+ * Waits for completion of a clear history operation, before
+ * proceeding with aCallback.
+ *
+ * @param aCallback
+ *        Function to be called when done.
+ */
 function waitForClearHistory(aCallback) {
   Services.obs.addObserver(function observeCH(aSubject, aTopic, aData) {
     Services.obs.removeObserver(observeCH, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
     aCallback();
   }, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
   PlacesUtils.bhistory.removeAllPages();
 }
+
+/**
+ * Waits for all pending async statements on the default connection, before
+ * proceeding with aCallback.
+ *
+ * @param aCallback
+ *        Function to be called when done.
+ * @param aScope
+ *        Scope for the callback.
+ * @param aArguments
+ *        Arguments array for the callback.
+ *
+ * @note The result is achieved by asynchronously executing a query requiring
+ *       a write lock.  Since all statements on the same connection are
+ *       serialized, the end of this write operation means that all writes are
+ *       complete.  Note that WAL makes so that writers don't block readers, but
+ *       this is a problem only across different connections.
+ */
+function waitForAsyncUpdates(aCallback, aScope, aArguments)
+{
+  let scope = aScope || this;
+  let args = aArguments || [];
+  let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
+                              .DBConnection;
+  db.createAsyncStatement("BEGIN EXCLUSIVE").executeAsync();
+  db.createAsyncStatement("COMMIT").executeAsync({
+    handleResult: function() {},
+    handleError: function() {},
+    handleCompletion: function(aReason)
+    {
+      aCallback.apply(scope, args);
+    }
+  });
+}
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placestitle.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placestitle.js
@@ -37,89 +37,73 @@
 
 // This test makes sure that the title of existing history entries does not
 // change inside the private browsing mode.
 
 function test() {
   // initialization
   let pb = Cc["@mozilla.org/privatebrowsing;1"].
            getService(Ci.nsIPrivateBrowsingService);
-  let bhist = Cc["@mozilla.org/browser/global-history;2"].
-              getService(Ci.nsIBrowserHistory);
-  let histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
-                getService(Ci.nsINavHistoryService).
-                QueryInterface(Ci.nsPIPlacesDatabase);
   let cm = Cc["@mozilla.org/cookiemanager;1"].
            getService(Ci.nsICookieManager);
   waitForExplicitFinish();
 
   const TEST_URL = "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/title.sjs";
 
-  function cleanup() {
-    // delete all history items
-    bhist.removeAllPages();
+  function waitForCleanup(aCallback) {
     // delete all cookies
     cm.removeAll();
+    // delete all history items
+    waitForClearHistory(aCallback);
   }
-  cleanup();
 
   let observer = {
     pass: 1,
-    onBeginUpdateBatch: function() {
-    },
-    onEndUpdateBatch: function() {
-    },
-    onVisit: function(aURI, aVisitID, aTime, aSessionId, aReferringId,
-                      aTransitionType, _added) {
-    },
     onTitleChanged: function(aURI, aPageTitle) {
       if (aURI.spec != TEST_URL)
         return;
       switch (this.pass++) {
       case 1: // the first time that the page is loaded
         is(aPageTitle, "No Cookie", "The page should be loaded without any cookie for the first time");
         gBrowser.selectedTab = gBrowser.addTab(TEST_URL);
         break;
       case 2: // the second time that the page is loaded
         is(aPageTitle, "Cookie", "The page should be loaded with a cookie for the second time");
-        cleanup();
-        gBrowser.selectedTab = gBrowser.addTab(TEST_URL);
+        waitForCleanup(function () {
+          gBrowser.selectedTab = gBrowser.addTab(TEST_URL);
+        });
         break;
       case 3: // before entering the private browsing mode
         is(aPageTitle, "No Cookie", "The page should be loaded without any cookie again");
         // enter private browsing mode
         pb.privateBrowsingEnabled = true;
         gBrowser.selectedTab = gBrowser.addTab(TEST_URL);
         executeSoon(function() {
-          histsvc.removeObserver(observer);
+          PlacesUtils.history.removeObserver(observer);
           pb.privateBrowsingEnabled = false;
-          while (gBrowser.browsers.length > 1)
+          while (gBrowser.browsers.length > 1) {
             gBrowser.removeCurrentTab();
-          cleanup();
-          finish();
+          }
+          waitForCleanup(finish);
         });
         break;
       default:
         ok(false, "Unexpected pass: " + (this.pass - 1));
       }
     },
-    onBeforeDeleteURI: function(aURI) {
-    },
-    onDeleteURI: function(aURI) {
-    },
-    onClearHistory: function() {
-    },
-    onPageChanged: function(aURI, aWhat, aValue) {
-    },
-    onDeleteVisits: function() {
-    },
-    QueryInterface: function(iid) {
-      if (iid.equals(Ci.nsINavHistoryObserver) ||
-          iid.equals(Ci.nsISupports)) {
-        return this;
-      }
-      throw Cr.NS_ERROR_NO_INTERFACE;
-    }
+
+    onBeginUpdateBatch: function () {},
+    onEndUpdateBatch: function () {},
+    onVisit: function () {},
+    onBeforeDeleteURI: function () {},
+    onDeleteURI: function () {},
+    onClearHistory: function () {},
+    onPageChanged: function () {},
+    onDeleteVisits: function() {},
+
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver])
   };
-  histsvc.addObserver(observer, false);
+  PlacesUtils.history.addObserver(observer, false);
 
-  gBrowser.selectedTab = gBrowser.addTab(TEST_URL);
+  waitForCleanup(function () {
+    gBrowser.selectedTab = gBrowser.addTab(TEST_URL);
+  });
 }
--- a/browser/components/privatebrowsing/test/browser/head.js
+++ b/browser/components/privatebrowsing/test/browser/head.js
@@ -4,8 +4,22 @@ registerCleanupFunction(function() {
            getService(Ci.nsIPrivateBrowsingService);
   ok(!pb.privateBrowsingEnabled, "Private browsing should be terminated after finishing the test");
   pb.privateBrowsingEnabled = false;
   try {
     Services.prefs.clearUserPref("browser.privatebrowsing.keep_current_session");
   } catch(e) {}
 });
 
+/**
+ * Waits for completion of a clear history operation, before
+ * proceeding with aCallback.
+ *
+ * @param aCallback
+ *        Function to be called when done.
+ */
+function waitForClearHistory(aCallback) {
+  Services.obs.addObserver(function observeCH(aSubject, aTopic, aData) {
+    Services.obs.removeObserver(observeCH, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
+    aCallback();
+  }, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
+  PlacesUtils.bhistory.removeAllPages();
+}
--- a/toolkit/components/places/tests/browser/Makefile.in
+++ b/toolkit/components/places/tests/browser/Makefile.in
@@ -41,16 +41,17 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = toolkit/components/places/tests/browser
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
+	head.js \
 	browser_bug399606.js \
 	browser_visituri.js \
 	browser_visituri_nohistory.js \
 	browser_visituri_privatebrowsing.js \
 	browser_settitle.js \
 	browser_bug646422.js \
 	$(NULL)
 
--- a/toolkit/components/places/tests/browser/browser_bug399606.js
+++ b/toolkit/components/places/tests/browser/browser_bug399606.js
@@ -70,32 +70,16 @@ function test() {
     onDeleteURI: function () {},
     onClearHistory: function () {},
     onPageChanged: function () {},
     onDeleteVisits: function () {},
     QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver])
   };
   hs.addObserver(historyObserver, false);
 
-  /**
-   * Clears history invoking callback when done.
-   */
-  function waitForClearHistory(aCallback)
-  {
-    let observer = {
-      observe: function(aSubject, aTopic, aData)
-      {
-        Services.obs.removeObserver(this, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
-        aCallback(aSubject, aTopic, aData);
-      }
-    };
-    Services.obs.addObserver(observer, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
-    PlacesUtils.bhistory.removeAllPages();
-  }
-
   function confirm_results() {
     gBrowser.removeCurrentTab();
     hs.removeObserver(historyObserver, false);
     for (let aURI in historyObserver.visitCount) {
       is(historyObserver.visitCount[aURI], 1,
          "onVisit has been received right number of times for " + aURI);
     }
     waitForClearHistory(finish);
--- a/toolkit/components/places/tests/browser/browser_settitle.js
+++ b/toolkit/components/places/tests/browser/browser_settitle.js
@@ -41,34 +41,16 @@ function getColumn(table, column, fromCo
     stmt.executeStep();
     return stmt.row[column];
   }
   finally {
     stmt.finalize();
   }
 }
 
-/**
- * Clears history invoking callback when done.
- */
-function waitForClearHistory(aCallback) {
-  const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
-  let observer = {
-    observe: function(aSubject, aTopic, aData) {
-      Services.obs.removeObserver(this, TOPIC_EXPIRATION_FINISHED);
-      aCallback();
-    }
-  };
-  Services.obs.addObserver(observer, TOPIC_EXPIRATION_FINISHED, false);
-
-  let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
-           getService(Ci.nsINavHistoryService);
-  hs.QueryInterface(Ci.nsIBrowserHistory).removeAllPages();
-}
-
 function test()
 {
   // Make sure titles are correctly saved for a URI with the proper
   // notifications.
 
   waitForExplicitFinish();
 
   // Create and add history observer.
--- a/toolkit/components/places/tests/browser/browser_visituri.js
+++ b/toolkit/components/places/tests/browser/browser_visituri.js
@@ -58,34 +58,16 @@ function getColumn(table, column, fromCo
     ok(stmt.executeStep(), "Expect to get a row");
     return stmt.row[column];
   }
   finally {
     stmt.reset();
   }
 }
 
-/**
- * Clears history invoking callback when done.
- */
-function waitForClearHistory(aCallback) {
-  const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
-  let observer = {
-    observe: function(aSubject, aTopic, aData) {
-      Services.obs.removeObserver(this, TOPIC_EXPIRATION_FINISHED);
-      aCallback();
-    }
-  };
-  Services.obs.addObserver(observer, TOPIC_EXPIRATION_FINISHED, false);
-
-  let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
-           getService(Ci.nsINavHistoryService);
-  hs.QueryInterface(Ci.nsIBrowserHistory).removeAllPages();
-}
-
 function test()
 {
   // Make sure places visit chains are saved correctly with a redirect
   // transitions.
 
   waitForExplicitFinish();
 
   // Part 1: observe history events that fire when a visit occurs.
--- a/toolkit/components/places/tests/browser/browser_visituri_nohistory.js
+++ b/toolkit/components/places/tests/browser/browser_visituri_nohistory.js
@@ -32,32 +32,16 @@ function waitForLoad(callback)
 {
   gTab.linkedBrowser.addEventListener("load", function()
   {
     gTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
     callback();
   }, true);
 }
 
-/**
- * Clears history invoking callback when done.
- */
-function waitForClearHistory(aCallback)
-{
-  let observer = {
-    observe: function(aSubject, aTopic, aData)
-    {
-      Services.obs.removeObserver(this, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
-      aCallback(aSubject, aTopic, aData);
-    }
-  };
-  Services.obs.addObserver(observer, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
-  PlacesUtils.bhistory.removeAllPages();
-}
-
 function test()
 {
   waitForExplicitFinish();
 
   Services.prefs.setBoolPref("places.history.enabled", false);
 
   waitForObserve("uri-visit-saved", function(subject, topic, data)
   {
--- a/toolkit/components/places/tests/browser/browser_visituri_privatebrowsing.js
+++ b/toolkit/components/places/tests/browser/browser_visituri_privatebrowsing.js
@@ -32,32 +32,16 @@ function waitForLoad(callback)
 {
   gTab.linkedBrowser.addEventListener("load", function()
   {
     gTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
     callback();
   }, true);
 }
 
-/**
- * Clears history invoking callback when done.
- */
-function waitForClearHistory(aCallback)
-{
-  let observer = {
-    observe: function(aSubject, aTopic, aData)
-    {
-      Services.obs.removeObserver(this, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
-      aCallback(aSubject, aTopic, aData);
-    }
-  };
-  Services.obs.addObserver(observer, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
-  PlacesUtils.bhistory.removeAllPages();
-}
-
 function test()
 {
   if (!("@mozilla.org/privatebrowsing;1" in Cc)) {
     todo(false, "PB service is not available, bail out");
     return;
   }
 
   gTab = gBrowser.selectedTab = gBrowser.addTab();
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/browser/head.js
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+function waitForClearHistory(aCallback) {
+  Services.obs.addObserver(function observeCH(aSubject, aTopic, aData) {
+    Services.obs.removeObserver(observeCH, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
+    aCallback();
+  }, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
+  PlacesUtils.bhistory.removeAllPages();
+}
--- a/toolkit/components/places/tests/queries/test_containersQueries_sorting.js
+++ b/toolkit/components/places/tests/queries/test_containersQueries_sorting.js
@@ -434,14 +434,9 @@ function run_test()
       lastVisit: (timeInMilliseconds - (18000 * 1000 * dayOffset++)) * 1000,
       visitCount: visitCount++,
       isTag: true,
       tagArray: tags,
       isInQuery: true }));
   populateDB(visits);
 
   cartProd([resultTypes, sortingModes], test_query_callback);
-
-  // Cleanup.
-  pages.forEach(function(aPageUrl) tagging.untagURI(uri(aPageUrl), tags));
-  remove_all_bookmarks();
-  bh.removeAllPages();
 }
--- a/toolkit/components/places/tests/queries/test_transitions.js
+++ b/toolkit/components/places/tests/queries/test_transitions.js
@@ -146,18 +146,16 @@ function run_test() {
   root.containerOpen = true;
   do_check_eq(testDataDownload.length, root.childCount);
   PlacesUtils.history
       .addVisit(PlacesUtils._uri("http://getfirefox.com"),
                 Date.now() * 1000, null,
                 Ci.nsINavHistoryService.TRANSITION_DOWNLOAD, false, 0);
   do_check_eq(testDataDownload.length + 1, root.childCount);
   root.containerOpen = false;
-
-  PlacesUtils.bhistory.removeAllPages();
 }
 
 /*
  * Takes a query and a set of indices. The indices correspond to elements
  * of testData that are the result of the query.
  */
 function compareQueryToTestData(queryStr, data) {
   var query = {};
--- a/toolkit/components/places/tests/unit/test_486978_sort_by_date_queries.js
+++ b/toolkit/components/places/tests/unit/test_486978_sort_by_date_queries.js
@@ -160,11 +160,9 @@ function run_test() {
   // Add visits.
   pages.forEach(function(aPage) {
       add_visit(aPage, noon - (pages.length - pages.indexOf(aPage)) * 1000);
     });
 
   // Kick off tests.
   while (gTests.length)
     (gTests.shift())();
-
-  hs.QueryInterface(Ci.nsIBrowserHistory).removeAllPages();
 }
--- a/toolkit/components/places/tests/unit/test_nsINavHistoryViewer.js
+++ b/toolkit/components/places/tests/unit/test_nsINavHistoryViewer.js
@@ -127,26 +127,21 @@ var resultObserver = {
     this.closedContainer = null;
     this.invalidatedContainer = null;
     this.sortingMode = null;
   }
 };
 
 var testURI = uri("http://mozilla.com");
 
-// main
 function run_test() {
-  check_history_query();
-  resultObserver.reset();
-  check_bookmarks_query();
-  resultObserver.reset();
-  check_mixed_query();
+  run_next_test();
 }
 
-function check_history_query() {
+add_test(function check_history_query() {
   var options = histsvc.getNewQueryOptions();
   options.sortingMode = options.SORT_BY_DATE_DESCENDING;
   options.resultType = options.RESULTS_AS_VISIT;
   var query = histsvc.getNewQuery();
   var result = histsvc.executeQuery(query, options);
   result.addObserver(resultObserver, false);
   var root = result.root;
   root.containerOpen = true;
@@ -204,19 +199,21 @@ function check_history_query() {
     }
   }, null);
   do_check_false(resultObserver.inBatchMode);
 
   // nsINavHistoryResultObserver.containerClosed
   root.containerOpen = false;
   do_check_eq(resultObserver.closedContainer, resultObserver.openedContainer);
   result.removeObserver(resultObserver);
-}
+  resultObserver.reset();
+  waitForAsyncUpdates(run_next_test);
+});
 
-function check_bookmarks_query() {
+add_test(function check_bookmarks_query() {
   var options = histsvc.getNewQueryOptions();
   var query = histsvc.getNewQuery();
   query.setFolders([bmsvc.bookmarksMenuFolder], 1);
   var result = histsvc.executeQuery(query, options);
   result.addObserver(resultObserver, false);
   var root = result.root;
   root.containerOpen = true;
 
@@ -271,19 +268,21 @@ function check_bookmarks_query() {
     }
   }, null);
   do_check_false(resultObserver.inBatchMode);
 
   // nsINavHistoryResultObserver.containerClosed
   root.containerOpen = false;
   do_check_eq(resultObserver.closedContainer, resultObserver.openedContainer);
   result.removeObserver(resultObserver);
-}
+  resultObserver.reset();
+  waitForAsyncUpdates(run_next_test);
+});
 
-function check_mixed_query() {
+add_test(function check_mixed_query() {
   var options = histsvc.getNewQueryOptions();
   var query = histsvc.getNewQuery();
   query.onlyBookmarked = true;
   var result = histsvc.executeQuery(query, options);
   result.addObserver(resultObserver, false);
   var root = result.root;
   root.containerOpen = true;
 
@@ -304,9 +303,11 @@ function check_mixed_query() {
     }
   }, null);
   do_check_false(resultObserver.inBatchMode);
 
   // nsINavHistoryResultObserver.containerClosed
   root.containerOpen = false;
   do_check_eq(resultObserver.closedContainer, resultObserver.openedContainer);
   result.removeObserver(resultObserver);
-}
+  resultObserver.reset();
+  waitForAsyncUpdates(run_next_test);
+});