diff options
Diffstat (limited to 'dom/fs/test/common')
-rw-r--r-- | dom/fs/test/common/.eslintrc.js | 14 | ||||
-rw-r--r-- | dom/fs/test/common/dummy.js | 0 | ||||
-rw-r--r-- | dom/fs/test/common/mochitest.toml | 15 | ||||
-rw-r--r-- | dom/fs/test/common/moz.build | 21 | ||||
-rw-r--r-- | dom/fs/test/common/nsresult.js | 9 | ||||
-rw-r--r-- | dom/fs/test/common/test_basics.js | 375 | ||||
-rw-r--r-- | dom/fs/test/common/test_fileSystemDirectoryHandle.js | 196 | ||||
-rw-r--r-- | dom/fs/test/common/test_syncAccessHandle.js | 237 | ||||
-rw-r--r-- | dom/fs/test/common/test_writableFileStream.js | 153 | ||||
-rw-r--r-- | dom/fs/test/common/xpcshell.toml | 10 |
10 files changed, 1030 insertions, 0 deletions
diff --git a/dom/fs/test/common/.eslintrc.js b/dom/fs/test/common/.eslintrc.js new file mode 100644 index 0000000000..d805cf6e0f --- /dev/null +++ b/dom/fs/test/common/.eslintrc.js @@ -0,0 +1,14 @@ +/* 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/. */ + +"use strict"; + +module.exports = { + globals: { + Assert: true, + exported_symbols: true, + require_module: true, + Utils: true, + }, +}; diff --git a/dom/fs/test/common/dummy.js b/dom/fs/test/common/dummy.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/dom/fs/test/common/dummy.js diff --git a/dom/fs/test/common/mochitest.toml b/dom/fs/test/common/mochitest.toml new file mode 100644 index 0000000000..3da39b96b8 --- /dev/null +++ b/dom/fs/test/common/mochitest.toml @@ -0,0 +1,15 @@ +# 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] +support-files = [ + "nsresult.js", + "test_basics.js", + "test_fileSystemDirectoryHandle.js", + "test_syncAccessHandle.js", + "test_writableFileStream.js", +] + +["dummy.js"] +skip-if = ["true"] diff --git a/dom/fs/test/common/moz.build b/dom/fs/test/common/moz.build new file mode 100644 index 0000000000..65d62c9cda --- /dev/null +++ b/dom/fs/test/common/moz.build @@ -0,0 +1,21 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +MOCHITEST_MANIFESTS += [ + "mochitest.toml", +] + +XPCSHELL_TESTS_MANIFESTS += [ + "xpcshell.toml", +] + +TESTING_JS_MODULES.dom.fs.test.common += [ + "nsresult.js", + "test_basics.js", + "test_fileSystemDirectoryHandle.js", + "test_syncAccessHandle.js", + "test_writableFileStream.js", +] diff --git a/dom/fs/test/common/nsresult.js b/dom/fs/test/common/nsresult.js new file mode 100644 index 0000000000..6e59b947a1 --- /dev/null +++ b/dom/fs/test/common/nsresult.js @@ -0,0 +1,9 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const nsresult = { + NS_ERROR_NOT_IMPLEMENTED: Cr.NS_ERROR_NOT_IMPLEMENTED, +}; +exported_symbols.nsresult = nsresult; diff --git a/dom/fs/test/common/test_basics.js b/dom/fs/test/common/test_basics.js new file mode 100644 index 0000000000..f1cb1c222e --- /dev/null +++ b/dom/fs/test/common/test_basics.js @@ -0,0 +1,375 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// This test must be first, since we need the actor not to be created already. +exported_symbols.testGetDirectoryTwice = async function () { + const promise1 = navigator.storage.getDirectory(); + const promise2 = navigator.storage.getDirectory(); + + await Promise.all([promise1, promise2]); + + Assert.ok(true, "Should not have thrown"); +}; + +exported_symbols.testGetDirectoryDoesNotThrow = async function () { + await navigator.storage.getDirectory(); + + Assert.ok(true, "Should not have thrown"); +}; + +exported_symbols.testGetDirectoryKindIsDirectory = async function () { + const root = await navigator.storage.getDirectory(); + + Assert.equal(root.kind, "directory"); +}; + +exported_symbols.testDirectoryHandleStringConversion = async function () { + const root = await navigator.storage.getDirectory(); + + Assert.equal( + "" + root, + "[object FileSystemDirectoryHandle]", + "Is directoryHandle convertible to string?" + ); +}; + +exported_symbols.testNewDirectoryHandleFromPrototype = async function () { + const root = await navigator.storage.getDirectory(); + + try { + Object.create(root.prototype); + Assert.ok(false, "Should have thrown"); + } catch (ex) { + Assert.ok(true, "Should have thrown"); + Assert.ok(ex instanceof TypeError, "Threw the right error type"); + } +}; + +exported_symbols.testIsSameEntryRoot = async function () { + const root = await navigator.storage.getDirectory(); + try { + await root.move(root); + Assert.ok(false, "root should not be movable"); + } catch (ex) { + Assert.ok(true, "root isn't movable"); + } +}; + +exported_symbols.testDirectoryHandleSupportsKeysIterator = async function () { + const root = await navigator.storage.getDirectory(); + + const it = await root.keys(); + Assert.ok(!!it, "Does root support keys iterator?"); +}; + +exported_symbols.testKeysIteratorNextIsCallable = async function () { + const root = await navigator.storage.getDirectory(); + + const it = await root.keys(); + Assert.ok(!!it, "Does root support keys iterator?"); + + const item = await it.next(); + Assert.ok(!!item, "Should return an item"); +}; + +exported_symbols.testDirectoryHandleSupportsValuesIterator = async function () { + const root = await navigator.storage.getDirectory(); + + const it = await root.values(); + Assert.ok(!!it, "Does root support values iterator?"); +}; + +exported_symbols.testValuesIteratorNextIsCallable = async function () { + const root = await navigator.storage.getDirectory(); + + const it = await root.values(); + Assert.ok(!!it, "Does root support values iterator?"); + + const item = await it.next(); + Assert.ok(!!item, "Should return an item"); +}; + +exported_symbols.testDirectoryHandleSupportsEntriesIterator = + async function () { + const root = await navigator.storage.getDirectory(); + + const it = await root.entries(); + Assert.ok(!!it, "Does root support entries iterator?"); + }; + +exported_symbols.testEntriesIteratorNextIsCallable = async function () { + const root = await navigator.storage.getDirectory(); + + const it = await root.entries(); + Assert.ok(!!it, "Does root support entries iterator?"); + + const item = await it.next(); + Assert.ok(!!item, "Should return an item"); +}; + +exported_symbols.testGetFileHandleIsCallable = async function () { + const root = await navigator.storage.getDirectory(); + const allowCreate = { create: true }; + + const item = await root.getFileHandle("fileName", allowCreate); + Assert.ok(!!item, "Should return an item"); + + await root.removeEntry("fileName"); +}; + +exported_symbols.testGetDirectoryHandleIsCallable = async function () { + const root = await navigator.storage.getDirectory(); + const allowCreate = { create: true }; + + const item = await root.getDirectoryHandle("dirName", allowCreate); + Assert.ok(!!item, "Should return an item"); + + await root.removeEntry("dirName"); +}; + +exported_symbols.testRemoveEntryIsCallable = async function () { + const root = await navigator.storage.getDirectory(); + const removeOptions = { recursive: true }; + const allowCreate = { create: true }; + + // Ensure file and directory items exists + await root.getFileHandle("fileName", allowCreate); + await root.getDirectoryHandle("dirName", allowCreate); + await root.removeEntry("fileName", removeOptions); + await root.removeEntry("dirName", removeOptions); + try { + await root.removeEntry("doesNotExist", removeOptions); + Assert.ok(false, "Should have thrown"); + } catch (ex) { + Assert.ok(true, "Should have thrown"); + Assert.equal( + ex.message, + "Entry not found", + "Threw the right error message" + ); + } +}; + +exported_symbols.testResolveIsCallable = async function () { + const root = await navigator.storage.getDirectory(); + const allowCreate = { create: true }; + const item = await root.getFileHandle("fileName", allowCreate); + + let path = await root.resolve(item); + Assert.equal(path.length, 1); + Assert.equal(path[0], "fileName", "Resolve got the right path"); + + await root.removeEntry("fileName"); +}; + +exported_symbols.testFileType = async function () { + const root = await navigator.storage.getDirectory(); + const allowCreate = { create: true }; + const nameStem = "testFileType"; + const empty = ""; + + const extensions = [ + "txt", + "jS", + "JSON", + "css", + "html", + "htm", + "xhtml", + "xml", + "xhtml+xml", + "png", + "apng", + "jPg", + "Jpeg", + "pdF", + "out", + "sh", + "ExE", + "psid", + "EXE ", + " EXE", + "EX\uff65", + "\udbff\udbff\udbff", + // XXX: Invalid surrogate combos like "\udc00\udc00\udc00" may map to the same names impacting cleanup. + "js\udbff", + "\udc00js", + "???", + "\root", + empty, + "AXS", + "dll", + "ocx", + "1", + "ps1", + "cmd", + "xpi", + "swf", + ]; + + const expectedTypes = [ + "text/plain", + "application/javascript", + "application/json", + "text/css", + "text/html", + "text/html", + "application/xhtml+xml", + "text/xml", + empty, + "image/png", + "image/apng", + "image/jpeg", + "image/jpeg", + "application/pdf", + empty, + "application/x-sh", + "application/octet-stream", + empty, + empty, + empty, + empty, + empty, + empty, + empty, + empty, + empty, + empty, + "application/olescript", + "application/x-msdownload", + "application/octet-stream", + empty, + empty, + "text/plain", + "application/x-xpinstall", + "application/x-shockwave-flash", + ]; + + Assert.equal(extensions.length, expectedTypes.length); + + await Promise.all( + extensions.map(async (ext, i) => { + const fileName = nameStem + "." + ext; + const fileHandle = await root.getFileHandle(fileName, allowCreate); + const fileObject = await fileHandle.getFile(); + Assert.equal(fileObject.name, fileHandle.name); + Assert.equal(fileObject.type, expectedTypes[i]); + await root.removeEntry(fileName); + }) + ); +}; + +exported_symbols.testContentTypeChangesOnFileMove = async function () { + const allowCreate = { create: true }; + const root = await navigator.storage.getDirectory(); + const oldName = "testFile.txt"; + const oldType = "text/plain"; + const subdir = await root.getDirectoryHandle("subdir", allowCreate); + + const fileHandle = await root.getFileHandle(oldName, allowCreate); + + async function checkMove(newName, newType) { + Assert.equal(fileHandle.name, newName, "Has filename changed?"); + { + const fileObject = await fileHandle.getFile(); + Assert.equal(fileObject.name, newName, "Is the fileobject renamed?"); + Assert.equal(fileObject.type, newType, "Is the fileobject type updated?"); + } + } + + async function restoreTest() { + await fileHandle.move(root, oldName); + await checkMove(oldName, oldType); + } + + // No name change + await checkMove(oldName, oldType); + await fileHandle.move(subdir); + await checkMove(oldName, oldType); + await restoreTest(); + + // With name change + + async function testMoveWithParams(testName, testType) { + async function testFileMoveCall(...combo) { + await fileHandle.move(...combo); + await checkMove(testName, testType); + await restoreTest(); + } + + await testFileMoveCall(subdir, testName); + await testFileMoveCall(root, testName); + await testFileMoveCall(testName); + } + + const testParams = { + "testFile.json": "application/json", + testFile: oldType, + "testFile.äüö": "", + }; + + for (const [aName, aType] of Object.entries(testParams)) { + await testMoveWithParams(aName, aType); + } +}; + +exported_symbols.testContentTypeChangesOnDirMove = async function () { + const allowCreate = { create: true }; + const root = await navigator.storage.getDirectory(); + const oldName = "testFile.txt"; + const oldType = "text/plain"; + const subDirOrig = await root.getDirectoryHandle("subDirOrig", allowCreate); + const subDirOther = await root.getDirectoryHandle("subDirOther", allowCreate); + const subSubDir = await subDirOrig.getDirectoryHandle( + "subSubDir", + allowCreate + ); + + const testName = "testFile.json"; + const testType = "application/json"; + + async function checkMove(newName, newType) { + const fileHandle = await subSubDir.getFileHandle(newName, allowCreate); + + Assert.equal(fileHandle.name, newName, "Has filename changed?"); + { + const fileObject = await fileHandle.getFile(); + Assert.equal(fileObject.name, newName, "Is the fileobject renamed?"); + Assert.equal(fileObject.type, newType, "Is the fileobject type updated?"); + } + } + + async function restoreTest() { + await subSubDir.move(subDirOrig, "subSubDir"); + await checkMove(oldName, oldType); + } + + await checkMove(oldName, oldType); + + // No name change + await subSubDir.move(subDirOther, "other"); + await checkMove(oldName, oldType); + await restoreTest(); + + // With name change + + async function testDirMoveCall(...combo) { + await subSubDir.move(...combo); + await checkMove(testName, testType); + await restoreTest(); + } + + await testDirMoveCall(subDirOther); + await testDirMoveCall(subDirOther, testName); + await testDirMoveCall(subDirOrig, testName); + await testDirMoveCall(subDirOrig); +}; + +for (const [key, value] of Object.entries(exported_symbols)) { + Object.defineProperty(value, "name", { + value: key, + writable: false, + }); +} diff --git a/dom/fs/test/common/test_fileSystemDirectoryHandle.js b/dom/fs/test/common/test_fileSystemDirectoryHandle.js new file mode 100644 index 0000000000..9142e47380 --- /dev/null +++ b/dom/fs/test/common/test_fileSystemDirectoryHandle.js @@ -0,0 +1,196 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +exported_symbols.smokeTest = async function smokeTest() { + const storage = navigator.storage; + const subdirectoryNames = new Set(["Documents", "Downloads", "Music"]); + const allowCreate = { create: true }; + + { + let root = await storage.getDirectory(); + Assert.ok(root, "Can we access the root directory?"); + + let it = await root.values(); + Assert.ok(!!it, "Does root have values iterator?"); + + let elem = await it.next(); + Assert.ok(elem.done, "Is root directory empty?"); + + for (let dirName of subdirectoryNames) { + await root.getDirectoryHandle(dirName, allowCreate); + Assert.ok(true, "Was it possible to add subdirectory " + dirName + "?"); + } + } + + { + let root = await storage.getDirectory(); + Assert.ok(root, "Can we refresh the root directory?"); + + let it = await root.values(); + Assert.ok(!!it, "Does root have values iterator?"); + + let hasElements = false; + let hangGuard = 0; + for await (let [key, elem] of root.entries()) { + Assert.ok(elem, "Is element not non-empty?"); + Assert.equal("directory", elem.kind, "Is found item a directory?"); + Assert.ok( + elem.name.length >= 1 && elem.name.match("^[A-Za-z]{1,64}"), + "Are names of the elements strings?" + ); + Assert.equal(key, elem.name); + Assert.ok(subdirectoryNames.has(elem.name), "Is name among known names?"); + hasElements = true; + ++hangGuard; + if (hangGuard == 10) { + break; // Exit if there is a hang + } + } + + Assert.ok(hasElements, "Is values container now non-empty?"); + Assert.equal(3, hangGuard, "Do we only have three elements?"); + + { + it = await root.values(); + Assert.ok(!!it, "Does root have values iterator?"); + let elem = await it.next(); + + await elem.value.getDirectoryHandle("Trash", allowCreate); + let subit = elem.value.values(); + Assert.ok(!!elem, "Is element not non-empty?"); + let subdirResult = await subit.next(); + let subdir = subdirResult.value; + Assert.ok(!!subdir, "Is element not non-empty?"); + Assert.equal("directory", subdir.kind, "Is found item a directory?"); + Assert.equal("Trash", subdir.name, "Is found item a directory?"); + } + + const wipeEverything = { recursive: true }; + for (let dirName of subdirectoryNames) { + await root.removeEntry(dirName, wipeEverything); + Assert.ok( + true, + "Was it possible to remove subdirectory " + dirName + "?" + ); + } + } + + { + let root = await storage.getDirectory(); + Assert.ok(root, "Can we refresh the root directory?"); + + let it = root.values(); + Assert.ok(!!it, "Does root have values iterator?"); + + let elem = await it.next(); + Assert.ok(elem.done, "Is root directory empty?"); + } +}; + +exported_symbols.quotaTest = async function () { + const storage = navigator.storage; + const allowCreate = { create: true }; + + { + let root = await storage.getDirectory(); + Assert.ok(root, "Can we access the root directory?"); + + const fileHandle = await root.getFileHandle("test.txt", allowCreate); + Assert.ok(!!fileHandle, "Can we get file handle?"); + + const usageAtStart = await Utils.getCachedOriginUsage(); + Assert.ok(true, "usageAtStart: " + usageAtStart); + + const writable = await fileHandle.createWritable(); + Assert.ok(!!writable, "Can we create writable file stream?"); + + const usageAtWritableCreated = await Utils.getCachedOriginUsage(); + Assert.equal( + usageAtWritableCreated - usageAtStart, + 0, + "Did usage increase when writable was created?" + ); + + const buffer = new ArrayBuffer(42); + Assert.ok(!!buffer, "Can we create array buffer?"); + + const result = await writable.write(buffer); + Assert.equal(result, undefined, "Can we write entire buffer?"); + + const usageAtWriteDone = await Utils.getCachedOriginUsage(); + // Note: Usage should change only on close after 1824305 + Assert.equal( + usageAtWriteDone - usageAtWritableCreated, + buffer.byteLength, + "Is write immediately reflected in usage?" + ); + + await writable.close(); + + const usageAtWritableClosed = await Utils.getCachedOriginUsage(); + + Assert.equal( + usageAtWritableClosed - usageAtWritableCreated, + buffer.byteLength, + "Did usage increase by the amount of bytes written?" + ); + + await root.removeEntry("test.txt"); + + const usageAtFileDeleted = await Utils.getCachedOriginUsage(); + + Assert.equal( + usageAtFileDeleted, + usageAtWritableCreated, + "Is usage back to the value before any writing when the file is removed?" + ); + } +}; + +exported_symbols.pagedIterationTest = async function () { + const root = await navigator.storage.getDirectory(); + + for await (let contentItem of root.keys()) { + await root.removeEntry(contentItem, { recursive: true }); + } + + const allowCreate = { create: true }; + + // When half of the buffer is iterated, a request for the second half is sent. + // We test that the this boundary is crossed smoothly. + // After the buffer is filled, a request for more items is sent. The + // items are placed in the first half of the buffer. + // This boundary should also be crossed without problems. + // Currently, the buffer is half-filled at 1024. + const itemBatch = 3 + 2 * 1024; + for (let i = 0; i <= itemBatch; ++i) { + await root.getDirectoryHandle("" + i, allowCreate); + } + + let result = 0; + let sum = 0; + const handles = new Set(); + let isUnique = true; + for await (let [key, elem] of root.entries()) { + result += key.length; + sum += parseInt(elem.name); + if (handles.has(key)) { + // Asserting here is slow and verbose + isUnique = false; + break; + } + handles.add(key); + } + Assert.ok(isUnique); + Assert.equal(result, 7098); + Assert.equal(sum, (itemBatch * (itemBatch + 1)) / 2); +}; + +for (const [key, value] of Object.entries(exported_symbols)) { + Object.defineProperty(value, "name", { + value: key, + writable: false, + }); +} diff --git a/dom/fs/test/common/test_syncAccessHandle.js b/dom/fs/test/common/test_syncAccessHandle.js new file mode 100644 index 0000000000..ac7e0ef769 --- /dev/null +++ b/dom/fs/test/common/test_syncAccessHandle.js @@ -0,0 +1,237 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const allowCreate = { create: true }; + +exported_symbols.test0 = async function () { + let root = await navigator.storage.getDirectory(); + Assert.ok(root, "Can we access the root directory?"); + + try { + await root.getFileHandle("test.txt"); + Assert.ok(false, "Opened file that shouldn't exist"); + } catch (e) { + dump("caught exception when we tried to open a non-existant file\n"); + } +}; + +exported_symbols.test1 = async function () { + let root = await navigator.storage.getDirectory(); + Assert.ok(root, "Can we access the root directory?"); + + const testFile = await root.getFileHandle("test.txt", allowCreate); + Assert.ok(!!testFile, "Can't create file"); + let handle = await testFile.createSyncAccessHandle(); + Assert.ok(!!handle, "Can't create SyncAccessHandle"); + await handle.close(); + handle = await testFile.createSyncAccessHandle(); + Assert.ok(!!handle, "Can't create second SyncAccessHandle to same file"); + await handle.close(); +}; + +exported_symbols.test2 = async function () { + let root = await navigator.storage.getDirectory(); + Assert.ok(root, "Can we access the root directory?"); + + const testFile = await root.getFileHandle("test.txt", allowCreate); + Assert.ok(!!testFile, "Can't open file"); + let handle = await testFile.createSyncAccessHandle(); + Assert.ok(!!handle, "Can't create SyncAccessHandle"); + await handle.close(); + + await root.removeEntry("test.txt"); + try { + handle = await testFile.createSyncAccessHandle(); + Assert.ok(!!handle, "Didn't remove file!"); + if (handle) { + await handle.close(); + } + } catch (e) { + dump("Caught exception trying to create accesshandle to deleted file\n"); + } +}; + +exported_symbols.test3 = async function () { + let root = await navigator.storage.getDirectory(); + Assert.ok(!!root, "Can we access the root directory?"); + + let dir = await root.getDirectoryHandle("dir", allowCreate); + Assert.ok(!!dir, "Can we create a directory?"); + + // XXX not implemented yet + //const path = await root.resolve(dir); + //Assert.ok(path == ["dir"], "Wrong path: " + path); + + let dir2 = await dir.getDirectoryHandle("dir", allowCreate); + Assert.ok(!!dir, "Can we create dir/dir?"); + + // XXX not implemented yet + //const path = await root.resolve(dir2); + //Assert.ok(path == ["dir", "dir"], "Wrong path: " + path); + + let dir3 = await dir.getDirectoryHandle("bar", allowCreate); + Assert.ok(!!dir3, "Can we create dir/bar?"); + + // This should fail + try { + await root.getDirectoryHandle("bar"); + Assert.ok(!dir, "we shouldn't be able to get bar unless we create it"); + } catch (e) { + dump("caught exception when we tried to get a non-existant dir\n"); + } + + const testFile = await dir2.getFileHandle("test.txt", allowCreate); + Assert.ok(!!testFile, "Can't create file in dir2"); + let handle = await testFile.createSyncAccessHandle(); + Assert.ok(!!handle, "Can't create SyncAccessHandle in dir2"); + await handle.close(); +}; + +exported_symbols.test4 = async function () { + let root = await navigator.storage.getDirectory(); + Assert.ok(!!root, "Can we access the root directory?"); + + const testFile = await root.getFileHandle("test.txt", allowCreate); + Assert.ok(!!testFile, "Can't access existing file"); + let handle = await testFile.createSyncAccessHandle(); + Assert.ok(!!handle, "Can't create SyncAccessHandle to existing file"); + + // Write a sentence to the end of the file. + const encoder = new TextEncoder(); + const writeBuffer = encoder.encode("Thank you for reading this."); + const writeSize = handle.write(writeBuffer); + Assert.ok(!!writeSize); + + // Read it back + // Get size of the file. + let fileSize = await handle.getSize(); + Assert.ok(fileSize == writeBuffer.byteLength); + // Read file content to a buffer. + const readBuffer = new ArrayBuffer(fileSize); + const readSize = handle.read(readBuffer, { at: 0 }); + Assert.ok(!!readSize); + //Assert.ok(readBuffer == writeBuffer); + + await handle.truncate(5); + fileSize = await handle.getSize(); + Assert.ok(fileSize == 5); + + await handle.flush(); + await handle.close(); +}; + +exported_symbols.test5 = async function () { + let root = await navigator.storage.getDirectory(); + Assert.ok(!!root, "Can we access the root directory?"); + + const testFile = await root.getFileHandle("test.txt", allowCreate); + Assert.ok(!!testFile, "Can't create file"); + let handle = await testFile.createSyncAccessHandle(); + Assert.ok(!!handle, "Can't create SyncAccessHandle"); + + try { + const testFile2 = await root.getFileHandle("test2.txt", allowCreate); + let handle2 = await testFile2.createSyncAccessHandle(); + Assert.ok(!!handle2, "can't create SyncAccessHandle to second file!"); + if (handle2) { + await handle2.close(); + } + } catch (e) { + Assert.ok(false, "Failed to create second file"); + } + + await handle.close(); +}; + +exported_symbols.test6 = async function () { + let root = await navigator.storage.getDirectory(); + Assert.ok(root, "Can we access the root directory?"); + + const testFile = await root.getFileHandle("test.txt", allowCreate); + Assert.ok(!!testFile, "Can't get file"); + let handle = await testFile.createSyncAccessHandle(); + Assert.ok(!!handle, "Can't create SyncAccessHandle"); + + try { + let handle2 = await testFile.createSyncAccessHandle(); + Assert.ok(!handle2, "Shouldn't create SyncAccessHandle!"); + if (handle2) { + await handle2.close(); + } + } catch (e) { + // should always happen + dump("caught exception when we tried to get 2 SyncAccessHandles\n"); + } + + // test that locks work across multiple connections for an origin + try { + let root2 = await navigator.storage.getDirectory(); + Assert.ok(root2, "Can we access the root2 directory?"); + + const testFile2 = await root2.getFileHandle("test.txt"); + Assert.ok(!!testFile2, "Can't get file"); + let handle2 = await testFile2.createSyncAccessHandle(); + Assert.ok(!handle2, "Shouldn't create SyncAccessHandle (2)!"); + if (handle2) { + await handle2.close(); + } + } catch (e) { + // should always happen + dump("caught exception when we tried to get 2 SyncAccessHandles\n"); + } + + if (handle) { + await handle.close(); + } +}; + +exported_symbols.quotaTest = async function () { + const shrinkedStorageSizeKB = 5 * 1024; + const defaultDatabaseSize = 491520; + + // Shrink storage size to 5MB. + await Utils.shrinkStorageSize(shrinkedStorageSizeKB); + + let root = await navigator.storage.getDirectory(); + Assert.ok(root, "Can we access the root directory?"); + + // Fill entire storage. + const fileHandle = await root.getFileHandle("test.txt", allowCreate); + Assert.ok(!!fileHandle, "Can we get file handle?"); + + const accessHandle = await fileHandle.createSyncAccessHandle(); + Assert.ok(!!accessHandle, "Can we create sync access handle?"); + + const buffer = new ArrayBuffer( + shrinkedStorageSizeKB * 1024 - defaultDatabaseSize + ); + Assert.ok(!!buffer, "Can we create array buffer?"); + + const written = accessHandle.write(buffer); + Assert.equal(written, buffer.byteLength, "Can we write entire buffer?"); + + // Try to write one more byte. + const fileHandle2 = await root.getFileHandle("test2.txt", allowCreate); + Assert.ok(!!fileHandle2, "Can we get file handle?"); + + const accessHandle2 = await fileHandle2.createSyncAccessHandle(); + Assert.ok(!!accessHandle2, "Can we create sync access handle?"); + + const buffer2 = new ArrayBuffer(1); + Assert.ok(!!buffer2, "Can we create array buffer?"); + + const written2 = accessHandle2.write(buffer2); + Assert.equal(written2, 0, "Can we write beyond the limit?"); + + await accessHandle.close(); + await accessHandle2.close(); + + await Utils.restoreStorageSize(); +}; + +for (const [key, value] of Object.entries(exported_symbols)) { + Object.defineProperty(value, "name", { + value: key, + writable: false, + }); +} diff --git a/dom/fs/test/common/test_writableFileStream.js b/dom/fs/test/common/test_writableFileStream.js new file mode 100644 index 0000000000..016c53bf3b --- /dev/null +++ b/dom/fs/test/common/test_writableFileStream.js @@ -0,0 +1,153 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const allowCreate = { create: true }; + +exported_symbols.test0 = async function () { + let root = await navigator.storage.getDirectory(); + Assert.ok(!!root, "Can we access the root directory?"); + + const testFile = await root.getFileHandle("test.txt", allowCreate); + Assert.ok(!!testFile, "Can't access existing file"); + let writable = await testFile.createWritable(); + Assert.ok(!!writable, "Can't create WritableFileStream to existing file"); + + // Write a sentence to the end of the file. + const encoder = new TextEncoder(); + const writeBuffer = encoder.encode("Thank you for reading this."); + try { + dump("Trying to write...\n"); + await writable.write(writeBuffer); + dump("closing...\n"); + await writable.close(); + } catch (e) { + Assert.ok(false, "Couldn't write to WritableFileStream: " + e); + } + + // Read it back + // Get size of the file. + let file = await testFile.getFile(); + Assert.ok( + !!file, + "Can't create File to file written with WritableFileStream" + ); + let fileSize = file.size; + Assert.ok(fileSize == writeBuffer.byteLength); +}; + +exported_symbols.quotaTest = async function () { + const shrinkedStorageSizeKB = 5 * 1024; + const defaultDatabaseSize = 491547; + + // Shrink storage size to 5MB. + await Utils.shrinkStorageSize(shrinkedStorageSizeKB); + + let root = await navigator.storage.getDirectory(); + Assert.ok(root, "Can we access the root directory?"); + + // Fill entire storage. + const fileHandle = await root.getFileHandle("test.txt", allowCreate); + Assert.ok(!!fileHandle, "Can we get file handle?"); + + const writable = await fileHandle.createWritable(); + Assert.ok(!!writable, "Can we create writable file stream?"); + + const buffer = new ArrayBuffer( + shrinkedStorageSizeKB * 1024 - defaultDatabaseSize + ); + Assert.ok(!!buffer, "Can we create array buffer?"); + + const result = await writable.write(buffer); + Assert.equal(result, undefined, "Can we write entire buffer?"); + + // Try to write one more byte. + const fileHandle2 = await root.getFileHandle("test2.txt", allowCreate); + Assert.ok(!!fileHandle2, "Can we get file handle?"); + + const writable2 = await fileHandle2.createWritable(); + Assert.ok(!!writable2, "Can we create writable file stream?"); + + const buffer2 = new ArrayBuffer(1); + Assert.ok(!!buffer2, "Can we create array buffer?"); + + try { + await writable2.write(buffer2); + Assert.ok(false, "Should have thrown"); + } catch (ex) { + Assert.ok(true, "Did throw"); + Assert.ok(DOMException.isInstance(ex), "Threw DOMException"); + Assert.equal(ex.name, "QuotaExceededError", "Threw right DOMException"); + } + + await writable.close(); + // writable2 is already closed because of the failed write above + + await Utils.restoreStorageSize(); +}; + +exported_symbols.bug1823445 = async function () { + const root = await navigator.storage.getDirectory(); + const testFileName = "test1823445.txt"; + let handle = await root.getFileHandle(testFileName, allowCreate); + let writable = await handle.createWritable(); + await writable.write("abcdefghijklmnop"); + await writable.close(); + + handle = await root.getFileHandle(testFileName); + writable = await handle.createWritable({ keepExistingData: false }); + await writable.write("12345"); + await writable.close(); + + handle = await root.getFileHandle(testFileName); + const file = await handle.getFile(); + const text = await file.text(); + Assert.equal(text, "12345"); +}; + +exported_symbols.bug1824993 = async function () { + const root = await navigator.storage.getDirectory(); + const testFileName = "test1824993.txt"; + const handle = await root.getFileHandle(testFileName, allowCreate); + { + const writable = await handle.createWritable(); + await writable.write("test"); + + { + const file = await handle.getFile(); + const contents = await file.text(); + Assert.equal(contents, ""); + } + + await writable.abort(); + } + + const file = await handle.getFile(); + const contents = await file.text(); + Assert.equal(contents, ""); +}; + +exported_symbols.bug1825018 = async function () { + const root = await navigator.storage.getDirectory(); + const testFileName = "test1825018.txt"; + const handle = await root.getFileHandle(testFileName, allowCreate); + const writable = await handle.createWritable(); + try { + await writable.write({ type: "truncate" }); + } catch (e) { + // Called write without size throws an error as expected + } + + try { + await writable.abort(); + await root.removeEntry(testFileName); + } catch (e) { + Assert.ok(false, e.message); + } +}; + +for (const [key, value] of Object.entries(exported_symbols)) { + Object.defineProperty(value, "name", { + value: key, + writable: false, + }); +} diff --git a/dom/fs/test/common/xpcshell.toml b/dom/fs/test/common/xpcshell.toml new file mode 100644 index 0000000000..9f086b65fe --- /dev/null +++ b/dom/fs/test/common/xpcshell.toml @@ -0,0 +1,10 @@ +[DEFAULT] +support-files = [ + "nsresult.js", + "test_basics.js", + "test_fileSystemDirectoryHandle.js", + "test_writableFileStream.js", +] + +["dummy.js"] +skip-if = ["true"] |