Bug 1182987 - Part 4: Add a test for QuotaExceededError recovery and the new "cleanup" transaction type; r=baku
authorJan Varga <jan.varga@gmail.com>
Tue, 15 Mar 2016 07:00:44 +0100
changeset 288739 27762ee249eb7d4c18c7e0f58a765a43b8496ed6
parent 288738 72f31b1f6186b228835d2f4e36c3b96c94674743
child 288740 b22ef40863a05144cf013de13d2fc1e2959cee8a
push id30089
push userkwierso@gmail.com
push dateWed, 16 Mar 2016 00:26:08 +0000
treeherdermozilla-central@7773387a9a2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1182987
milestone48.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 1182987 - Part 4: Add a test for QuotaExceededError recovery and the new "cleanup" transaction type; r=baku
dom/indexedDB/test/unit/test_quotaExceeded_recovery.js
dom/indexedDB/test/unit/test_temporary_storage.js
dom/indexedDB/test/unit/xpcshell-head-parent-process.js
dom/indexedDB/test/unit/xpcshell-parent-process.ini
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_quotaExceeded_recovery.js
@@ -0,0 +1,153 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var disableWorkerTest = "Need a way to set temporary prefs from a worker";
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const spec = "http://foo.com";
+  const name =
+    this.window ? window.location.pathname : "test_quotaExceeded_recovery";
+  const objectStoreName = "foo";
+
+  // We want 32 MB database file, but there's the group limit so we need to
+  // multiply by 5.
+  const tempStorageLimitKB = 32 * 1024 * 5;
+
+  // Store in 1 MB chunks.
+  const dataSize = 1024 * 1024;
+
+  for (let blobs of [false, true]) {
+    setTemporaryStorageLimit(tempStorageLimitKB);
+
+    clearAllDatabases(continueToNextStepSync);
+    yield undefined;
+
+    info("Opening database");
+
+    let request = indexedDB.openForPrincipal(getPrincipal(spec), name);
+    request.onerror = errorHandler;
+    request.onupgradeneeded = grabEventAndContinueHandler;;
+    request.onsuccess = unexpectedSuccessHandler;
+
+    yield undefined;
+
+    // upgradeneeded
+    request.onupgradeneeded = unexpectedSuccessHandler;
+    request.onsuccess = grabEventAndContinueHandler;
+
+    info("Creating objectStore");
+
+    request.result.createObjectStore(objectStoreName);
+
+    yield undefined;
+
+    // success
+    let db = request.result;
+    db.onerror = errorHandler;
+
+    ok(true, "Adding data until quota is reached");
+
+    let obj = {
+      name: "foo"
+    }
+
+    if (!blobs) {
+      obj.data = getRandomView(dataSize);
+    }
+
+    let i = 1;
+    let j = 1;
+    while (true) {
+      if (blobs) {
+        obj.data = getBlob(getView(dataSize));
+      }
+
+      let trans = db.transaction(objectStoreName, "readwrite");
+      request = trans.objectStore(objectStoreName).add(obj, i);
+      request.onerror = function(event)
+      {
+        event.stopPropagation();
+      }
+
+      trans.oncomplete = function(event) {
+        i++;
+        j++;
+        testGenerator.send(true);
+      }
+      trans.onabort = function(event) {
+        is(trans.error.name, "QuotaExceededError", "Reached quota limit");
+        testGenerator.send(false);
+      }
+
+      let shouldContinue = yield undefined;
+      if (shouldContinue) {
+        ok(true, "Got complete event");
+      } else {
+        ok(true, "Got abort event");
+
+        if (j==1) {
+          break;
+        } else {
+          j = 1;
+
+          trans = db.transaction(objectStoreName, "cleanup");
+          trans.onabort = unexpectedSuccessHandler;;
+          trans.oncomplete = grabEventAndContinueHandler;
+
+          yield undefined;
+        }
+      }
+    }
+
+    info("Reopening database");
+
+    db.close();
+
+    request = indexedDB.openForPrincipal(getPrincipal(spec), name);
+    request.onerror = errorHandler;
+    request.onsuccess = grabEventAndContinueHandler;
+
+    yield undefined;
+
+    db = request.result;
+    db.onerror = errorHandler;
+
+    info("Deleting some data")
+
+    let trans = db.transaction(objectStoreName, "cleanup");
+    trans.objectStore(objectStoreName).delete(1);
+
+    trans.onabort = unexpectedSuccessHandler;;
+    trans.oncomplete = grabEventAndContinueHandler;
+
+    yield undefined;
+
+    info("Adding data again")
+
+    trans = db.transaction(objectStoreName, "readwrite");
+    trans.objectStore(objectStoreName).add(obj, 1);
+
+    trans.onabort = unexpectedSuccessHandler;
+    trans.oncomplete = grabEventAndContinueHandler;
+
+    yield undefined;
+
+    info("Deleting database");
+
+    db.close();
+
+    request = indexedDB.deleteForPrincipal(getPrincipal(spec), name);
+    request.onerror = errorHandler;
+    request.onsuccess = grabEventAndContinueHandler;
+
+    yield undefined;
+  }
+
+  finishTest();
+  yield undefined;
+}
--- a/dom/indexedDB/test/unit/test_temporary_storage.js
+++ b/dom/indexedDB/test/unit/test_temporary_storage.js
@@ -10,44 +10,24 @@ function testSteps()
   const name = this.window ?
                window.location.pathname :
                "test_temporary_storage.js";
   const finalVersion = 2;
 
   const tempStorageLimitKB = 1024;
   const checkpointSleepTimeSec = 5;
 
-  function setLimit(limit) {
-    const pref = "dom.quotaManager.temporaryStorage.fixedLimit";
-    if (limit) {
-      info("Setting temporary storage limit to " + limit);
-      SpecialPowers.setIntPref(pref, limit);
-    } else {
-      info("Removing temporary storage limit");
-      SpecialPowers.clearUserPref(pref);
-    }
-  }
-
   function getSpec(index) {
     return "http://foo" + index + ".com";
   }
 
-  function getPrincipal(url) {
-    let uri = Cc["@mozilla.org/network/io-service;1"]
-                .getService(Ci.nsIIOService)
-                .newURI(url, null, null);
-    let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
-                .getService(Ci.nsIScriptSecurityManager);
-    return ssm.createCodebasePrincipal(uri, {});
-  }
-
   for (let temporary of [true, false]) {
     info("Testing '" + (temporary ? "temporary" : "default") + "' storage");
 
-    setLimit(tempStorageLimitKB);
+    setTemporaryStorageLimit(tempStorageLimitKB);
 
     clearAllDatabases(continueToNextStepSync);
     yield undefined;
 
     info("Stage 1 - Creating empty databases until we reach the quota limit");
 
     let databases = [];
     let options = { version: finalVersion };
@@ -160,17 +140,17 @@ function testSteps()
 
     is(event.type, "success", "Got success event");
 
     let db = event.target.result;
     is(db.version, finalVersion, "Correct version " + finalVersion);
     db.close();
     db = null;
 
-    setLimit(tempStorageLimitKB * 2);
+    setTemporaryStorageLimit(tempStorageLimitKB * 2);
 
     resetAllDatabases(continueToNextStepSync);
     yield undefined;
 
     delete options.version;
 
     spec = getSpec(0);
     info("Opening database for " + spec + " with unspecified version");
@@ -192,17 +172,17 @@ function testSteps()
     db = event.target.result;
     is(db.version, 1, "Correct version 1 (database was recreated)");
     db.close();
     db = null;
 
     info("Stage 3 - " +
          "Cutting storage limit in half to force deletion of some databases");
 
-    setLimit(tempStorageLimitKB / 2);
+    setTemporaryStorageLimit(tempStorageLimitKB / 2);
 
     resetAllDatabases(continueToNextStepSync);
     yield undefined;
 
     info("Opening database for " + spec + " with unspecified version");
 
     // Open the same db again to force QM to delete others. The first origin (0)
     // should be the most recent so it should not be deleted and we should not
@@ -215,17 +195,17 @@ function testSteps()
 
     is(event.type, "success", "Got correct event type");
 
     db = event.target.result;
     is(db.version, 1, "Correct version 1");
     db.close();
     db = null;
 
-    setLimit(tempStorageLimitKB * 2);
+    setTemporaryStorageLimit(tempStorageLimitKB * 2);
 
     resetAllDatabases(continueToNextStepSync);
     yield undefined;
 
     options.version = finalVersion;
 
     let newDatabaseCount = 0;
     for (let i = 0; i < databaseCount; i++) {
--- a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js
+++ b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js
@@ -326,16 +326,33 @@ function installPackagedProfile(packageN
       istream.close();
       bostream.close();
     }
   }
 
   zipReader.close();
 }
 
+function getView(size)
+{
+  let buffer = new ArrayBuffer(size);
+  let view = new Uint8Array(buffer);
+  is(buffer.byteLength, size, "Correct byte length");
+  return view;
+}
+
+function getRandomView(size)
+{
+  let view = getView(size);
+  for (let i = 0; i < size; i++) {
+    view[i] = parseInt(Math.random() * 255)
+  }
+  return view;
+}
+
 function getBlob(str)
 {
   return new Blob([str], {type: "type/text"});
 }
 
 function getFile(name, type, str)
 {
   return new File([str], name, {type: type});
@@ -413,16 +430,38 @@ function verifyMutableFile(mutableFile1,
      "Instance of IDBMutableFile");
   is(mutableFile1.name, file2.name, "Correct name");
   is(mutableFile1.type, file2.type, "Correct type");
   executeSoon(function() {
     testGenerator.next();
   });
 }
 
+function setTemporaryStorageLimit(limit)
+{
+  const pref = "dom.quotaManager.temporaryStorage.fixedLimit";
+  if (limit) {
+    info("Setting temporary storage limit to " + limit);
+    SpecialPowers.setIntPref(pref, limit);
+  } else {
+    info("Removing temporary storage limit");
+    SpecialPowers.clearUserPref(pref);
+  }
+}
+
+function getPrincipal(url)
+{
+  let uri = Cc["@mozilla.org/network/io-service;1"]
+              .getService(Ci.nsIIOService)
+              .newURI(url, null, null);
+  let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
+              .getService(Ci.nsIScriptSecurityManager);
+  return ssm.createCodebasePrincipal(uri, {});
+}
+
 var SpecialPowers = {
   isMainProcess: function() {
     return Components.classes["@mozilla.org/xre/app-info;1"]
                      .getService(Components.interfaces.nsIXULRuntime)
                      .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
   },
   notifyObservers: function(subject, topic, data) {
     var obsvc = Cc['@mozilla.org/observer-service;1']
--- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini
+++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini
@@ -30,14 +30,15 @@ support-files =
 skip-if = toolkit == 'android'
 [test_idle_maintenance.js]
 [test_invalidate.js]
 # disabled for the moment.
 skip-if = true
 [test_lowDiskSpace.js]
 [test_metadataRestore.js]
 [test_mutableFileUpgrade.js]
+[test_quotaExceeded_recovery.js]
 [test_readwriteflush_disabled.js]
 [test_schema18upgrade.js]
 [test_schema21upgrade.js]
 [test_temporary_storage.js]
 # bug 951017: intermittent failure on Android x86 emulator
 skip-if = os == "android" && processor == "x86"