Bug 1002439 - browser_bug248970.js is almost perma fail when run by directory on osx opt. r=mano, a=test-only
authorMarco Bonardo <mbonardo@mozilla.com>
Tue, 12 Aug 2014 12:36:49 +0200
changeset 208362 0b44c271f755
parent 208361 c340fefc0fe8
child 208363 d94be43c729c
push id3841
push userryanvm@gmail.com
push date2014-08-21 14:28 +0000
treeherdermozilla-beta@0b44c271f755 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmano, test-only
bugs1002439, 248970
milestone32.0
Bug 1002439 - browser_bug248970.js is almost perma fail when run by directory on osx opt. r=mano, a=test-only
toolkit/components/places/tests/browser/browser_bug248970.js
toolkit/components/places/tests/browser/head.js
--- a/toolkit/components/places/tests/browser/browser_bug248970.js
+++ b/toolkit/components/places/tests/browser/browser_bug248970.js
@@ -10,147 +10,133 @@ let visitedURIs = [
   "http://www.test-link.com/",
   "http://www.test-typed.com/",
   "http://www.test-bookmark.com/",
   "http://www.test-redirect-permanent.com/",
   "http://www.test-redirect-temporary.com/",
   "http://www.test-embed.com/",
   "http://www.test-framed.com/",
   "http://www.test-download.com/"
-];
+].map(NetUtil.newURI.bind(NetUtil));
 
-function test() {
-  waitForExplicitFinish();
-
+add_task(function () {
   let windowsToClose = [];
-  let windowCount = 0;
   let placeItemsCount = 0;
 
   registerCleanupFunction(function() {
     windowsToClose.forEach(function(win) {
       win.close();
     });
   });
 
-  function testOnWindow(aIsPrivate, aCallback) {
-    whenNewWindowLoaded(aIsPrivate, function(win) {
-      windowsToClose.push(win);
-      checkPlaces(win, aIsPrivate, aCallback);
-    });
-  }
+  yield promiseClearHistory();
+
+  // History database should be empty
+  is(PlacesUtils.history.hasHistoryEntries, false,
+     "History database should be empty");
+
+   // Ensure we wait for the default bookmarks import.
+  let bookmarksDeferred = Promise.defer();
+  waitForCondition(() => {
+    placeItemsCount = getPlacesItemsCount();
+    return placeItemsCount > 0
+  }, bookmarksDeferred.resolve, "Should have default bookmarks");
+  yield bookmarksDeferred.promise;
 
-  function checkPlaces(aWindow, aIsPrivate, aCallback) {
+  // Create a handful of history items with various visit types
+  yield promiseAddVisits([
+    { uri: visitedURIs[0], transition: TRANSITION_LINK },
+    { uri: visitedURIs[1], transition: TRANSITION_TYPED },
+    { uri: visitedURIs[2], transition: TRANSITION_BOOKMARK },
+    { uri: visitedURIs[3], transition: TRANSITION_REDIRECT_PERMANENT },
+    { uri: visitedURIs[4], transition: TRANSITION_REDIRECT_TEMPORARY },
+    { uri: visitedURIs[5], transition: TRANSITION_EMBED },
+    { uri: visitedURIs[6], transition: TRANSITION_FRAMED_LINK },
+    { uri: visitedURIs[7], transition: TRANSITION_DOWNLOAD }
+  ]);
+
+  // History database should have entries
+  is(PlacesUtils.history.hasHistoryEntries, true,
+     "History database should have entries");
+
+  placeItemsCount += 7;
+  // We added 7 new items to history.
+  is(getPlacesItemsCount(), placeItemsCount,
+     "Check the total items count");
+
+  function* testOnWindow(aIsPrivate, aCount) {
+    let deferred = Promise.defer();
+    whenNewWindowLoaded({ private: aIsPrivate }, deferred.resolve);
+    let win = yield deferred.promise;
+    windowsToClose.push(win);
+
     // History items should be retrievable by query
-    Task.spawn(checkHistoryItems).then(function() {
-      // Updates the place items count
-      placeItemsCount = getPlacesItemsCount(aWindow);
-      // Create Bookmark
-      let bookmarkTitle = "title " + windowCount;
-      let bookmarkKeyword = "keyword " + windowCount;
-      let bookmarkUri = NetUtil.newURI("http://test-a-" + windowCount + ".com/");
-      createBookmark(aWindow, bookmarkUri, bookmarkTitle, bookmarkKeyword);
-      placeItemsCount++;
-      windowCount++;
-      ok(PlacesUtils.bookmarks.isBookmarked(bookmarkUri),
-         "Bookmark should be bookmarked, data should be retrievable");
-      is(bookmarkKeyword, PlacesUtils.bookmarks.getKeywordForURI(bookmarkUri),
-         "Check bookmark uri keyword");
-      is(getPlacesItemsCount(aWindow), placeItemsCount,
-         "Check the new bookmark items count");
-      is(isBookmarkAltered(aWindow), false, "Check if bookmark has been visited");
+    yield checkHistoryItems();
+
+    // Updates the place items count
+    let count = getPlacesItemsCount();
+
+    // Create Bookmark
+    let bookmarkTitle = "title " + windowsToClose.length;
+    let bookmarkKeyword = "keyword " + windowsToClose.length;
+    let bookmarkUri = NetUtil.newURI("http://test-a-" + windowsToClose.length + ".com/");
 
-      aCallback();
-    });
+    let id = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.bookmarksMenuFolderId,
+                                                  bookmarkUri,
+                                                  PlacesUtils.bookmarks.DEFAULT_INDEX,
+                                                  bookmarkTitle);
+    PlacesUtils.bookmarks.setKeywordForBookmark(id, bookmarkKeyword);
+    count++;
+
+    ok(PlacesUtils.bookmarks.isBookmarked(bookmarkUri),
+       "Bookmark should be bookmarked, data should be retrievable");
+    is(bookmarkKeyword, PlacesUtils.bookmarks.getKeywordForURI(bookmarkUri),
+       "Check bookmark uri keyword");
+    is(getPlacesItemsCount(), count,
+       "Check the new bookmark items count");
+    is(isBookmarkAltered(), false, "Check if bookmark has been visited");
   }
 
-  clearHistory(function() {
-    // Updates the place items count
-    placeItemsCount = getPlacesItemsCount(window);
-    // History database should be empty
-    is(PlacesUtils.history.hasHistoryEntries, false,
-       "History database should be empty");
-    // Create a handful of history items with various visit types
-    fillHistoryVisitedURI(window, function() {
-      placeItemsCount += 7;
-      // History database should have entries
-      is(PlacesUtils.history.hasHistoryEntries, true,
-         "History database should have entries");
-      // We added 7 new items to history.
-      is(getPlacesItemsCount(window), placeItemsCount,
-         "Check the total items count");
-      // Test on windows.
-      testOnWindow(false, function() {
-        testOnWindow(true, function() {
-          testOnWindow(false, finish);
-        });
-      });
-    });
-  });
-}
-
-function whenNewWindowLoaded(aIsPrivate, aCallback) {
-  let win = OpenBrowserWindow({private: aIsPrivate});
-  win.addEventListener("load", function onLoad() {
-    win.removeEventListener("load", onLoad, false);
-    aCallback(win);
-  }, false);
-}
-
-function clearHistory(aCallback) {
-  Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
-    Services.obs.removeObserver(observer, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
-    aCallback();
-  }, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
-  PlacesUtils.bhistory.removeAllPages();
-}
+  // Test on windows.
+  yield testOnWindow(false);
+  yield testOnWindow(true);
+  yield testOnWindow(false);
+});
 
 /**
  * Function performs a really simple query on our places entries,
  * and makes sure that the number of entries equal num_places_entries.
  */
-function getPlacesItemsCount(aWin){
+function getPlacesItemsCount() {
   // Get bookmarks count
-  let options = aWin.PlacesUtils.history.getNewQueryOptions();
+  let options = PlacesUtils.history.getNewQueryOptions();
   options.includeHidden = true;
   options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS;
-  let root = aWin.PlacesUtils.history.executeQuery(
-    aWin.PlacesUtils.history.getNewQuery(), options).root;
+  let root = PlacesUtils.history.executeQuery(
+    PlacesUtils.history.getNewQuery(), options).root;
   root.containerOpen = true;
   let cc = root.childCount;
   root.containerOpen = false;
 
   // Get history item count
   options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY;
-  let root = aWin.PlacesUtils.history.executeQuery(
-    aWin.PlacesUtils.history.getNewQuery(), options).root;
+  let root = PlacesUtils.history.executeQuery(
+    PlacesUtils.history.getNewQuery(), options).root;
   root.containerOpen = true;
   cc += root.childCount;
   root.containerOpen = false;
 
   return cc;
 }
 
-function fillHistoryVisitedURI(aWin, aCallback) {
-  addVisits([
-    {uri: NetUtil.newURI(visitedURIs[0]), transition: PlacesUtils.history.TRANSITION_LINK},
-    {uri: NetUtil.newURI(visitedURIs[1]), transition: PlacesUtils.history.TRANSITION_TYPED},
-    {uri: NetUtil.newURI(visitedURIs[2]), transition: PlacesUtils.history.TRANSITION_BOOKMARK},
-    {uri: NetUtil.newURI(visitedURIs[3]), transition: PlacesUtils.history.TRANSITION_REDIRECT_PERMANENT},
-    {uri: NetUtil.newURI(visitedURIs[4]), transition: PlacesUtils.history.TRANSITION_REDIRECT_TEMPORARY},
-    {uri: NetUtil.newURI(visitedURIs[5]), transition: PlacesUtils.history.TRANSITION_EMBED},
-    {uri: NetUtil.newURI(visitedURIs[6]), transition: PlacesUtils.history.TRANSITION_FRAMED_LINK},
-    {uri: NetUtil.newURI(visitedURIs[7]), transition: PlacesUtils.history.TRANSITION_DOWNLOAD}],
-    aWin, aCallback);
-}
-
-function checkHistoryItems() {
+function* checkHistoryItems() {
   for (let i = 0; i < visitedURIs.length; i++) {
     let visitedUri = visitedURIs[i];
-    ok((yield promiseIsURIVisited(NetUtil.newURI(visitedUri))), "");
-    if (/embed/.test(visitedUri)) {
+    ok((yield promiseIsURIVisited(visitedUri)), "");
+    if (/embed/.test(visitedUri.spec)) {
       is(!!pageInDatabase(visitedUri), false, "Check if URI is in database");
     } else {
       ok(!!pageInDatabase(visitedUri), "Check if URI is in database");
     }
   }
 }
 
 /**
@@ -170,84 +156,30 @@ function pageInDatabase(aURI) {
       return 0;
     return stmt.getInt64(0);
   } finally {
     stmt.finalize();
   }
 }
 
 /**
- * Gets the database connection.  If the Places connection is invalid it will
- * try to create a new connection.
- *
- * @param [optional] aForceNewConnection
- *        Forces creation of a new connection to the database.  When a
- *        connection is asyncClosed it cannot anymore schedule async statements,
- *        though connectionReady will keep returning true (Bug 726990).
- *
- * @return The database connection or null if unable to get one.
- */
-let gDBConn;
-function DBConn(aForceNewConnection) {
-  if (!aForceNewConnection) {
-    let db =
-      PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
-    if (db.connectionReady)
-      return db;
-  }
-
-  // If the Places database connection has been closed, create a new connection.
-  if (!gDBConn || aForceNewConnection) {
-    let file = Services.dirsvc.get('ProfD', Ci.nsIFile);
-    file.append("places.sqlite");
-    let dbConn = gDBConn = Services.storage.openDatabase(file);
-
-    // Be sure to cleanly close this connection.
-    Services.obs.addObserver(function DBCloseCallback(aSubject, aTopic, aData) {
-      Services.obs.removeObserver(DBCloseCallback, aTopic);
-      dbConn.asyncClose();
-    }, "profile-before-change", false);
-  }
-
-  return gDBConn.connectionReady ? gDBConn : null;
-};
-
-/**
- * Function creates a bookmark
- * @param aURI
- *        The URI for the bookmark
- * @param aTitle
- *        The title for the bookmark
- * @param aKeyword
- *        The keyword for the bookmark
- * @returns the bookmark
- */
-function createBookmark(aWin, aURI, aTitle, aKeyword) {
-  let bookmarkID = aWin.PlacesUtils.bookmarks.insertBookmark(
-    aWin.PlacesUtils.bookmarksMenuFolderId, aURI,
-    aWin.PlacesUtils.bookmarks.DEFAULT_INDEX, aTitle);
-  aWin.PlacesUtils.bookmarks.setKeywordForBookmark(bookmarkID, aKeyword);
-  return bookmarkID;
-}
-
-/**
  * Function attempts to check if Bookmark-A has been visited
  * during private browsing mode, function should return false
  *
  * @returns false if the accessCount has not changed
  *          true if the accessCount has changed
  */
-function isBookmarkAltered(aWin){
-  let options = aWin.PlacesUtils.history.getNewQueryOptions();
+function isBookmarkAltered(){
+  let options = PlacesUtils.history.getNewQueryOptions();
   options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS;
   options.maxResults = 1; // should only expect a new bookmark
 
-  let query = aWin.PlacesUtils.history.getNewQuery();
-  query.setFolders([aWin.PlacesUtils.bookmarks.bookmarksMenuFolder], 1);
+  let query = PlacesUtils.history.getNewQuery();
+  query.setFolders([PlacesUtils.bookmarks.bookmarksMenuFolder], 1);
 
-  let root = aWin.PlacesUtils.history.executeQuery(query, options).root;
+  let root = PlacesUtils.history.executeQuery(query, options).root;
   root.containerOpen = true;
   is(root.childCount, options.maxResults, "Check new bookmarks results");
   let node = root.getChild(0);
   root.containerOpen = false;
 
   return (node.accessCount != 0);
 }
--- a/toolkit/components/places/tests/browser/head.js
+++ b/toolkit/components/places/tests/browser/head.js
@@ -1,12 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 const TRANSITION_LINK = Ci.nsINavHistoryService.TRANSITION_LINK;
 const TRANSITION_TYPED = Ci.nsINavHistoryService.TRANSITION_TYPED;
+const TRANSITION_BOOKMARK = Ci.nsINavHistoryService.TRANSITION_BOOKMARK;
+const TRANSITION_REDIRECT_PERMANENT = Ci.nsINavHistoryService.TRANSITION_REDIRECT_PERMANENT;
+const TRANSITION_REDIRECT_TEMPORARY = Ci.nsINavHistoryService.TRANSITION_REDIRECT_TEMPORARY;
+const TRANSITION_EMBED = Ci.nsINavHistoryService.TRANSITION_EMBED;
+const TRANSITION_FRAMED_LINK = Ci.nsINavHistoryService.TRANSITION_FRAMED_LINK;
+const TRANSITION_DOWNLOAD = Ci.nsINavHistoryService.TRANSITION_DOWNLOAD;
 
 Components.utils.import("resource://gre/modules/NetUtil.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 
@@ -173,18 +179,18 @@ function waitForFaviconChanged(aExpected
   };
   aWindow.PlacesUtils.history.addObserver(historyObserver, false);
 }
 
 /**
  * Asynchronously adds visits to a page, invoking a callback function when done.
  *
  * @param aPlaceInfo
- *        Can be an nsIURI, in such a case a single LINK visit will be added.
- *        Otherwise can be an object describing the visit to add, or an array
+ *        Either an nsIURI, in such a case a single LINK visit will be added.
+ *        Or can be an object describing the visit to add, or an array
  *        of these objects:
  *          { uri: nsIURI of the page,
  *            transition: one of the TRANSITION_* from nsINavHistoryService,
  *            [optional] title: title of the page,
  *            [optional] visitDate: visit date in microseconds from the epoch
  *            [optional] referrer: nsIURI of the referrer for this visit
  *          }
  * @param [optional] aCallback
@@ -201,25 +207,25 @@ function addVisits(aPlaceInfo, aWindow, 
   else if (Array.isArray(aPlaceInfo)) {
     places = places.concat(aPlaceInfo);
   } else {
     places.push(aPlaceInfo)
   }
 
   // Create mozIVisitInfo for each entry.
   let now = Date.now();
-  for (let i = 0; i < places.length; i++) {
-    if (!places[i].title) {
-      places[i].title = "test visit for " + places[i].uri.spec;
+  for (let place of places) {
+    if (!place.title) {
+      place.title = "test visit for " + place.uri.spec;
     }
-    places[i].visits = [{
-      transitionType: places[i].transition === undefined ? TRANSITION_LINK
-                                                         : places[i].transition,
-      visitDate: places[i].visitDate || (now++) * 1000,
-      referrerURI: places[i].referrer
+    place.visits = [{
+      transitionType: place.transition === undefined ? TRANSITION_LINK
+                                                     : place.transition,
+      visitDate: place.visitDate || (now++) * 1000,
+      referrerURI: place.referrer
     }];
   }
 
   aWindow.PlacesUtils.asyncHistory.updatePlaces(
     places,
     {
       handleError: function AAV_handleError() {
         throw("Unexpected error in adding visit.");
@@ -229,16 +235,79 @@ function addVisits(aPlaceInfo, aWindow, 
         if (aCallback)
           aCallback();
       }
     }
   );
 }
 
 /**
+ * Asynchronously adds visits to a page.
+ *
+ * @param aPlaceInfo
+ *        Can be an nsIURI, in such a case a single LINK visit will be added.
+ *        Otherwise can be an object describing the visit to add, or an array
+ *        of these objects:
+ *          { uri: nsIURI of the page,
+ *            transition: one of the TRANSITION_* from nsINavHistoryService,
+ *            [optional] title: title of the page,
+ *            [optional] visitDate: visit date in microseconds from the epoch
+ *            [optional] referrer: nsIURI of the referrer for this visit
+ *          }
+ *
+ * @return {Promise}
+ * @resolves When all visits have been added successfully.
+ * @rejects JavaScript exception.
+ */
+function promiseAddVisits(aPlaceInfo)
+{
+  let deferred = Promise.defer();
+  let places = [];
+  if (aPlaceInfo instanceof Ci.nsIURI) {
+    places.push({ uri: aPlaceInfo });
+  }
+  else if (Array.isArray(aPlaceInfo)) {
+    places = places.concat(aPlaceInfo);
+  } else {
+    places.push(aPlaceInfo)
+  }
+
+  // Create mozIVisitInfo for each entry.
+  let now = Date.now();
+  for (let i = 0; i < places.length; i++) {
+    if (!places[i].title) {
+      places[i].title = "test visit for " + places[i].uri.spec;
+    }
+    places[i].visits = [{
+      transitionType: places[i].transition === undefined ? TRANSITION_LINK
+                                                         : places[i].transition,
+      visitDate: places[i].visitDate || (now++) * 1000,
+      referrerURI: places[i].referrer
+    }];
+  }
+
+  PlacesUtils.asyncHistory.updatePlaces(
+    places,
+    {
+      handleError: function AAV_handleError(aResultCode, aPlaceInfo) {
+        let ex = new Components.Exception("Unexpected error in adding visits.",
+                                          aResultCode);
+        deferred.reject(ex);
+      },
+      handleResult: function () {},
+      handleCompletion: function UP_handleCompletion() {
+        deferred.resolve();
+      }
+    }
+  );
+
+  return deferred.promise;
+}
+
+/**
  * Checks that the favicon for the given page matches the provided data.
  *
  * @param aPageURI
  *        nsIURI object for the page to check.
  * @param aExpectedMimeType
  *        Expected MIME type of the icon, for example "image/png".
  * @param aExpectedData
  *        Expected icon data, expressed as an array of byte values.
@@ -367,8 +436,33 @@ function promiseIsURIVisited(aURI, aExpe
   let deferred = Promise.defer();
 
   PlacesUtils.asyncHistory.isURIVisited(aURI, function(aURI, aIsVisited) {
     deferred.resolve(aIsVisited);
   });
 
   return deferred.promise;
 }
+
+function waitForCondition(condition, nextTest, errorMsg) {
+  let tries = 0;
+  let interval = setInterval(function() {
+    if (tries >= 30) {
+      ok(false, errorMsg);
+      moveOn();
+    }
+    let conditionPassed;
+    try {
+      conditionPassed = condition();
+    } catch (e) {
+      ok(false, e + "\n" + e.stack);
+      conditionPassed = false;
+    }
+    if (conditionPassed) {
+      moveOn();
+    }
+    tries++;
+  }, 200);
+  function moveOn() {
+    clearInterval(interval);
+    nextTest();
+  };
+}