Bug 1252998 - Fix sanitize-offlineData test failures, move SW utility functions to SiteDataTestUtils.jsm. r=baku
authorJohann Hofmann <jhofmann@mozilla.com>
Wed, 11 Apr 2018 16:53:50 +0200
changeset 467881 db8bf70e7847af6bcfe5a5829ec894f0ea61abe8
parent 467880 38653c75863b9727c86621523d92908aefe09c11
child 467882 0a880af671625c7a212d30e3158a6c2a9270ce4a
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1252998
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1252998 - Fix sanitize-offlineData test failures, move SW utility functions to SiteDataTestUtils.jsm. r=baku This patch fixes a bunch of intermittent/perma failures in sanitize-offlineData.js by: - Ignoring localStorage for now. LocalStorage is cleared by sending an observer notification. The flush often happens after several seconds, heavily interfering with our own test or with subsequent tests. We can not reliably wait on the operation to finish. Waiting for "domstorage-test-flushed" after calling Sanitizer.sanitize() fixes the problem, but that notification is intermittently not triggered for other unknown reasons, which makes it not really viable to use. - Creating and checking indexedDB data in the chrome process (using SiteDataTestUtils). - Cleaning up after running the test. - Ignoring a stray NS_ERROR_ABORT that's hard to track down and doesn't seem to do any damage right now. I've also moved the ServiceWorker utility functions into SiteDataTestUtils, which we're planning to use in all other browser tests that handle site data.
browser/base/content/test/sanitize/SiteDataTestUtils.jsm
browser/base/content/test/sanitize/browser.ini
browser/base/content/test/sanitize/browser_sanitize-offlineData.js
browser/base/content/test/sanitize/sanitize.html
--- a/browser/base/content/test/sanitize/SiteDataTestUtils.jsm
+++ b/browser/base/content/test/sanitize/SiteDataTestUtils.jsm
@@ -1,66 +1,121 @@
 "use strict";
 
 var EXPORTED_SYMBOLS = [
   "SiteDataTestUtils",
 ];
 
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://testing-common/ContentTask.jsm");
+ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
+
 const {Sanitizer} = ChromeUtils.import("resource:///modules/Sanitizer.jsm", {});
 
+XPCOMUtils.defineLazyServiceGetter(this, "swm",
+                                   "@mozilla.org/serviceworkers/manager;1",
+                                   "nsIServiceWorkerManager");
+
 /**
  * This module assists with tasks around testing functionality that shows
  * or clears site data.
  *
  * Please note that you will have to clean up changes made manually, for
  * example using SiteDataTestUtils.clear().
  */
 var SiteDataTestUtils = {
 
   /**
    * Adds a new entry to a dummy indexedDB database for the specified origin.
    *
    * @param {String} origin - the origin of the site to add test data for
+   * @param {String} name [optional] - the entry key
+   * @param {String} value [optional] - the entry value
    *
    * @returns a Promise that resolves when the data was added successfully.
    */
-  addToIndexedDB(origin) {
+  addToIndexedDB(origin, key = "foo", value = "bar") {
     return new Promise(resolve => {
       let uri = Services.io.newURI(origin);
       let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
       let request = indexedDB.openForPrincipal(principal, "TestDatabase", 1);
       request.onupgradeneeded = function(e) {
         let db = e.target.result;
         db.createObjectStore("TestStore", { keyPath: "id" });
       };
       request.onsuccess = function(e) {
         let db = e.target.result;
         let tx = db.transaction("TestStore", "readwrite");
         let store = tx.objectStore("TestStore");
         tx.oncomplete = resolve;
-        store.put({ id: performance.now().toString(), description: "IndexedDB Test"});
+        store.put({ id: key, description: value});
       };
     });
   },
 
   /**
    * Adds a new cookie for the specified origin, with the specified contents.
    * The cookie will be valid for one day.
    *
-   * @param {String} name - the cookie name
-   * @param {String} value - the cookie value
+   * @param {String} origin - the origin of the site to add test data for
+   * @param {String} name [optional] - the cookie name
+   * @param {String} value [optional] - the cookie value
    */
-  addToCookies(origin, name, value) {
+  addToCookies(origin, name = "foo", value = "bar") {
     let uri = Services.io.newURI(origin);
     Services.cookies.add(uri.host, uri.pathQueryRef, name, value,
       false, false, false, Date.now() + 24000 * 60 * 60);
   },
 
   /**
+   * Adds a new serviceworker with the specified path. Note that this
+   * method will open a new tab at the domain of the SW path to that effect.
+   *
+   * @param {String} path - the path to the service worker to add.
+   *
+   * @returns a Promise that resolves when the service worker was registered
+   */
+  addServiceWorker(path) {
+    let uri = Services.io.newURI(path);
+    // Register a dummy ServiceWorker.
+    return BrowserTestUtils.withNewTab(uri.prePath, async function(browser) {
+      return ContentTask.spawn(browser, {path}, async ({path: p}) => {
+        let r = await content.navigator.serviceWorker.register(p);
+        return new Promise(resolve => {
+          let worker = r.installing;
+          worker.addEventListener("statechange", () => {
+            if (worker.state === "installed") {
+              resolve();
+            }
+          });
+        });
+      });
+    });
+  },
+
+  /**
+   * Checks whether the specified origin has registered ServiceWorkers.
+   *
+   * @param {String} origin - the origin of the site to check
+   *
+   * @returns {Boolean} whether or not the site has ServiceWorkers.
+   */
+  hasServiceWorkers(origin) {
+    let serviceWorkers = swm.getAllRegistrations();
+    for (let i = 0; i < serviceWorkers.length; i++) {
+      let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
+      if (sw.principal.origin == origin) {
+        return true;
+      }
+    }
+    return false;
+  },
+
+  /**
    * Gets the current quota usage for the specified origin.
    *
    * @returns a Promise that resolves to an integer with the total
    *          amount of disk usage by a given origin.
    */
   getQuotaUsage(origin) {
     return new Promise(resolve => {
       let uri = Services.io.newURI(origin);
--- a/browser/base/content/test/sanitize/browser.ini
+++ b/browser/base/content/test/sanitize/browser.ini
@@ -1,14 +1,13 @@
 [DEFAULT]
 support-files=
   head.js
   dummy.js
   dummy_page.html
-  sanitize.html
 
 [browser_purgehistory_clears_sh.js]
 [browser_sanitize-formhistory.js]
 [browser_sanitize-offlineData.js]
 [browser_sanitize-passwordDisabledHosts.js]
 [browser_sanitize-sitepermissions.js]
 [browser_sanitize-timespans.js]
 [browser_sanitizeDialog.js]
--- a/browser/base/content/test/sanitize/browser_sanitize-offlineData.js
+++ b/browser/base/content/test/sanitize/browser_sanitize-offlineData.js
@@ -1,28 +1,42 @@
 // Bug 380852 - Delete permission manager entries in Clear Recent History
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 const {Sanitizer} = ChromeUtils.import("resource:///modules/Sanitizer.jsm", {});
+const {SiteDataTestUtils} = ChromeUtils.import("resource://testing-common/SiteDataTestUtils.jsm", {});
+const {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm", {});
 
 XPCOMUtils.defineLazyServiceGetter(this, "sas",
                                    "@mozilla.org/storage/activity-service;1",
                                    "nsIStorageActivityService");
 XPCOMUtils.defineLazyServiceGetter(this, "swm",
                                    "@mozilla.org/serviceworkers/manager;1",
                                    "nsIServiceWorkerManager");
-XPCOMUtils.defineLazyServiceGetter(this, "quotaManagerService",
-                                   "@mozilla.org/dom/quota-manager-service;1",
-                                   "nsIQuotaManagerService");
 
 const oneHour = 3600000000;
 const fiveHours = oneHour * 5;
 
 const itemsToClear = [ "cookies", "offlineApps" ];
 
+function hasIndexedDB(origin) {
+  return new Promise(resolve => {
+    let hasData = true;
+    let uri = Services.io.newURI(origin);
+    let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
+    let request = indexedDB.openForPrincipal(principal, "TestDatabase", 1);
+    request.onupgradeneeded = function(e) {
+      hasData = false;
+    };
+    request.onsuccess = function(e) {
+      resolve(hasData);
+    };
+  });
+}
+
 function waitForUnregister(host) {
   return new Promise(resolve => {
     let listener = {
       onUnregister: registration => {
         if (registration.principal.URI.host != host) {
           return;
         }
         let swm = Cc["@mozilla.org/serviceworkers/manager;1"]
@@ -31,81 +45,40 @@ function waitForUnregister(host) {
         resolve(registration);
       }
     };
     swm.addListener(listener);
   });
 }
 
 async function createData(host) {
-  let pageURL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://" + host) + "sanitize.html";
+  let origin = "https://" + host;
+  let dummySWURL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) + "dummy.js";
 
-  return BrowserTestUtils.withNewTab(pageURL, async function(browser) {
-    await ContentTask.spawn(browser, null, () => {
-      return new content.window.Promise(resolve => {
-        let id = content.window.setInterval(() => {
-          if ("foobar" in content.window.localStorage) {
-            content.window.clearInterval(id);
-            resolve(true);
-          }
-        }, 1000);
-      });
-    });
-  });
+  await SiteDataTestUtils.addToIndexedDB(origin);
+  await SiteDataTestUtils.addServiceWorker(dummySWURL);
 }
 
 function moveOriginInTime(principals, endDate, host) {
   for (let i = 0; i < principals.length; ++i) {
     let principal = principals.queryElementAt(i, Ci.nsIPrincipal);
     if (principal.URI.host == host) {
       sas.moveOriginInTime(principal, endDate - fiveHours);
       return true;
     }
   }
   return false;
 }
 
-async function getData(host) {
-  let dummyURL = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://" + host) + "dummy_page.html";
-
-  // LocalStorage + IndexedDB
-  let data = await BrowserTestUtils.withNewTab(dummyURL, async function(browser) {
-    return ContentTask.spawn(browser, null, () => {
-      return new content.window.Promise(resolve => {
-        let obj = {
-          localStorage: "foobar" in content.window.localStorage,
-          indexedDB: true,
-          serviceWorker: false,
-        };
+add_task(async function testWithRange() {
+  // We have intermittent occurrences of NS_ERROR_ABORT being
+  // thrown at closing database instances when using Santizer.sanitize().
+  // This does not seem to impact cleanup, since our tests run fine anyway.
+  PromiseTestUtils.whitelistRejectionsGlobally(/NS_ERROR_ABORT/);
 
-        let request = content.window.indexedDB.open("sanitizer_test", 1);
-        request.onupgradeneeded = event => {
-          obj.indexedDB = false;
-        };
-        request.onsuccess = event => {
-          resolve(obj);
-        };
-      });
-    });
-  });
-
-  // ServiceWorkers
-  let serviceWorkers = swm.getAllRegistrations();
-  for (let i = 0; i < serviceWorkers.length; i++) {
-    let sw = serviceWorkers.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
-    if (sw.principal.URI.host == host) {
-      data.serviceWorker = true;
-      break;
-    }
-  }
-
-  return data;
-}
-
-add_task(async function testWithRange() {
   await SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]});
 
   // The service may have picked up activity from prior tests in this run.
   // Clear it.
@@ -132,58 +105,61 @@ add_task(async function testWithRange() 
     if (principal.URI.host == "example.org" ||
         principal.URI.host == "example.com") {
       found++;
     }
   }
 
   is(found, 2, "Our origins are active.");
 
-  let dataPre = await getData("example.org");
-  ok(dataPre.localStorage, "We have localStorage data");
-  ok(dataPre.indexedDB, "We have indexedDB data");
-  ok(dataPre.serviceWorker, "We have serviceWorker data");
+  ok(await hasIndexedDB("https://example.org"),
+    "We have indexedDB data for example.org");
+  ok(SiteDataTestUtils.hasServiceWorkers("https://example.org"),
+    "We have serviceWorker data for example.org");
 
-  dataPre = await getData("example.com");
-  ok(dataPre.localStorage, "We have localStorage data");
-  ok(dataPre.indexedDB, "We have indexedDB data");
-  ok(dataPre.serviceWorker, "We have serviceWorker data");
+  ok(await hasIndexedDB("https://example.com"),
+    "We have indexedDB data for example.com");
+  ok(SiteDataTestUtils.hasServiceWorkers("https://example.com"),
+    "We have serviceWorker data for example.com");
 
   // Let's move example.com in the past.
   ok(moveOriginInTime(principals, endDate, "example.com"), "Operation completed!");
 
   let p = waitForUnregister("example.org");
 
   // Clear it
   info("sanitize: " + itemsToClear.join(", "));
   await Sanitizer.sanitize(itemsToClear, {ignoreTimespan: false});
   await p;
 
-  let dataPost = await getData("example.org");
-  ok(!dataPost.localStorage, "We don't have localStorage data");
-  ok(!dataPost.indexedDB, "We don't have indexedDB data");
-  ok(!dataPost.serviceWorker, "We don't have serviceWorker data");
+  ok(!(await hasIndexedDB("https://example.org")),
+    "We don't have indexedDB data for example.org");
+  ok(!SiteDataTestUtils.hasServiceWorkers("https://example.org"),
+    "We don't have serviceWorker data for example.org");
 
-  dataPost = await getData("example.com");
-  ok(dataPost.localStorage, "We still have localStorage data");
-  ok(dataPost.indexedDB, "We still have indexedDB data");
-  ok(dataPost.serviceWorker, "We still have serviceWorker data");
+  ok(await hasIndexedDB("https://example.com"),
+    "We still have indexedDB data for example.com");
+  ok(SiteDataTestUtils.hasServiceWorkers("https://example.com"),
+    "We still have serviceWorker data for example.com");
 
   // We have to move example.com in the past because how we check IDB triggers
   // a storage activity.
   ok(moveOriginInTime(principals, endDate, "example.com"), "Operation completed!");
 
   // Let's call the clean up again.
   info("sanitize again to ensure clearing doesn't expand the activity scope");
   await Sanitizer.sanitize(itemsToClear, {ignoreTimespan: false});
 
-  dataPost = await getData("example.com");
-  ok(dataPost.localStorage, "We still have localStorage data");
-  ok(dataPost.indexedDB, "We still have indexedDB data");
-  ok(dataPost.serviceWorker, "We still have serviceWorker data");
+  ok(await hasIndexedDB("https://example.com"),
+    "We still have indexedDB data for example.com");
+  ok(SiteDataTestUtils.hasServiceWorkers("https://example.com"),
+    "We still have serviceWorker data for example.com");
 
-  dataPost = await getData("example.org");
-  ok(!dataPost.localStorage, "We don't have localStorage data");
-  ok(!dataPost.indexedDB, "We don't have indexedDB data");
-  ok(!dataPost.serviceWorker, "We don't have serviceWorker data");
+  ok(!(await hasIndexedDB("https://example.org")),
+    "We don't have indexedDB data for example.org");
+  ok(!SiteDataTestUtils.hasServiceWorkers("https://example.org"),
+    "We don't have serviceWorker data for example.org");
 
   sas.testOnlyReset();
+
+  // Clean up.
+  await Sanitizer.sanitize(itemsToClear);
 });
deleted file mode 100644
--- a/browser/base/content/test/sanitize/sanitize.html
+++ /dev/null
@@ -1,41 +0,0 @@
-<html>
-<head>
-  <meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
-</head>
-<body>
-  <script>
-
-// indexedDB
-let p = new Promise(resolve => {
-  let request = indexedDB.open("sanitizer_test", 1);
-  request.onupgradeneeded = event => {
-    let db = event.target.result;
-    event.target.onsuccess = resolve;
-    db.createObjectStore("foo", { autoIncrement: true });
-    db.createObjectStore("bar", { autoIncrement: true });
-  };
-});
-
-// ServiceWorker
-p.then(() => {
-  return navigator.serviceWorker.register("dummy.js")
-                .then(r => {
-    return new Promise(resolve => {
-      let worker = r.installing;
-      worker.addEventListener("statechange", () => {
-        if (worker.state === "installed") {
-          resolve(true);
-        }
-      });
-    });
-  });
-})
-
-// localStorage
-.then(() => {
-  localStorage.foobar = "hello world!";
-});
-
-  </script>
-</body>
-</html>