diff options
Diffstat (limited to 'dom/indexedDB/test/unit/xpcshell-head-parent-process.js')
-rw-r--r-- | dom/indexedDB/test/unit/xpcshell-head-parent-process.js | 738 |
1 files changed, 738 insertions, 0 deletions
diff --git a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js new file mode 100644 index 0000000000..6b7683969d --- /dev/null +++ b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js @@ -0,0 +1,738 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Tests using testGenerator are expected to define it themselves. +// Testing functions are expected to call testSteps and its type should either +// be GeneratorFunction or AsyncFunction +/* global testGenerator, testSteps:false */ + +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +if (!("self" in this)) { + this.self = this; +} + +var bufferCache = []; + +function is(a, b, msg) { + Assert.equal(a, b, msg); +} + +function ok(cond, msg) { + Assert.ok(!!cond, msg); +} + +function isnot(a, b, msg) { + Assert.notEqual(a, b, msg); +} + +function todo(condition, name, diag) { + todo_check_true(condition); +} + +function run_test() { + runTest(); +} + +if (!this.runTest) { + this.runTest = function () { + if (SpecialPowers.isMainProcess()) { + // XPCShell does not get a profile by default. + do_get_profile(); + + enableTesting(); + enableExperimental(); + } + + Cu.importGlobalProperties(["indexedDB"]); + + // In order to support converting tests to using async functions from using + // generator functions, we detect async functions by checking the name of + // function's constructor. + Assert.ok( + typeof testSteps === "function", + "There should be a testSteps function" + ); + if (testSteps.constructor.name === "AsyncFunction") { + // Do run our existing cleanup function that would normally be called by + // the generator's call to finishTest(). + registerCleanupFunction(resetTesting); + + add_task(testSteps); + + // Since we defined run_test, we must invoke run_next_test() to start the + // async test. + run_next_test(); + } else { + Assert.ok( + testSteps.constructor.name === "GeneratorFunction", + "Unsupported function type" + ); + + do_test_pending(); + testGenerator.next(); + } + }; +} + +function finishTest() { + if (SpecialPowers.isMainProcess()) { + resetExperimental(); + resetTesting(); + } + + SpecialPowers.removeFiles(); + + executeSoon(function () { + do_test_finished(); + }); +} + +function grabEventAndContinueHandler(event) { + testGenerator.next(event); +} + +function continueToNextStep() { + executeSoon(function () { + testGenerator.next(); + }); +} + +function errorHandler(event) { + try { + dump("indexedDB error: " + event.target.error.name); + } catch (e) { + dump("indexedDB error: " + e); + } + Assert.ok(false); + finishTest(); +} + +function unexpectedSuccessHandler() { + Assert.ok(false); + finishTest(); +} + +function expectedErrorHandler(name) { + return function (event) { + Assert.equal(event.type, "error"); + Assert.equal(event.target.error.name, name); + event.preventDefault(); + grabEventAndContinueHandler(event); + }; +} + +function expectUncaughtException(expecting) { + // This is dummy for xpcshell test. +} + +function ExpectError(name, preventDefault) { + this._name = name; + this._preventDefault = preventDefault; +} +ExpectError.prototype = { + handleEvent(event) { + Assert.equal(event.type, "error"); + Assert.equal(this._name, event.target.error.name); + if (this._preventDefault) { + event.preventDefault(); + event.stopPropagation(); + } + grabEventAndContinueHandler(event); + }, +}; + +function continueToNextStepSync() { + testGenerator.next(); +} + +// TODO compareKeys is duplicated in ../helpers.js, can we import that here? +// the same applies to many other functions in this file +// this duplication should be avoided (bug 1565986) +function compareKeys(k1, k2) { + let t = typeof k1; + if (t != typeof k2) { + return false; + } + + if (t !== "object") { + return k1 === k2; + } + + if (k1 instanceof Date) { + return k2 instanceof Date && k1.getTime() === k2.getTime(); + } + + if (k1 instanceof Array) { + if (!(k2 instanceof Array) || k1.length != k2.length) { + return false; + } + + for (let i = 0; i < k1.length; ++i) { + if (!compareKeys(k1[i], k2[i])) { + return false; + } + } + + return true; + } + + if (k1 instanceof ArrayBuffer) { + if (!(k2 instanceof ArrayBuffer)) { + return false; + } + + function arrayBuffersAreEqual(a, b) { + if (a.byteLength != b.byteLength) { + return false; + } + let ui8b = new Uint8Array(b); + return new Uint8Array(a).every((val, i) => val === ui8b[i]); + } + + return arrayBuffersAreEqual(k1, k2); + } + + return false; +} + +function addPermission(permission, url) { + throw new Error("addPermission"); +} + +function removePermission(permission, url) { + throw new Error("removePermission"); +} + +function allowIndexedDB(url) { + throw new Error("allowIndexedDB"); +} + +function disallowIndexedDB(url) { + throw new Error("disallowIndexedDB"); +} + +function enableExperimental() { + SpecialPowers.setBoolPref("dom.indexedDB.experimental", true); +} + +function resetExperimental() { + SpecialPowers.clearUserPref("dom.indexedDB.experimental"); +} + +function enableTesting() { + SpecialPowers.setBoolPref("dom.indexedDB.testing", true); +} + +function resetTesting() { + SpecialPowers.clearUserPref("dom.indexedDB.testing"); +} + +function gc() { + Cu.forceGC(); + Cu.forceCC(); +} + +function scheduleGC() { + SpecialPowers.exactGC(continueToNextStep); +} + +function setTimeout(fun, timeout) { + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + var event = { + notify(timer) { + fun(); + }, + }; + timer.initWithCallback(event, timeout, Ci.nsITimer.TYPE_ONE_SHOT); + return timer; +} + +function initStorage() { + return Services.qms.init(); +} + +function initPersistentOrigin(principal) { + return Services.qms.initializePersistentOrigin(principal); +} + +function resetOrClearAllDatabases(callback, clear) { + if (!SpecialPowers.isMainProcess()) { + throw new Error("clearAllDatabases not implemented for child processes!"); + } + + const quotaPref = "dom.quotaManager.testing"; + + let oldPrefValue; + if (Services.prefs.prefHasUserValue(quotaPref)) { + oldPrefValue = SpecialPowers.getBoolPref(quotaPref); + } + + SpecialPowers.setBoolPref(quotaPref, true); + + let request; + + try { + if (clear) { + request = Services.qms.clear(); + } else { + request = Services.qms.reset(); + } + } catch (e) { + if (oldPrefValue !== undefined) { + SpecialPowers.setBoolPref(quotaPref, oldPrefValue); + } else { + SpecialPowers.clearUserPref(quotaPref); + } + throw e; + } + + request.callback = callback; + + return request; +} + +function resetAllDatabases(callback) { + return resetOrClearAllDatabases(callback, false); +} + +function clearAllDatabases(callback) { + return resetOrClearAllDatabases(callback, true); +} + +function installPackagedProfile(packageName) { + let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile); + + let currentDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile); + + let packageFile = currentDir.clone(); + packageFile.append(packageName + ".zip"); + + let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance( + Ci.nsIZipReader + ); + zipReader.open(packageFile); + + let entryNames = []; + for (let entry of zipReader.findEntries(null)) { + if (entry != "create_db.html") { + entryNames.push(entry); + } + } + entryNames.sort(); + + for (let entryName of entryNames) { + let zipentry = zipReader.getEntry(entryName); + + let file = profileDir.clone(); + let split = entryName.split("/"); + for (let i = 0; i < split.length; i++) { + file.append(split[i]); + } + + if (zipentry.isDirectory) { + file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8)); + } else { + let istream = zipReader.getInputStream(entryName); + + var ostream = Cc[ + "@mozilla.org/network/file-output-stream;1" + ].createInstance(Ci.nsIFileOutputStream); + ostream.init(file, -1, parseInt("0644", 8), 0); + + let bostream = Cc[ + "@mozilla.org/network/buffered-output-stream;1" + ].createInstance(Ci.nsIBufferedOutputStream); + bostream.init(ostream, 32768); + + bostream.writeFrom(istream, istream.available()); + + istream.close(); + bostream.close(); + } + } + + zipReader.close(); +} + +function getChromeFilesDir() { + let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile); + + let idbDir = profileDir.clone(); + idbDir.append("storage"); + idbDir.append("permanent"); + idbDir.append("chrome"); + idbDir.append("idb"); + + let idbEntries = idbDir.directoryEntries; + while (idbEntries.hasMoreElements()) { + let file = idbEntries.nextFile; + if (file.isDirectory()) { + return file; + } + } + + throw new Error("files directory doesn't exist!"); +} + +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 }); +} + +function isWasmSupported() { + let testingFunctions = Cu.getJSTestingFunctions(); + return testingFunctions.wasmIsSupported(); +} + +function getWasmModule(binary) { + let module = new WebAssembly.Module(binary); + return module; +} + +function compareBuffers(buffer1, buffer2) { + if (buffer1.byteLength != buffer2.byteLength) { + return false; + } + + let view1 = buffer1 instanceof Uint8Array ? buffer1 : new Uint8Array(buffer1); + let view2 = buffer2 instanceof Uint8Array ? buffer2 : new Uint8Array(buffer2); + for (let i = 0; i < buffer1.byteLength; i++) { + if (view1[i] != view2[i]) { + return false; + } + } + return true; +} + +function verifyBuffers(buffer1, buffer2) { + ok(compareBuffers(buffer1, buffer2), "Correct buffer data"); +} + +function verifyBlob(blob1, blob2) { + is(Blob.isInstance(blob1), true, "Instance of nsIDOMBlob"); + is(File.isInstance(blob1), File.isInstance(blob2), "Instance of DOM File"); + is(blob1.size, blob2.size, "Correct size"); + is(blob1.type, blob2.type, "Correct type"); + if (File.isInstance(blob2)) { + is(blob1.name, blob2.name, "Correct name"); + } + + let buffer1; + let buffer2; + + for (let i = 0; i < bufferCache.length; i++) { + if (bufferCache[i].blob == blob2) { + buffer2 = bufferCache[i].buffer; + break; + } + } + + if (!buffer2) { + let reader = new FileReader(); + reader.readAsArrayBuffer(blob2); + reader.onload = function (event) { + buffer2 = event.target.result; + bufferCache.push({ blob: blob2, buffer: buffer2 }); + if (buffer1) { + verifyBuffers(buffer1, buffer2); + testGenerator.next(); + } + }; + } + + let reader = new FileReader(); + reader.readAsArrayBuffer(blob1); + reader.onload = function (event) { + buffer1 = event.target.result; + if (buffer2) { + verifyBuffers(buffer1, buffer2); + testGenerator.next(); + } + }; +} + +function verifyView(view1, view2) { + is(view1.byteLength, view2.byteLength, "Correct byteLength"); + verifyBuffers(view1, view2); + continueToNextStep(); +} + +function grabFileUsageAndContinueHandler(request) { + testGenerator.next(request.result.fileUsage); +} + +function getCurrentUsage(usageHandler) { + let principal = Cc["@mozilla.org/systemprincipal;1"].createInstance( + Ci.nsIPrincipal + ); + Services.qms.getUsageForPrincipal(principal, usageHandler); +} + +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 setDataThreshold(threshold) { + info("Setting data threshold to " + threshold); + SpecialPowers.setIntPref("dom.indexedDB.dataThreshold", threshold); +} + +function resetDataThreshold() { + info("Clearing data threshold pref"); + SpecialPowers.clearUserPref("dom.indexedDB.dataThreshold"); +} + +function setMaxSerializedMsgSize(aSize) { + info("Setting maximal size of a serialized message to " + aSize); + SpecialPowers.setIntPref("dom.indexedDB.maxSerializedMsgSize", aSize); +} + +function enablePreprocessing() { + info("Setting preprocessing pref"); + SpecialPowers.setBoolPref("dom.indexedDB.preprocessing", true); +} + +function resetPreprocessing() { + info("Clearing preprocessing pref"); + SpecialPowers.clearUserPref("dom.indexedDB.preprocessing"); +} + +function getSystemPrincipal() { + return Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal); +} + +function getPrincipal(url) { + let uri = Services.io.newURI(url); + return Services.scriptSecurityManager.createContentPrincipal(uri, {}); +} + +class RequestError extends Error { + constructor(resultCode, resultName) { + super(`Request failed (code: ${resultCode}, name: ${resultName})`); + this.name = "RequestError"; + this.resultCode = resultCode; + this.resultName = resultName; + } +} + +async function requestFinished(request) { + await new Promise(function (resolve) { + request.callback = function () { + resolve(); + }; + }); + + if (request.resultCode !== Cr.NS_OK) { + throw new RequestError(request.resultCode, request.resultName); + } + + return request.result; +} + +// TODO: Rename to openDBRequestSucceeded ? +function expectingSuccess(request) { + return new Promise(function (resolve, reject) { + request.onerror = function (event) { + ok(false, "indexedDB error, '" + event.target.error.name + "'"); + reject(event); + }; + request.onsuccess = function (event) { + resolve(event); + }; + request.onupgradeneeded = function (event) { + ok(false, "Got upgrade, but did not expect it!"); + reject(event); + }; + }); +} + +// TODO: Rename to openDBRequestUpgradeNeeded ? +function expectingUpgrade(request) { + return new Promise(function (resolve, reject) { + request.onerror = function (event) { + ok(false, "indexedDB error, '" + event.target.error.name + "'"); + reject(event); + }; + request.onupgradeneeded = function (event) { + resolve(event); + }; + request.onsuccess = function (event) { + ok(false, "Got success, but did not expect it!"); + reject(event); + }; + }); +} + +function requestSucceeded(request) { + return new Promise(function (resolve, reject) { + request.onerror = function (event) { + ok(false, "indexedDB error, '" + event.target.error.name + "'"); + reject(event); + }; + request.onsuccess = function (event) { + resolve(event); + }; + }); +} + +// Given a "/"-delimited path relative to the profile directory, +// return an nsIFile representing the path. This does not test +// for the existence of the file or parent directories. +// It is safe even on Windows where the directory separator is not "/", +// but make sure you're not passing in a "\"-delimited path. +function getRelativeFile(relativePath) { + let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile); + + let file = profileDir.clone(); + relativePath.split("/").forEach(function (component) { + file.append(component); + }); + + return file; +} + +var SpecialPowers = { + isMainProcess() { + return ( + Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT + ); + }, + notifyObservers(subject, topic, data) { + Services.obs.notifyObservers(subject, topic, data); + }, + notifyObserversInParentProcess(subject, topic, data) { + if (subject) { + throw new Error("Can't send subject to another process!"); + } + return this.notifyObservers(subject, topic, data); + }, + getBoolPref(prefName) { + return Services.prefs.getBoolPref(prefName); + }, + setBoolPref(prefName, value) { + Services.prefs.setBoolPref(prefName, value); + }, + setIntPref(prefName, value) { + Services.prefs.setIntPref(prefName, value); + }, + clearUserPref(prefName) { + Services.prefs.clearUserPref(prefName); + }, + // Copied (and slightly adjusted) from testing/specialpowers/content/SpecialPowersAPI.jsm + exactGC(callback) { + let count = 0; + + function doPreciseGCandCC() { + function scheduledGCCallback() { + Cu.forceCC(); + + if (++count < 3) { + doPreciseGCandCC(); + } else { + callback(); + } + } + + Cu.schedulePreciseGC(scheduledGCCallback); + } + + doPreciseGCandCC(); + }, + + get Cc() { + return Cc; + }, + + get Ci() { + return Ci; + }, + + get Cu() { + return Cu; + }, + + // Based on SpecialPowersObserver.prototype.receiveMessage + createFiles(requests, callback) { + let filePaths = []; + if (!this._createdFiles) { + this._createdFiles = []; + } + let createdFiles = this._createdFiles; + let promises = []; + requests.forEach(function (request) { + const filePerms = 0o666; + let testFile = Services.dirsvc.get("ProfD", Ci.nsIFile); + if (request.name) { + testFile.append(request.name); + } else { + testFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, filePerms); + } + let outStream = Cc[ + "@mozilla.org/network/file-output-stream;1" + ].createInstance(Ci.nsIFileOutputStream); + outStream.init( + testFile, + 0x02 | 0x08 | 0x20, // PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE + filePerms, + 0 + ); + if (request.data) { + outStream.write(request.data, request.data.length); + outStream.close(); + } + promises.push( + File.createFromFileName(testFile.path, request.options).then(function ( + file + ) { + filePaths.push(file); + }) + ); + createdFiles.push(testFile); + }); + + Promise.all(promises).then(function () { + setTimeout(function () { + callback(filePaths); + }, 0); + }); + }, + + removeFiles() { + if (this._createdFiles) { + this._createdFiles.forEach(function (testFile) { + try { + testFile.remove(false); + } catch (e) {} + }); + this._createdFiles = null; + } + }, +}; |