diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/cache/test/xpcshell | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | dom/cache/test/xpcshell/bug1425146_profile.zip | bin | 0 -> 2834 bytes | |||
-rw-r--r-- | dom/cache/test/xpcshell/head.js | 208 | ||||
-rw-r--r-- | dom/cache/test/xpcshell/make_profile.js | 137 | ||||
-rw-r--r-- | dom/cache/test/xpcshell/schema_15_profile.zip | bin | 0 -> 3111 bytes | |||
-rw-r--r-- | dom/cache/test/xpcshell/schema_25_profile.zip | bin | 0 -> 60710 bytes | |||
-rw-r--r-- | dom/cache/test/xpcshell/test_bug1425146.js | 51 | ||||
-rw-r--r-- | dom/cache/test/xpcshell/test_empty_directories.js | 85 | ||||
-rw-r--r-- | dom/cache/test/xpcshell/test_migration.js | 65 | ||||
-rw-r--r-- | dom/cache/test/xpcshell/test_originInit.js | 249 | ||||
-rw-r--r-- | dom/cache/test/xpcshell/test_padding_error_handle.js | 72 | ||||
-rw-r--r-- | dom/cache/test/xpcshell/test_schema_26_upgrade.js | 18 | ||||
-rw-r--r-- | dom/cache/test/xpcshell/xpcshell.ini | 21 |
12 files changed, 906 insertions, 0 deletions
diff --git a/dom/cache/test/xpcshell/bug1425146_profile.zip b/dom/cache/test/xpcshell/bug1425146_profile.zip Binary files differnew file mode 100644 index 0000000000..fc57bc1043 --- /dev/null +++ b/dom/cache/test/xpcshell/bug1425146_profile.zip diff --git a/dom/cache/test/xpcshell/head.js b/dom/cache/test/xpcshell/head.js new file mode 100644 index 0000000000..58717f0f68 --- /dev/null +++ b/dom/cache/test/xpcshell/head.js @@ -0,0 +1,208 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * All images in schema_15_profile.zip are from https://github.com/mdn/sw-test/ + * and are CC licensed by https://www.flickr.com/photos/legofenris/. + */ + +// testSteps is expected to be defined by the file including this file. +/* global testSteps */ + +const NS_APP_USER_PROFILE_50_DIR = "ProfD"; +const osWindowsName = "WINNT"; +const pathDelimiter = "/"; + +const persistentPersistence = "persistent"; +const defaultPersistence = "default"; + +const storageDirName = "storage"; +const persistentPersistenceDirName = "permanent"; +const defaultPersistenceDirName = "default"; + +function cacheClientDirName() { + return "cache"; +} + +// services required be initialized in order to run CacheStorage +var ss = Cc["@mozilla.org/storage/service;1"].createInstance( + Ci.mozIStorageService +); +var sts = Cc["@mozilla.org/network/stream-transport-service;1"].getService( + Ci.nsIStreamTransportService +); +var hash = Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash); + +class RequestError extends Error { + constructor(resultCode, resultName) { + super(`Request failed (code: ${resultCode}, name: ${resultName})`); + this.name = "RequestError"; + this.resultCode = resultCode; + this.resultName = resultName; + } +} + +add_setup(function () { + do_get_profile(); + + enableTesting(); + + // Expose Cache and Fetch symbols on the global + Cu.importGlobalProperties(["caches", "fetch"]); + + registerCleanupFunction(resetTesting); +}); + +function enableTesting() { + Services.prefs.setBoolPref("dom.simpleDB.enabled", true); + Services.prefs.setBoolPref("dom.quotaManager.testing", true); +} + +function resetTesting() { + Services.prefs.clearUserPref("dom.quotaManager.testing"); + Services.prefs.clearUserPref("dom.simpleDB.enabled"); +} + +function initStorage() { + return Services.qms.init(); +} + +function initTemporaryStorage() { + return Services.qms.initTemporaryStorage(); +} + +function initPersistentOrigin(principal) { + return Services.qms.initializePersistentOrigin(principal); +} + +function initTemporaryOrigin(principal) { + return Services.qms.initializeTemporaryOrigin("default", principal); +} + +function clearOrigin(principal, persistence) { + let request = Services.qms.clearStoragesForPrincipal(principal, persistence); + + return request; +} + +function reset() { + return Services.qms.reset(); +} + +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; +} + +// Extract a zip file into the profile +function create_test_profile(zipFileName) { + var directoryService = Services.dirsvc; + + var profileDir = directoryService.get(NS_APP_USER_PROFILE_50_DIR, Ci.nsIFile); + var currentDir = directoryService.get("CurWorkD", Ci.nsIFile); + + var packageFile = currentDir.clone(); + packageFile.append(zipFileName); + + var zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance( + Ci.nsIZipReader + ); + zipReader.open(packageFile); + + var entryNames = Array.from(zipReader.findEntries(null)); + entryNames.sort(); + + for (var entryName of entryNames) { + var zipentry = zipReader.getEntry(entryName); + + var file = profileDir.clone(); + entryName.split(pathDelimiter).forEach(function (part) { + file.append(part); + }); + + if (zipentry.isDirectory) { + file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8)); + } else { + var 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); + + var bostream = Cc[ + "@mozilla.org/network/buffered-output-stream;1" + ].createInstance(Ci.nsIBufferedOutputStream); + bostream.init(ostream, 32 * 1024); + + bostream.writeFrom(istream, istream.available()); + + istream.close(); + bostream.close(); + } + } + + zipReader.close(); +} + +function getCacheDir() { + return getRelativeFile( + `${storageDirName}/${defaultPersistenceDirName}/chrome/${cacheClientDirName()}` + ); +} + +function getPrincipal(url, attrs) { + let uri = Services.io.newURI(url); + if (!attrs) { + attrs = {}; + } + return Services.scriptSecurityManager.createContentPrincipal(uri, attrs); +} + +function getDefaultPrincipal() { + return getPrincipal("http://example.com"); +} + +function getRelativeFile(relativePath) { + let file = Services.dirsvc + .get(NS_APP_USER_PROFILE_50_DIR, Ci.nsIFile) + .clone(); + + if (Services.appinfo.OS === osWindowsName) { + let winFile = file.QueryInterface(Ci.nsILocalFileWin); + winFile.useDOSDevicePathSyntax = true; + } + + relativePath.split(pathDelimiter).forEach(function (component) { + if (component == "..") { + file = file.parent; + } else { + file.append(component); + } + }); + + return file; +} + +function getSimpleDatabase(principal, persistence) { + let connection = Cc["@mozilla.org/dom/sdb-connection;1"].createInstance( + Ci.nsISDBConnection + ); + + if (!principal) { + principal = getDefaultPrincipal(); + } + + connection.init(principal, persistence); + + return connection; +} diff --git a/dom/cache/test/xpcshell/make_profile.js b/dom/cache/test/xpcshell/make_profile.js new file mode 100644 index 0000000000..b7b1a9042b --- /dev/null +++ b/dom/cache/test/xpcshell/make_profile.js @@ -0,0 +1,137 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * All images in schema_15_profile.zip are from https://github.com/mdn/sw-test/ + * and are CC licensed by https://www.flickr.com/photos/legofenris/. + */ + +// Enumerate the directory tree and store results in entryList as +// +// { path: 'a/b/c', file: <nsIFile> } +// +// The algorithm starts with the first entry already in entryList. +function enumerate_tree(entryList) { + for (var index = 0; index < entryList.length; ++index) { + var path = entryList[index].path; + var file = entryList[index].file; + + if (file.isDirectory()) { + var dirList = file.directoryEntries; + while (dirList.hasMoreElements()) { + var dirFile = dirList.nextFile; + entryList.push({ path: path + "/" + dirFile.leafName, file: dirFile }); + } + } + } +} + +function zip_profile(zipFile, profileDir) { + var zipWriter = Cc["@mozilla.org/zipwriter;1"].createInstance( + Ci.nsIZipWriter + ); + zipWriter.open(zipFile, 0x04 | 0x08 | 0x20); + + var root = profileDir.clone(); + root.append("storage"); + root.append("default"); + root.append("chrome"); + + var entryList = [{ path: "storage/default/chrome", file: root }]; + enumerate_tree(entryList); + + entryList.forEach(function (entry) { + if (entry.file.isDirectory()) { + zipWriter.addEntryDirectory( + entry.path, + entry.file.lastModifiedTime, + false + ); + } else { + var istream = Cc[ + "@mozilla.org/network/file-input-stream;1" + ].createInstance(Ci.nsIFileInputStream); + istream.init(entry.file, -1, -1, 0); + zipWriter.addEntryStream( + entry.path, + entry.file.lastModifiedTime, + Ci.nsIZipWriter.COMPRESSION_DEFAULT, + istream, + false + ); + istream.close(); + } + }); + + zipWriter.close(); +} + +function exactGC() { + return new Promise(function (resolve) { + var count = 0; + function doPreciseGCandCC() { + function scheduleGCCallback() { + Cu.forceCC(); + + if (++count < 2) { + doPreciseGCandCC(); + } else { + resolve(); + } + } + Cu.schedulePreciseGC(scheduleGCCallback); + } + doPreciseGCandCC(); + }); +} + +function resetQuotaManager() { + return new Promise(function (resolve) { + var prefService = Services.prefs; + + // enable quota manager testing mode + var pref = "dom.quotaManager.testing"; + prefService.getBranch(null).setBoolPref(pref, true); + + var request = Services.qms.reset(); + request.callback = resolve; + + // disable quota manager testing mode + // prefService.getBranch(null).setBoolPref(pref, false); + }); +} + +function run_test() { + do_test_pending(); + do_get_profile(); + + var directoryService = Services.dirsvc; + var profileDir = directoryService.get("ProfD", Ci.nsIFile); + var currentDir = directoryService.get("CurWorkD", Ci.nsIFile); + + var zipFile = currentDir.clone(); + zipFile.append("new_profile.zip"); + if (zipFile.exists()) { + zipFile.remove(false); + } + ok(!zipFile.exists()); + + caches + .open("xpcshell-test") + .then(function (c) { + var request = new Request("http://example.com/index.html"); + var response = new Response("hello world"); + return c.put(request, response); + }) + .then(exactGC) + .then(resetQuotaManager) + .then(function () { + zip_profile(zipFile, profileDir); + dump("### ### created zip at: " + zipFile.path + "\n"); + do_test_finished(); + }) + .catch(function (e) { + do_test_finished(); + ok(false, e); + }); +} diff --git a/dom/cache/test/xpcshell/schema_15_profile.zip b/dom/cache/test/xpcshell/schema_15_profile.zip Binary files differnew file mode 100644 index 0000000000..32cc8f2eeb --- /dev/null +++ b/dom/cache/test/xpcshell/schema_15_profile.zip diff --git a/dom/cache/test/xpcshell/schema_25_profile.zip b/dom/cache/test/xpcshell/schema_25_profile.zip Binary files differnew file mode 100644 index 0000000000..626aa8f625 --- /dev/null +++ b/dom/cache/test/xpcshell/schema_25_profile.zip diff --git a/dom/cache/test/xpcshell/test_bug1425146.js b/dom/cache/test/xpcshell/test_bug1425146.js new file mode 100644 index 0000000000..d678bb3745 --- /dev/null +++ b/dom/cache/test/xpcshell/test_bug1425146.js @@ -0,0 +1,51 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This test is mainly to verify that we are able to recover from a situation +// in which the padding file and the padding column are missing during origin +// initialization. This was originally reported in bug 1425146 comment 39. +// The situation can be described as follows: +// 1. A profile is used in FF57. The storage version is 2.1. There's no cache +// storage for http://www.mozilla.org +// 2. The same profile is used in FF56. The storage version is still 2.1 which +// doesn't prevent storage system from working since minor upgrades are +// backwards-compatible. The cache storage for http://www.mozilla.org is +// created with schema version 25 (without any padding stuff). +// 3. The profile is used in FF57 again. Zero padding files for existing cache +// storages are not created because storage is already at version 2.1. +// Storage is being initialized and a missing padding file triggers padding +// size computation from the cache database with schema version 25. Since +// the computation happens before any real DOM cache operation, the database +// is not upgraded to schema version 26, so the padding column is missing. + +add_task(async function testSteps() { + // The profile contains one cache storage, a script for cache creation and + // the storage database: + // - storage/default/http+++www.mozilla.org/cache + // - create_cache.js + // - storage.sqlite + // The file create_cache.js in the package was run locally, specifically it + // was temporarily added to xpcshell.ini and then executed: + // mach xpcshell-test --interactive dom/cache/test/xpcshell/create_cache.js + // Note: it must be executed in FF56 and it only creates the directory + // "storage/default/chrome/cache" and the file "storage.sqlite". To make it + // become the profile in the test, additional manual steps are needed. + // 1. Create "http+++www.mozilla.org" folder under the ""storage/default". + // 2. Copy the "cache" folder under the "storage/default/chrome" to + // "storage/default/http+++www.mozilla.org". + // 3. Remove the folder "storage/default/chrome" + // 4. Remove the folder "storage/temporary". + // 5. Add "create_cache.js". + // 6. Replace the "storage.sqlite" created by FF56 (storage v2.0) with the + // "storage.sqlite" created by FF57 (storage v2.1) + create_test_profile("bug1425146_profile.zip"); + + try { + await caches.open("test"); + ok(true, "Should not have thrown"); + } catch (ex) { + ok(false, "Should not have thrown"); + } +}); diff --git a/dom/cache/test/xpcshell/test_empty_directories.js b/dom/cache/test/xpcshell/test_empty_directories.js new file mode 100644 index 0000000000..2e43ba67bd --- /dev/null +++ b/dom/cache/test/xpcshell/test_empty_directories.js @@ -0,0 +1,85 @@ +/** + * This test is mainly to verify cache won't leave emptry directory. + */ + +function resetStorage() { + return new Promise(function (resolve, reject) { + var qms = Services.qms; + var request = qms.reset(); + request.callback = resolve; + }); +} + +async function setUpEnv() { + Services.prefs.setBoolPref("dom.quotaManager.testing", true); + + // We need this for generating the basic profile path + create_test_profile("schema_25_profile.zip"); + + // Trigger storage upgrade + await caches.open("test"); + const cacheDir = getCacheDir(); + let morgueDir = cacheDir.clone(); + morgueDir.append("morgue"); + + // clean the cache directoy + for (let dir of morgueDir.directoryEntries) { + for (let file of dir.directoryEntries) { + file.remove(false); + } + } + + await resetStorage(); +} + +// This function ensure the directory with file shouldn't have been deleted and +// ensure the directory without file should've been deleted. +function verifyResult() { + const cacheDir = getCacheDir(); + let morgueDir = cacheDir.clone(); + morgueDir.append("morgue"); + + let foundEmpty = false; + for (let dir of morgueDir.directoryEntries) { + let empty = true; + // eslint-disable-next-line no-unused-vars + for (let file of dir.directoryEntries) { + empty = false; + } + + foundEmpty = foundEmpty || empty; + } + return !foundEmpty; +} + +add_task(async function testSteps() { + const url = "https://www.mozilla.org"; + + info("Setting up environment"); + + await setUpEnv(); + + info("Test 0 - InitOrigin shouldn't leave an empty directoy"); + + let cache = await caches.open("test"); + let response = await cache.match(url); + ok(!!response, "Upgrade from 25 to 26 do succeed"); + ok(verifyResult(), "InitOrigin should clean all empty directories"); + + info("Test 1 - DeleteOrphanedBodyFiles shouldn't leave an empty directoy"); + + await cache.put(url, response.clone()); + // eslint-disable-next-line no-unused-vars + let r = await cache.match(url); + await cache.delete(url); + await resetStorage(); + + cache = await caches.open("test"); + + // Extra operation to ensure the deletion is completed + await cache.match(url); + + ok(verifyResult(), "Empty directory should be removed"); + + await caches.delete("test"); +}); diff --git a/dom/cache/test/xpcshell/test_migration.js b/dom/cache/test/xpcshell/test_migration.js new file mode 100644 index 0000000000..5a6637eecc --- /dev/null +++ b/dom/cache/test/xpcshell/test_migration.js @@ -0,0 +1,65 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * All images in schema_15_profile.zip are from https://github.com/mdn/sw-test/ + * and are CC licensed by https://www.flickr.com/photos/legofenris/. + */ + +add_task(async function testSteps() { + create_test_profile("schema_15_profile.zip"); + + const cache = await caches.open("xpcshell-test"); + ok(cache, "cache exists"); + + const requestList = await cache.keys(); + + ok(requestList.length, "should have at least one request in cache"); + for (const request of requestList) { + ok(request, "each request in list should be non-null"); + ok( + request.redirect === "follow", + 'request.redirect should default to "follow"' + ); + ok( + request.cache === "default", + 'request.cache should have been updated to "default"' + request.cache + ); + ok( + request.mode === "navigate", + 'request.mode should have been updated to "navigate"' + ); + ok( + request.referrerPolicy === "no-referrer-when-downgrade", + 'request.referrerPolicy should have been updated to "no-referrer-when-downgrade"' + ); + } + + const responseList = await Promise.all( + requestList.map(function (request) { + return cache.match(request); + }) + ); + + ok(responseList.length, "should have at least one response in cache"); + for (const response of responseList) { + ok(response, "each response should be non-null"); + // reponse.url is a empty string in current test file. It should test for + // not being a empty string once thet test file is updated. + ok( + typeof response.url === "string", + "each response.url should be a string" + ); + // reponse.redirected may be changed once test file is updated. It should + // be false since current reponse.url is a empty string. + ok( + response.redirected === false, + "each response.redirected should be false" + ); + Assert.equal( + response.headers.get("Content-Type"), + "text/plain;charset=UTF-8", + "the response should have the correct header" + ); + } +}); diff --git a/dom/cache/test/xpcshell/test_originInit.js b/dom/cache/test/xpcshell/test_originInit.js new file mode 100644 index 0000000000..395cba8a0f --- /dev/null +++ b/dom/cache/test/xpcshell/test_originInit.js @@ -0,0 +1,249 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +add_task(async function testSteps() { + // ToDo: Replace storage and default with a getter function once we expose the + // filenames of them to a IDL file. + const basePath = `${storageDirName}/${defaultPersistenceDirName}/`; + const principal = getPrincipal("https://example.com"); + const originDirName = "https+++example.com"; + + // ToDo: Replace caches.sqlite with a getter function once we expose the + // filename of it to a IDL file. + const cachesDatabase = getRelativeFile( + `${basePath}/${originDirName}/${cacheClientDirName()}/caches.sqlite` + ); + // ToDo: Replace .padding with a getter function once we expose the filename + // of it to a IDL file. + const paddingFile = getRelativeFile( + `${basePath}/${originDirName}/${cacheClientDirName()}/.padding` + ); + // ToDo: Replace .padding-tmp with a getter function once we expose the + // filename of it to a IDL file. + const paddingTempFile = getRelativeFile( + `${basePath}/${originDirName}/${cacheClientDirName()}/.padding-tmp` + ); + // ToDo: Replace morgue with a getter function once we expose the + // filename of it to a IDL file. + const morgueDir = getRelativeFile( + `${basePath}/${originDirName}/${cacheClientDirName()}/morgue` + ); + + const persistentCacheDir = getRelativeFile( + `${storageDirName}/${persistentPersistenceDirName}/${originDirName}/` + + `${cacheClientDirName()}` + ); + + async function createNormalCacheOrigin() { + async function sandboxScript() { + const cache = await caches.open("myCache"); + const request = new Request("https://example.com/index.html"); + const response = new Response("hello world"); + await cache.put(request, response); + } + + const sandbox = new Cu.Sandbox(principal, { + wantGlobalProperties: ["caches", "fetch"], + }); + + const promise = new Promise(function (resolve, reject) { + sandbox.resolve = resolve; + sandbox.reject = reject; + }); + + Cu.evalInSandbox( + sandboxScript.toSource() + " sandboxScript().then(resolve, reject);", + sandbox + ); + await promise; + + let request = reset(); + await requestFinished(request); + } + + async function createPersistentTestOrigin() { + let database = getSimpleDatabase(principal, "persistent"); + + let request = database.open("data"); + await requestFinished(request); + + request = reset(); + await requestFinished(request); + } + + function removeFile(file) { + file.remove(false); + } + + function removeDir(dir) { + dir.remove(true); + } + + function createEmptyFile(file) { + file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o644); + } + + function createEmptyDirectory(dir) { + dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o0755); + } + + async function initTestOrigin() { + let request = initStorage(); + await requestFinished(request); + + request = initTemporaryStorage(); + await requestFinished(request); + + request = initTemporaryOrigin(principal); + await requestFinished(request); + } + + async function initPersistentTestOrigin() { + let request = initStorage(); + await requestFinished(request); + + request = initPersistentOrigin(principal); + await requestFinished(request); + } + + function checkFiles( + expectCachesDatabase, + expectPaddingFile, + expectTempPaddingFile, + expectMorgueDir + ) { + let exists = cachesDatabase.exists(); + if (expectCachesDatabase) { + ok(exists, "caches.sqlite does exist"); + } else { + ok(!exists, "caches.sqlite doesn't exist"); + } + + exists = paddingFile.exists(); + if (expectPaddingFile) { + ok(exists, ".padding does exist"); + } else { + ok(!exists, ".padding doesn't exist"); + } + + exists = paddingTempFile.exists(); + if (expectTempPaddingFile) { + ok(exists, ".padding-tmp does exist"); + } else { + ok(!exists, ".padding-tmp doesn't exist"); + } + + exists = morgueDir.exists(); + if (expectMorgueDir) { + ok(exists, "morgue does exist"); + } else { + ok(!exists, "moruge doesn't exist"); + } + } + + async function clearTestOrigin() { + let request = clearOrigin(principal, defaultPersistence); + await requestFinished(request); + } + + async function clearPersistentTestOrigin() { + let request = clearOrigin(principal, persistentPersistence); + await requestFinished(request); + } + + async function testOriginInit( + createCachesDatabase, + createPaddingFile, + createTempPaddingFile, + createMorgueDir + ) { + info( + `Testing initialization of cache directory when caches.sqlite ` + + `${createCachesDatabase ? "exists" : "doesn't exist"}, .padding ` + + `${createPaddingFile ? "exists" : "doesn't exist"}, .padding-tmp ` + + `${createTempPaddingFile ? "exists" : "doesn't exist"}, morgue ` + + `${createMorgueDir ? "exists" : "doesn't exist"}` + ); + + await createNormalCacheOrigin(); + + checkFiles(true, true, false, true); + + if (!createCachesDatabase) { + removeFile(cachesDatabase); + } + + if (!createPaddingFile) { + removeFile(paddingFile); + } + + if (createTempPaddingFile) { + createEmptyFile(paddingTempFile); + } + + if (!createMorgueDir) { + removeDir(morgueDir); + } + + await initTestOrigin(); + + checkFiles( + createCachesDatabase, + // After the origin is initialized, ".padding" should only exist when + // "caches.sqlite" exists. + createCachesDatabase, + // After the origin is initialized, ".padding-tmp" should have always been + // removed. + false, + createCachesDatabase && createMorgueDir + ); + + await clearTestOrigin(); + } + + // Test all possible combinations. + for (let createCachesDatabase of [false, true]) { + for (let createPaddingFile of [false, true]) { + for (let createTempPaddingFile of [false, true]) { + for (let createMorgueDir of [false, true]) { + await testOriginInit( + createCachesDatabase, + createPaddingFile, + createTempPaddingFile, + createMorgueDir + ); + } + } + } + } + + // Verify that InitializeOrigin doesn't fail when a + // storage/permanent/${origin}/cache exists. + async function testPermanentCacheDir() { + info( + "Testing initialization of cache directory placed in permanent origin " + + "directory" + ); + + await createPersistentTestOrigin(); + + createEmptyDirectory(persistentCacheDir); + + try { + await initPersistentTestOrigin(); + + ok(true, "Should not have thrown"); + } catch (ex) { + ok(false, "Should not have thrown"); + } + + let exists = persistentCacheDir.exists(); + ok(exists, "cache directory in permanent origin directory does exist"); + + await clearPersistentTestOrigin(); + } + + await testPermanentCacheDir(); +}); diff --git a/dom/cache/test/xpcshell/test_padding_error_handle.js b/dom/cache/test/xpcshell/test_padding_error_handle.js new file mode 100644 index 0000000000..1bb1e1804f --- /dev/null +++ b/dom/cache/test/xpcshell/test_padding_error_handle.js @@ -0,0 +1,72 @@ +/** + * This test is mainly to verify cache actions work as usual even there exists + * an unexpected padding file. + */ + +function getTempPaddingFilePath() { + let cacheDir = getCacheDir(); + let temporaryPaddingFile = cacheDir.clone(); + temporaryPaddingFile.append(".padding-tmp"); + return temporaryPaddingFile; +} + +function createTempPaddingFile() { + let temporaryPaddingFile = getTempPaddingFilePath(); + temporaryPaddingFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0644", 8)); + + ok( + temporaryPaddingFile.exists(), + "Temporary padding file does be created by test" + ); +} + +add_task(async function testSteps() { + create_test_profile("schema_25_profile.zip"); + let cache = await caches.open("test"); + + // Step 1: Verify cache.match won't fail when there is a temporary padding + // file + createTempPaddingFile(); + + let response = await cache.match("https://www.mozilla.org"); + ok(!!response, "Upgrade from 25 to 26 do succeed"); + + // Note: Only cache write actions(e.g. cache.put/add/addAll/delete) will + // remove unexpected temporary padding file when writting an opaque response + // into the file-system. Cache read actions(e.g. cache.keys/match) won't. + let temporaryPaddingFile = getTempPaddingFilePath(); + ok( + temporaryPaddingFile.exists(), + "Temporary padding file doesn't be removed by cache.match" + ); + + // Step 2: Verify cache.put won't fail when there is a temporary padding + // file + await cache.put("https://foo.com", response); + ok( + !temporaryPaddingFile.exists(), + "Temporary padding file does be removed by cache.put" + ); + + // Step 3: Verify cache.keys won't fail when there is a temporary padding + // file + createTempPaddingFile(); + + let cacheEntries = await cache.keys("https://foo.com"); + ok(cacheEntries.length === 1, "Cache.put does succeed"); + + ok( + temporaryPaddingFile.exists(), + "Temporary padding file doesn't be removed by cache.keys" + ); + + // Step 4: Verify cache.delete won't fail when there is a temporary padding + // file + await cache.delete("https://foo.com"); + ok( + !temporaryPaddingFile.exists(), + "Temporary padding file does be removed by cache.delete" + ); + + await caches.delete("test"); +}); diff --git a/dom/cache/test/xpcshell/test_schema_26_upgrade.js b/dom/cache/test/xpcshell/test_schema_26_upgrade.js new file mode 100644 index 0000000000..3bdb789e17 --- /dev/null +++ b/dom/cache/test/xpcshell/test_schema_26_upgrade.js @@ -0,0 +1,18 @@ +/** + * The schema_25_profile.zip are made from local Nightly by following step + * 1. Go to any website + * 2. Open web console and type + * caches.open("test") + * .then(c => fetch("https://www.mozilla.org", {mode:"no-cors"}) + * .then(r => c.put("https://www.mozilla.org", r))); + * 3. Go to profile directory and rename the website folder to "chrome" + */ + +add_task(async function testSteps() { + create_test_profile("schema_25_profile.zip"); + + let cache = await caches.open("test"); + let response = await cache.match("https://www.mozilla.org"); + ok(!!response, "Upgrade from 25 to 26 do succeed"); + ok(response.type === "opaque", "The response type does be opaque"); +}); diff --git a/dom/cache/test/xpcshell/xpcshell.ini b/dom/cache/test/xpcshell/xpcshell.ini new file mode 100644 index 0000000000..b1640ce42d --- /dev/null +++ b/dom/cache/test/xpcshell/xpcshell.ini @@ -0,0 +1,21 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +[DEFAULT] +head = head.js +support-files = + bug1425146_profile.zip + schema_15_profile.zip + schema_25_profile.zip + +# dummy test entry to generate profile zip files +[make_profile.js] + skip-if = true + +[test_bug1425146.js] +[test_empty_directories.js] +[test_migration.js] +[test_originInit.js] +[test_padding_error_handle.js] +[test_schema_26_upgrade.js] |