зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1657605: Split up IOUtils test suite r=barret,Gijs
This change splits the dom/system/tests/test_ioutils.html file into several tests located under dom/system/tests/ioutils. Differential Revision: https://phabricator.services.mozilla.com/D86056
This commit is contained in:
Родитель
1743e71bc9
Коммит
fd57fb1ae0
|
@ -103,5 +103,5 @@ LOCAL_INCLUDES += [
|
|||
'/xpcom/base',
|
||||
]
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']
|
||||
MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini', 'tests/ioutils/chrome.ini']
|
||||
MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
worker_constants.js
|
||||
file_ioutils_worker.js
|
||||
|
||||
[test_constants.xhtml]
|
||||
[test_ioutils.html]
|
||||
[test_ioutils_worker.xhtml]
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
file_ioutils_test_fixtures.js
|
||||
file_ioutils_worker.js
|
||||
|
||||
[test_ioutils.html]
|
||||
[test_ioutils_copy_move.html]
|
||||
[test_ioutils_mkdir.html]
|
||||
[test_ioutils_read_write.html]
|
||||
[test_ioutils_remove.html]
|
||||
[test_ioutils_stat.html]
|
||||
[test_ioutils_worker.xhtml]
|
|
@ -0,0 +1,85 @@
|
|||
// Utility functions.
|
||||
|
||||
/* global IOUtils */
|
||||
|
||||
Uint8Array.prototype.equals = function equals(other) {
|
||||
if (this.byteLength !== other.byteLength) {
|
||||
return false;
|
||||
}
|
||||
return this.every((val, i) => val === other[i]);
|
||||
};
|
||||
|
||||
async function createFile(location, contents = "") {
|
||||
if (typeof contents === "string") {
|
||||
contents = new TextEncoder().encode(contents);
|
||||
}
|
||||
await IOUtils.writeAtomic(location, contents);
|
||||
const exists = await fileExists(location);
|
||||
ok(exists, `Created temporary file at: ${location}`);
|
||||
}
|
||||
|
||||
async function createDir(location) {
|
||||
await IOUtils.makeDirectory(location, {
|
||||
ignoreExisting: true,
|
||||
createAncestors: true,
|
||||
});
|
||||
const exists = await OS.File.exists(location);
|
||||
ok(exists, `Created temporary directory at: ${location}`);
|
||||
}
|
||||
|
||||
async function fileHasBinaryContents(location, expectedContents) {
|
||||
if (!(expectedContents instanceof Uint8Array)) {
|
||||
throw new TypeError("expectedContents must be a byte array");
|
||||
}
|
||||
info(`Opening ${location} for reading`);
|
||||
const bytes = await IOUtils.read(location);
|
||||
return bytes.equals(expectedContents);
|
||||
}
|
||||
|
||||
async function fileHasTextContents(location, expectedContents) {
|
||||
if (typeof expectedContents !== "string") {
|
||||
throw new TypeError("expectedContents must be a string");
|
||||
}
|
||||
info(`Opening ${location} for reading`);
|
||||
const bytes = await IOUtils.read(location);
|
||||
const contents = new TextDecoder().decode(bytes);
|
||||
return contents === expectedContents;
|
||||
}
|
||||
|
||||
async function fileExists(file) {
|
||||
try {
|
||||
let { type } = await IOUtils.stat(file);
|
||||
return type === "regular";
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function dirExists(dir) {
|
||||
try {
|
||||
let { type } = await IOUtils.stat(dir);
|
||||
return type === "directory";
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fileOrDirExists(location) {
|
||||
try {
|
||||
await IOUtils.stat(location);
|
||||
return true;
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanup(...files) {
|
||||
for (const file of files) {
|
||||
await IOUtils.remove(file, {
|
||||
ignoreAbsent: true,
|
||||
recursive: true,
|
||||
});
|
||||
const exists = await fileOrDirExists(file);
|
||||
ok(!exists, `Removed temporary file: ${file}`);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
// - http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
/* eslint-env mozilla/chrome-worker, node */
|
||||
/* global finish, log*/
|
||||
/* global finish, log */
|
||||
|
||||
"use strict";
|
||||
|
||||
|
@ -12,6 +12,7 @@ importScripts("resource://gre/modules/ObjectUtils.jsm");
|
|||
// TODO: Remove this import for OS.File. It is currently being used as a
|
||||
// stop gap for missing IOUtils functionality.
|
||||
importScripts("resource://gre/modules/osfile.jsm");
|
||||
importScripts("file_ioutils_test_fixtures.js");
|
||||
|
||||
self.onmessage = async function(msg) {
|
||||
const tmpDir = OS.Constants.Path.tmpDir;
|
||||
|
@ -102,58 +103,4 @@ self.onmessage = async function(msg) {
|
|||
|
||||
await cleanup(dir);
|
||||
}
|
||||
|
||||
// Utility functions.
|
||||
|
||||
Uint8Array.prototype.equals = function equals(other) {
|
||||
if (this.byteLength !== other.byteLength) {
|
||||
return false;
|
||||
}
|
||||
return this.every((val, i) => val === other[i]);
|
||||
};
|
||||
|
||||
async function cleanup(...files) {
|
||||
for (const file of files) {
|
||||
await self.IOUtils.remove(file, { ignoreAbsent: true, recursive: true });
|
||||
const exists = await fileOrDirExists(file);
|
||||
ok(!exists, `Removed temporary file: ${file}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function createFile(location, contents = "") {
|
||||
if (typeof contents === "string") {
|
||||
contents = new TextEncoder().encode(contents);
|
||||
}
|
||||
await self.IOUtils.writeAtomic(location, contents);
|
||||
const exists = await fileExists(location);
|
||||
ok(exists, `Created temporary file at: ${location}`);
|
||||
}
|
||||
|
||||
async function fileOrDirExists(location) {
|
||||
try {
|
||||
await self.IOUtils.stat(location);
|
||||
return true;
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fileExists(location) {
|
||||
try {
|
||||
let { type } = await self.IOUtils.stat(location);
|
||||
return type === "regular";
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fileHasTextContents(location, expectedContents) {
|
||||
if (typeof expectedContents !== "string") {
|
||||
throw new TypeError("expectedContents must be a string");
|
||||
}
|
||||
info(`Opening ${location} for reading`);
|
||||
const bytes = await self.IOUtils.read(location);
|
||||
const contents = new TextDecoder().decode(bytes);
|
||||
return contents === expectedContents;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the IOUtils file I/O API</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
add_task(async function test_api_is_available_on_window() {
|
||||
ok(window.IOUtils, "IOUtils is present on the window");
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,366 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the IOUtils file I/O API</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<script src="file_ioutils_test_fixtures.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm");
|
||||
const { ObjectUtils } = ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm");
|
||||
|
||||
// TODO: Remove this import for OS.File. It is currently being used as a
|
||||
// stop gap for missing IOUtils functionality.
|
||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
|
||||
const tmpDir = OS.Constants.Path.tmpDir;
|
||||
|
||||
add_task(async function test_move_relative_path() {
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_move_relative_path.tmp");
|
||||
const dest = "relative_to_cwd.tmp";
|
||||
await createFile(tmpFileName, "source");
|
||||
|
||||
info("Test moving a file to a relative destination");
|
||||
await Assert.rejects(
|
||||
window.IOUtils.move(tmpFileName, dest),
|
||||
/Refusing to work with path\(.*\) because only absolute file paths are permitted/,
|
||||
"IOUtils::move only works with absolute paths"
|
||||
);
|
||||
ok(
|
||||
await fileHasTextContents(tmpFileName, "source"),
|
||||
"IOUtils::move doesn't change source file when move fails"
|
||||
);
|
||||
|
||||
await cleanup(tmpFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_move_rename() {
|
||||
// Set up.
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_move_src.tmp");
|
||||
const destFileName = OS.Path.join(tmpDir, "test_ioutils_move_dest.tmp");
|
||||
await createFile(tmpFileName, "dest");
|
||||
// Test.
|
||||
info("Test move to new file in same directory");
|
||||
await window.IOUtils.move(tmpFileName, destFileName);
|
||||
info(`Moved ${tmpFileName} to ${destFileName}`);
|
||||
ok(
|
||||
!await fileExists(tmpFileName)
|
||||
&& await fileHasTextContents(destFileName, "dest"),
|
||||
"IOUtils::move can move source to dest in same directory"
|
||||
)
|
||||
|
||||
// Set up.
|
||||
info("Test move to existing file with no overwrite");
|
||||
await createFile(tmpFileName, "source");
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.move(tmpFileName, destFileName, { noOverwrite: true }),
|
||||
/Could not move source file\(.*\) to destination\(.*\) because the destination already exists and overwrites are not allowed/,
|
||||
"IOUtils::move will refuse to move a file if overwrites are disabled"
|
||||
);
|
||||
ok(
|
||||
await fileExists(tmpFileName)
|
||||
&& await fileHasTextContents(destFileName, "dest"),
|
||||
"Failed IOUtils::move doesn't move the source file"
|
||||
);
|
||||
|
||||
// Test.
|
||||
info("Test move to existing file with overwrite");
|
||||
await window.IOUtils.move(tmpFileName, destFileName, { noOverwrite: false });
|
||||
ok(!await fileExists(tmpFileName), "IOUtils::move moved source");
|
||||
ok(
|
||||
await fileHasTextContents(destFileName, "source"),
|
||||
"IOUtils::move overwrote the destination with the source"
|
||||
);
|
||||
|
||||
// Clean up.
|
||||
await cleanup(tmpFileName, destFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_move_to_dir() {
|
||||
// Set up.
|
||||
info("Test move and rename to non-existing directory");
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_move_to_dir.tmp");
|
||||
const destDir = OS.Path.join(tmpDir, "test_move_to_dir.tmp.d");
|
||||
const dest = OS.Path.join(destDir, "dest.tmp");
|
||||
await createFile(tmpFileName);
|
||||
// Test.
|
||||
ok(!await fileOrDirExists(destDir), "Expected path not to exist");
|
||||
await window.IOUtils.move(tmpFileName, dest);
|
||||
ok(
|
||||
!await fileExists(tmpFileName) && await fileExists(dest),
|
||||
"IOUtils::move creates non-existing parents if needed"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test move and rename to existing directory.")
|
||||
await createFile(tmpFileName);
|
||||
// Test.
|
||||
ok(await dirExists(destDir), `Expected ${destDir} to be a directory`);
|
||||
await window.IOUtils.move(tmpFileName, dest);
|
||||
ok(
|
||||
!await fileExists(tmpFileName)
|
||||
&& await fileExists(dest),
|
||||
"IOUtils::move can move/rename a file into an existing dir"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test move to existing directory without specifying leaf name.")
|
||||
await createFile(tmpFileName);
|
||||
// Test.
|
||||
await window.IOUtils.move(tmpFileName, destDir);
|
||||
ok(await dirExists(destDir), `Expected ${destDir} to be a directory`);
|
||||
ok(
|
||||
!await fileExists(tmpFileName)
|
||||
&& await fileExists(OS.Path.join(destDir, OS.Path.basename(tmpFileName))),
|
||||
"IOUtils::move can move a file into an existing dir"
|
||||
);
|
||||
|
||||
// Clean up.
|
||||
await cleanup(destDir);
|
||||
});
|
||||
|
||||
add_task(async function test_move_dir() {
|
||||
// Set up.
|
||||
info("Test rename an empty directory");
|
||||
const srcDir = OS.Path.join(tmpDir, "test_move_dir.tmp.d");
|
||||
const destDir = OS.Path.join(tmpDir, "test_move_dir_dest.tmp.d");
|
||||
await createDir(srcDir);
|
||||
// Test.
|
||||
await window.IOUtils.move(srcDir, destDir);
|
||||
ok(
|
||||
!await fileOrDirExists(srcDir) && await dirExists(destDir),
|
||||
"IOUtils::move can rename directories"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test move directory and its content into another directory");
|
||||
await createDir(srcDir);
|
||||
await createFile(OS.Path.join(srcDir, "file.tmp"), "foo");
|
||||
// Test.
|
||||
await window.IOUtils.move(srcDir, destDir);
|
||||
const destFile = OS.Path.join(destDir, OS.Path.basename(srcDir), "file.tmp");
|
||||
ok(
|
||||
!await fileOrDirExists(srcDir)
|
||||
&& await dirExists(destDir)
|
||||
&& await dirExists(OS.Path.join(destDir, OS.Path.basename(srcDir)))
|
||||
&& await fileHasTextContents(destFile, "foo"),
|
||||
"IOUtils::move can move a directory and its contents into another one"
|
||||
)
|
||||
|
||||
// Clean up.
|
||||
await cleanup(srcDir, destDir);
|
||||
});
|
||||
|
||||
add_task(async function test_move_failures() {
|
||||
// Set up.
|
||||
info("Test attempt to rename a non-existent source file");
|
||||
const notExistsSrc = OS.Path.join(tmpDir, "not_exists_src.tmp");
|
||||
const notExistsDest = OS.Path.join(tmpDir, "not_exists_dest.tmp");
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.move(notExistsSrc, notExistsDest),
|
||||
/Could not move source file\(.*\) because it does not exist/,
|
||||
"IOUtils::move throws if source file does not exist"
|
||||
);
|
||||
ok(
|
||||
!await fileExists(notExistsSrc) && !await fileExists(notExistsDest),
|
||||
"IOUtils::move fails if source file does not exist"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test attempt to move a directory to a file");
|
||||
const destFile = OS.Path.join(tmpDir, "test_move_failures_file_dest.tmp");
|
||||
const srcDir = OS.Path.join(tmpDir, "test_move_failure_src.tmp.d");
|
||||
await createFile(destFile);
|
||||
await createDir(srcDir);
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.move(srcDir, destFile),
|
||||
/Could not move the source directory\(.*\) to the destination\(.*\) because the destination is not a directory/,
|
||||
"IOUtils::move throws if try to move dir into an existing file"
|
||||
);
|
||||
|
||||
// Clean up.
|
||||
await cleanup(destFile, srcDir);
|
||||
});
|
||||
|
||||
add_task(async function test_copy() {
|
||||
// Set up.
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_orig.tmp");
|
||||
const destFileName = OS.Path.join(tmpDir, "test_ioutils_copy.tmp");
|
||||
await createFile(tmpFileName, "original");
|
||||
// Test.
|
||||
info("Test copy to new file in same directory");
|
||||
await window.IOUtils.copy(tmpFileName, destFileName);
|
||||
ok(
|
||||
await fileExists(tmpFileName)
|
||||
&& await fileHasTextContents(destFileName, "original"),
|
||||
"IOUtils::copy can copy source to dest in same directory"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test copy to existing file with no overwrite");
|
||||
await createFile(tmpFileName, "new contents");
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.copy(tmpFileName, destFileName, { noOverwrite: true }),
|
||||
/Could not copy source file\(.*\) to destination\(.*\) because the destination already exists and overwrites are not allowed/,
|
||||
"IOUtils::copy will refuse to copy to existing destination if overwrites are disabled"
|
||||
);
|
||||
ok(
|
||||
await fileExists(tmpFileName)
|
||||
&& await fileHasTextContents(destFileName, "original"),
|
||||
"Failed IOUtils::move doesn't move the source file"
|
||||
);
|
||||
|
||||
// Test.
|
||||
info("Test copy to existing file with overwrite");
|
||||
await window.IOUtils.copy(tmpFileName, destFileName, { noOverwrite: false });
|
||||
ok(await fileExists(tmpFileName), "IOUtils::copy retains source");
|
||||
ok(
|
||||
await fileHasTextContents(destFileName, "new contents"),
|
||||
"IOUtils::copy overwrote the destination with the source"
|
||||
);
|
||||
|
||||
// Clean up.
|
||||
await cleanup(tmpFileName, destFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_copy_file_to_dir() {
|
||||
// Set up.
|
||||
info("Test copy file to non-existing directory");
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_copy_file_to_dir.tmp");
|
||||
const destDir = OS.Path.join(tmpDir, "test_copy_file_to_dir.tmp.d");
|
||||
const dest = OS.Path.join(destDir, "dest.tmp");
|
||||
await createFile(tmpFileName);
|
||||
// Test.
|
||||
ok(!await fileOrDirExists(destDir), "Expected path not to exist");
|
||||
await window.IOUtils.copy(tmpFileName, dest);
|
||||
ok(
|
||||
await fileExists(tmpFileName) && await fileExists(dest),
|
||||
"IOUtils::copy creates non-existing parents if needed"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test copy file to existing directory")
|
||||
await createFile(tmpFileName);
|
||||
// Test.
|
||||
ok(await dirExists(destDir), `Expected ${destDir} to be a directory`);
|
||||
await window.IOUtils.copy(tmpFileName, dest);
|
||||
ok(
|
||||
await fileExists(tmpFileName)
|
||||
&& await fileExists(dest),
|
||||
"IOUtils::copy can copy a file into an existing dir"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test copy file to existing directory without specifying leaf name")
|
||||
await createFile(tmpFileName);
|
||||
// Test.
|
||||
await window.IOUtils.copy(tmpFileName, destDir);
|
||||
ok(await dirExists(destDir), `Expected ${destDir} to be a directory`);
|
||||
ok(
|
||||
await fileExists(tmpFileName)
|
||||
&& await fileExists(OS.Path.join(destDir, OS.Path.basename(tmpFileName))),
|
||||
"IOUtils::copy can copy a file into an existing dir"
|
||||
);
|
||||
|
||||
// Clean up.
|
||||
await cleanup(tmpFileName, destDir);
|
||||
});
|
||||
|
||||
add_task(async function test_copy_dir_recursive() {
|
||||
// Set up.
|
||||
info("Test rename an empty directory");
|
||||
const srcDir = OS.Path.join(tmpDir, "test_copy_dir.tmp.d");
|
||||
const destDir = OS.Path.join(tmpDir, "test_copy_dir_dest.tmp.d");
|
||||
await createDir(srcDir);
|
||||
// Test.
|
||||
await window.IOUtils.copy(srcDir, destDir, { recursive: true });
|
||||
ok(
|
||||
await dirExists(srcDir) && await dirExists(destDir),
|
||||
"IOUtils::copy can recursively copy entire directories"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test copy directory and its content into another directory");
|
||||
await createDir(srcDir);
|
||||
await createFile(OS.Path.join(srcDir, "file.tmp"), "foo");
|
||||
// Test.
|
||||
await window.IOUtils.copy(srcDir, destDir, { recursive: true });
|
||||
const destFile = OS.Path.join(destDir, OS.Path.basename(srcDir), "file.tmp");
|
||||
ok(
|
||||
await dirExists(srcDir)
|
||||
&& await dirExists(destDir)
|
||||
&& await dirExists(OS.Path.join(destDir, OS.Path.basename(srcDir)))
|
||||
&& await fileHasTextContents(destFile, "foo"),
|
||||
"IOUtils::copy can move a directory and its contents into another one"
|
||||
)
|
||||
|
||||
// Clean up.
|
||||
await cleanup(srcDir, destDir);
|
||||
});
|
||||
|
||||
add_task(async function test_copy_failures() {
|
||||
// Set up.
|
||||
info("Test attempt to copy a non-existent source file");
|
||||
const notExistsSrc = OS.Path.join(tmpDir, "test_copy_not_exists_src.tmp");
|
||||
const notExistsDest = OS.Path.join(tmpDir, "test_copy_not_exists_dest.tmp");
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.copy(notExistsSrc, notExistsDest),
|
||||
/Could not copy source file\(.*\) because it does not exist/,
|
||||
"IOUtils::copy throws if source file does not exist"
|
||||
);
|
||||
ok(
|
||||
!await fileExists(notExistsSrc) && !await fileExists(notExistsDest),
|
||||
"IOUtils::copy failure due to missing source file does not affect destination"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test attempt to copy a directory to a file");
|
||||
const destFile = OS.Path.join(tmpDir, "test_copy_failures_file_dest.tmp");
|
||||
const srcDir = OS.Path.join(tmpDir, "test_copy_failure_src.tmp.d");
|
||||
await createFile(destFile);
|
||||
await createDir(srcDir);
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.copy(srcDir, destFile, { recursive: true }),
|
||||
/Could not copy the source directory\(.*\) to the destination\(.*\) because the destination is not a directory/,
|
||||
"IOUtils::copy throws if try to move dir into an existing file"
|
||||
);
|
||||
ok(await fileHasTextContents(destFile, ""), "IOUtils::copy failure does not affect destination");
|
||||
|
||||
// Set up.
|
||||
info("Test copy directory without recursive option");
|
||||
await createDir(srcDir);
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.copy(srcDir, notExistsDest, { recursive: false }),
|
||||
/Refused to copy source directory\(.*\) to the destination\(.*\)/,
|
||||
"IOUtils::copy throws if try to copy a directory with { recursive: false }"
|
||||
);
|
||||
console.log(`${notExistsDest} exists?`, await fileOrDirExists(notExistsDest))
|
||||
ok(!await fileOrDirExists(notExistsDest), "IOUtils::copy failure does not affect destination");
|
||||
|
||||
// Clean up.
|
||||
await cleanup(destFile, srcDir);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,97 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the IOUtils file I/O API</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<script src="file_ioutils_test_fixtures.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm");
|
||||
const { ObjectUtils } = ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm");
|
||||
|
||||
// TODO: Remove this import for OS.File. It is currently being used as a
|
||||
// stop gap for missing IOUtils functionality.
|
||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
|
||||
const tmpDir = OS.Constants.Path.tmpDir;
|
||||
|
||||
add_task(async function test_make_directory() {
|
||||
info("Test creating a new directory");
|
||||
const newDirectoryName = OS.Path.join(tmpDir, "test_ioutils_new_dir.tmp.d");
|
||||
await window.IOUtils.makeDirectory(newDirectoryName);
|
||||
ok(
|
||||
await OS.File.exists(newDirectoryName),
|
||||
"IOUtils::makeDirectory can create a new directory"
|
||||
);
|
||||
|
||||
info("Test creating an existing directory");
|
||||
await window.IOUtils.makeDirectory(newDirectoryName, { ignoreExisting: true });
|
||||
ok(
|
||||
await OS.File.exists(newDirectoryName),
|
||||
"IOUtils::makeDirectory can ignore existing directories"
|
||||
);
|
||||
await Assert.rejects(
|
||||
window.IOUtils.makeDirectory(newDirectoryName, { ignoreExisting: false }),
|
||||
/Could not create directory because it already exists at .*/,
|
||||
"IOUtils::makeDirectory can throw if the target dir exists"
|
||||
)
|
||||
|
||||
info("Test creating a nested directory");
|
||||
const parentDirName = OS.Path.join(tmpDir, "test_ioutils_mkdir_parent.tmp.d");
|
||||
const nestedDirName = OS.Path.join(
|
||||
parentDirName,
|
||||
"test_ioutils_mkdir_child.tmp.d"
|
||||
);
|
||||
await Assert.rejects(
|
||||
window.IOUtils.makeDirectory(nestedDirName, { createAncestors: false }),
|
||||
/Could not create directory at .* because the path has missing ancestor components/,
|
||||
"IOUtils::makeDirectory can fail if the target is missing parents"
|
||||
);
|
||||
ok(!await OS.File.exists(nestedDirName), `Expected ${nestedDirName} not to exist`);
|
||||
await window.IOUtils.makeDirectory(nestedDirName, { createAncestors: true });
|
||||
ok(
|
||||
await OS.File.exists(nestedDirName),
|
||||
"IOUtils::makeDirectory can create ancestors of the target directory"
|
||||
);
|
||||
|
||||
await cleanup(newDirectoryName, parentDirName);
|
||||
});
|
||||
|
||||
add_task(async function test_make_directory_failure() {
|
||||
info("Try to create a directory where a file already exists");
|
||||
const notADirFileName = OS.Path.join(tmpDir, "test_ioutils_not_a_dir.tmp");
|
||||
await createFile(notADirFileName);
|
||||
|
||||
await Assert.rejects(
|
||||
window.IOUtils.makeDirectory(notADirFileName, { ignoreExisting: false }),
|
||||
/Could not create directory because the target file\(.*\) exists and is not a directory/,
|
||||
"IOUtils::makeDirectory [ignoreExisting: false] throws when the target is an existing file"
|
||||
);
|
||||
ok(await fileExists(notADirFileName), `Expected ${notADirFileName} to exist`);
|
||||
|
||||
await Assert.rejects(
|
||||
window.IOUtils.makeDirectory(notADirFileName, { ignoreExisting: true }),
|
||||
/Could not create directory because the target file\(.*\) exists and is not a directory/,
|
||||
"IOUtils::makeDirectory [ignoreExisting: true] throws when the target is an existing file"
|
||||
);
|
||||
ok(await fileExists(notADirFileName), `Expected ${notADirFileName} to exist`);
|
||||
|
||||
await cleanup(notADirFileName);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,279 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the IOUtils file I/O API</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<script src="file_ioutils_test_fixtures.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm");
|
||||
const { ObjectUtils } = ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm");
|
||||
|
||||
// TODO: Remove this import for OS.File. It is currently being used as a
|
||||
// stop gap for missing IOUtils functionality.
|
||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
|
||||
const tmpDir = OS.Constants.Path.tmpDir;
|
||||
|
||||
add_task(async function test_read_failure() {
|
||||
const doesNotExist = OS.Path.join(tmpDir, "does_not_exist.tmp");
|
||||
await Assert.rejects(
|
||||
window.IOUtils.read(doesNotExist),
|
||||
/Could not open the file at .*/,
|
||||
"IOUtils::read rejects when file does not exist"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_write_no_overwrite() {
|
||||
// Make a new file, and try to write to it with overwrites disabled.
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_overwrite.tmp");
|
||||
const untouchableContents = new TextEncoder().encode("Can't touch this!\n");
|
||||
await window.IOUtils.writeAtomic(tmpFileName, untouchableContents);
|
||||
|
||||
const newContents = new TextEncoder().encode("Nah nah nah!\n");
|
||||
await Assert.rejects(
|
||||
window.IOUtils.writeAtomic(tmpFileName, newContents, {
|
||||
noOverwrite: true,
|
||||
}),
|
||||
/Refusing to overwrite the file at */,
|
||||
"IOUtils::writeAtomic rejects writing to existing file if overwrites are disabled"
|
||||
);
|
||||
ok(
|
||||
await fileHasBinaryContents(tmpFileName, untouchableContents),
|
||||
"IOUtils::writeAtomic doesn't change target file when overwrite is refused"
|
||||
);
|
||||
|
||||
const bytesWritten = await window.IOUtils.writeAtomic(
|
||||
tmpFileName,
|
||||
newContents,
|
||||
{ noOverwrite: false /* Default. */ }
|
||||
);
|
||||
is(
|
||||
bytesWritten,
|
||||
newContents.length,
|
||||
"IOUtils::writeAtomic can overwrite files if specified"
|
||||
);
|
||||
|
||||
await cleanup(tmpFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_write_with_backup() {
|
||||
info("Test backup file option with non-existing file");
|
||||
let fileContents = new TextEncoder().encode("Original file contents");
|
||||
let destFileName = OS.Path.join(tmpDir, "test_write_with_backup_option.tmp");
|
||||
let backupFileName = destFileName + ".backup";
|
||||
let bytesWritten =
|
||||
await window.IOUtils.writeAtomic(destFileName, fileContents, {
|
||||
backupFile: backupFileName,
|
||||
});
|
||||
ok(
|
||||
await fileHasTextContents(destFileName, "Original file contents"),
|
||||
"IOUtils::writeAtomic creates a new file with the correct contents"
|
||||
);
|
||||
ok(
|
||||
!await fileExists(backupFileName),
|
||||
"IOUtils::writeAtomic does not create a backup if the target file does not exist"
|
||||
);
|
||||
is(
|
||||
bytesWritten,
|
||||
fileContents.length,
|
||||
"IOUtils::writeAtomic correctly writes to a new file without performing a backup"
|
||||
);
|
||||
|
||||
info("Test backup file option with existing destination");
|
||||
let newFileContents = new TextEncoder().encode("New file contents");
|
||||
ok(await fileExists(destFileName), `Expected ${destFileName} to exist`);
|
||||
bytesWritten =
|
||||
await window.IOUtils.writeAtomic(destFileName, newFileContents, {
|
||||
backupFile: backupFileName,
|
||||
});
|
||||
ok(
|
||||
await fileHasTextContents(backupFileName, "Original file contents"),
|
||||
"IOUtils::writeAtomic can backup an existing file before writing"
|
||||
);
|
||||
ok(
|
||||
await fileHasTextContents(destFileName, "New file contents"),
|
||||
"IOUtils::writeAtomic can create the target with the correct contents"
|
||||
);
|
||||
is(
|
||||
bytesWritten,
|
||||
newFileContents.length,
|
||||
"IOUtils::writeAtomic correctly writes to the target after taking a backup"
|
||||
);
|
||||
|
||||
await cleanup(destFileName, backupFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_write_with_backup_and_tmp() {
|
||||
info("Test backup with tmp and backup file options, non-existing destination");
|
||||
let fileContents = new TextEncoder().encode("Original file contents");
|
||||
let destFileName = OS.Path.join(tmpDir, "test_write_with_backup_and_tmp_options.tmp");
|
||||
let backupFileName = destFileName + ".backup";
|
||||
let tmpFileName = OS.Path.join(tmpDir, "temp_file.tmp");
|
||||
let bytesWritten =
|
||||
await window.IOUtils.writeAtomic(destFileName, fileContents, {
|
||||
backupFile: backupFileName,
|
||||
tmpPath: tmpFileName,
|
||||
});
|
||||
ok(!await fileExists(tmpFileName), "IOUtils::writeAtomic cleans up the tmpFile");
|
||||
ok(
|
||||
!await fileExists(backupFileName),
|
||||
"IOUtils::writeAtomic does not create a backup if the target file does not exist"
|
||||
);
|
||||
ok(
|
||||
await fileHasTextContents(destFileName, "Original file contents"),
|
||||
"IOUtils::writeAtomic can write to the destination when a temporary file is used"
|
||||
);
|
||||
is(
|
||||
bytesWritten,
|
||||
fileContents.length,
|
||||
"IOUtils::writeAtomic can copy tmp file to destination without performing a backup"
|
||||
);
|
||||
|
||||
info("Test backup with tmp and backup file options, existing destination");
|
||||
let newFileContents = new TextEncoder().encode("New file contents");
|
||||
bytesWritten =
|
||||
await window.IOUtils.writeAtomic(destFileName, newFileContents, {
|
||||
backupFile: backupFileName,
|
||||
tmpPath: tmpFileName,
|
||||
});
|
||||
|
||||
ok(!await fileExists(tmpFileName), "IOUtils::writeAtomic cleans up the tmpFile");
|
||||
ok(
|
||||
await fileHasTextContents(backupFileName, "Original file contents"),
|
||||
"IOUtils::writeAtomic can create a backup if the target file exists"
|
||||
);
|
||||
ok(
|
||||
await fileHasTextContents(destFileName, "New file contents"),
|
||||
"IOUtils::writeAtomic can write to the destination when a temporary file is used"
|
||||
);
|
||||
is(
|
||||
bytesWritten,
|
||||
newFileContents.length,
|
||||
"IOUtils::writeAtomic IOUtils::writeAtomic can move tmp file to destination after performing a backup"
|
||||
);
|
||||
|
||||
await cleanup(destFileName, backupFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_partial_read() {
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_partial_read.tmp");
|
||||
const bytes = Uint8Array.of(...new Array(50).keys());
|
||||
const bytesWritten = await window.IOUtils.writeAtomic(tmpFileName, bytes);
|
||||
is(
|
||||
bytesWritten,
|
||||
50,
|
||||
"IOUtils::writeAtomic can write entire byte array to file"
|
||||
);
|
||||
|
||||
// Read just the first 10 bytes.
|
||||
const first10 = bytes.slice(0, 10);
|
||||
const bytes10 = await window.IOUtils.read(tmpFileName, 10);
|
||||
ok(
|
||||
ObjectUtils.deepEqual(bytes10, first10),
|
||||
"IOUtils::read can read part of a file, up to specified max bytes"
|
||||
);
|
||||
|
||||
// Trying to explicitly read nothing isn't useful, but it should still
|
||||
// succeed.
|
||||
const bytes0 = await window.IOUtils.read(tmpFileName, 0);
|
||||
is(bytes0.length, 0, "IOUtils::read can read 0 bytes");
|
||||
|
||||
await cleanup(tmpFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_empty_read_and_write() {
|
||||
// Trying to write an empty file isn't very useful, but it should still
|
||||
// succeed.
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_empty.tmp");
|
||||
const emptyByteArray = new Uint8Array(0);
|
||||
const bytesWritten = await window.IOUtils.writeAtomic(
|
||||
tmpFileName,
|
||||
emptyByteArray
|
||||
);
|
||||
is(bytesWritten, 0, "IOUtils::writeAtomic can create an empty file");
|
||||
|
||||
// Trying to explicitly read nothing isn't useful, but it should still
|
||||
// succeed.
|
||||
const bytes0 = await window.IOUtils.read(tmpFileName, 0);
|
||||
is(bytes0.length, 0, "IOUtils::read can read 0 bytes");
|
||||
|
||||
// Implicitly try to read nothing.
|
||||
const nothing = await window.IOUtils.read(tmpFileName);
|
||||
is(nothing.length, 0, "IOUtils:: read can read empty files");
|
||||
|
||||
await cleanup(tmpFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_full_read_and_write() {
|
||||
// Write a file.
|
||||
info("Test writing to a new binary file");
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_numbers.tmp");
|
||||
const bytes = Uint8Array.of(...new Array(50).keys());
|
||||
const bytesWritten = await window.IOUtils.writeAtomic(tmpFileName, bytes);
|
||||
is(
|
||||
bytesWritten,
|
||||
50,
|
||||
"IOUtils::writeAtomic can write entire byte array to file"
|
||||
);
|
||||
|
||||
// Read it back.
|
||||
info("Test reading a binary file");
|
||||
let fileContents = await window.IOUtils.read(tmpFileName);
|
||||
ok(
|
||||
ObjectUtils.deepEqual(bytes, fileContents) &&
|
||||
bytes.length == fileContents.length,
|
||||
"IOUtils::read can read back entire file"
|
||||
);
|
||||
|
||||
const tooManyBytes = bytes.length + 1;
|
||||
fileContents = await window.IOUtils.read(tmpFileName, tooManyBytes);
|
||||
ok(
|
||||
ObjectUtils.deepEqual(bytes, fileContents) &&
|
||||
fileContents.length == bytes.length,
|
||||
"IOUtils::read can read entire file when requested maxBytes is too large"
|
||||
);
|
||||
|
||||
// Clean up.
|
||||
await cleanup(tmpFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_write_relative_path() {
|
||||
const tmpFileName = "test_ioutils_write_relative_path.tmp";
|
||||
const bytes = Uint8Array.of(...new Array(50).keys());
|
||||
|
||||
info("Test writing a file at a relative destination");
|
||||
await Assert.rejects(
|
||||
window.IOUtils.writeAtomic(tmpFileName, bytes),
|
||||
/Refusing to work with path\(.*\) because only absolute file paths are permitted/,
|
||||
"IOUtils::writeAtomic only works with absolute paths"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_read_relative_path() {
|
||||
const tmpFileName = "test_ioutils_read_relative_path.tmp";
|
||||
|
||||
info("Test reading a file at a relative destination");
|
||||
await Assert.rejects(
|
||||
window.IOUtils.read(tmpFileName),
|
||||
/Refusing to work with path\(.*\) because only absolute file paths are permitted/,
|
||||
"IOUtils::writeAtomic only works with absolute paths"
|
||||
);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,97 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the IOUtils file I/O API</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<script src="file_ioutils_test_fixtures.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm");
|
||||
const { ObjectUtils } = ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm");
|
||||
|
||||
// TODO: Remove this import for OS.File. It is currently being used as a
|
||||
// stop gap for missing IOUtils functionality.
|
||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
|
||||
const tmpDir = OS.Constants.Path.tmpDir;
|
||||
|
||||
add_task(async function test_create_and_remove_file() {
|
||||
info("Test creating and removing a single file");
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_create_and_remove.tmp");
|
||||
await window.IOUtils.writeAtomic(tmpFileName, new Uint8Array(0));
|
||||
ok(await fileExists(tmpFileName), `Expected file ${tmpFileName} to exist`);
|
||||
|
||||
await window.IOUtils.remove(tmpFileName);
|
||||
ok(!await fileExists(tmpFileName), "IOUtils::remove can remove files");
|
||||
|
||||
info("Test creating and removing an empty directory");
|
||||
const tmpDirName = OS.Path.join(tmpDir, "test_ioutils_create_and_remove.tmp.d");
|
||||
await window.IOUtils.makeDirectory(tmpDirName);
|
||||
ok(await dirExists(tmpDirName), `Expected directory ${tmpDirName} to exist`);
|
||||
|
||||
await window.IOUtils.remove(tmpDirName);
|
||||
ok(!await dirExists(tmpDirName), "IOUtils::remove can remove empty directories");
|
||||
});
|
||||
|
||||
add_task(async function test_remove_non_existing() {
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutil_remove_non_existing.tmp");
|
||||
ok(!await fileExists(tmpFileName), `Expected file ${tmpFileName} not to exist`);
|
||||
|
||||
await window.IOUtils.remove(tmpFileName, { ignoreAbsent: true });
|
||||
ok(!await fileExists(tmpFileName), "IOUtils::remove can ignore missing files without error");
|
||||
|
||||
await Assert.rejects(
|
||||
window.IOUtils.remove(tmpFileName, { ignoreAbsent: false }),
|
||||
/Could not remove the file at .* because it does not exist/,
|
||||
"IOUtils::remove can throw an error when target file is missing"
|
||||
);
|
||||
ok(!await fileExists(tmpFileName), `Expected file ${tmpFileName} not to exist`);
|
||||
});
|
||||
|
||||
add_task(async function test_remove_recursive() {
|
||||
const tmpParentDir = OS.Path.join(tmpDir, "test_ioutils_remove.tmp.d");
|
||||
const tmpChildDir = OS.Path.join(tmpParentDir, "child.tmp.d");
|
||||
const tmpTopLevelFileName = OS.Path.join(tmpParentDir, "top.tmp");
|
||||
const tmpNestedFileName = OS.Path.join(tmpChildDir, "nested.tmp");
|
||||
await createDir(tmpChildDir);
|
||||
await createFile(tmpTopLevelFileName, "");
|
||||
await createFile(tmpNestedFileName, "");
|
||||
|
||||
ok(
|
||||
await fileExists(tmpTopLevelFileName),
|
||||
`Expected file ${tmpTopLevelFileName} to exist`
|
||||
);
|
||||
ok(
|
||||
await fileExists(tmpNestedFileName),
|
||||
`Expected file ${tmpNestedFileName} to exist`
|
||||
);
|
||||
|
||||
await Assert.rejects(
|
||||
window.IOUtils.remove(tmpParentDir, { recursive: false }),
|
||||
/Could not remove the non-empty directory at .*/,
|
||||
"IOUtils::remove fails if non-recursively removing directory with contents"
|
||||
);
|
||||
|
||||
await window.IOUtils.remove(tmpParentDir, { recursive: true });
|
||||
ok(
|
||||
!await dirExists(tmpParentDir),
|
||||
"IOUtils::remove can recursively remove a directory"
|
||||
);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,91 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the IOUtils file I/O API</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<script src="file_ioutils_test_fixtures.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm");
|
||||
const { ObjectUtils } = ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm");
|
||||
|
||||
// TODO: Remove this import for OS.File. It is currently being used as a
|
||||
// stop gap for missing IOUtils functionality.
|
||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
|
||||
const tmpDir = OS.Constants.Path.tmpDir;
|
||||
|
||||
add_task(async function test_stat() {
|
||||
info("Test attempt to stat a regular empty file");
|
||||
const emptyFileName = OS.Path.join(tmpDir, "test_stat_empty.tmp");
|
||||
await createFile(emptyFileName);
|
||||
|
||||
const emptyFileInfo = await window.IOUtils.stat(emptyFileName);
|
||||
is(emptyFileInfo.size, 0, "IOUtils::stat can get correct (empty) file size");
|
||||
is(emptyFileInfo.path, emptyFileName, "IOUtils::stat result contains the path");
|
||||
is(emptyFileInfo.type, "regular", "IOUtils::stat can stat regular (empty) files");
|
||||
Assert.less(
|
||||
(emptyFileInfo.lastModified - new Date().valueOf()),
|
||||
1000, // Allow for 1 second deviation in case of slow tests.
|
||||
"IOUtils::stat can get the last modification date for a regular file"
|
||||
);
|
||||
|
||||
info("Test attempt to stat a regular binary file");
|
||||
const tempFileName = OS.Path.join(tmpDir, "test_stat_binary.tmp");
|
||||
const bytes = Uint8Array.of(...new Array(50).keys());
|
||||
await createFile(tempFileName, bytes);
|
||||
|
||||
const fileInfo = await window.IOUtils.stat(tempFileName);
|
||||
is(fileInfo.size, 50, "IOUtils::stat can get correct file size");
|
||||
is(fileInfo.path, tempFileName, "IOUtils::stat result contains the path");
|
||||
is(fileInfo.type, "regular", "IOUtils::stat can stat regular files");
|
||||
Assert.less(
|
||||
(fileInfo.lastModified - new Date().valueOf()),
|
||||
1000, // Allow for 1 second deviation in case of slow tests.
|
||||
"IOUtils::stat can get the last modification date for a regular file"
|
||||
);
|
||||
|
||||
info("Test attempt to stat a directory");
|
||||
const tempDirName = OS.Path.join(tmpDir, "test_stat_dir.tmp.d");
|
||||
await OS.File.makeDir(tempDirName);
|
||||
|
||||
const dirInfo = await window.IOUtils.stat(tempDirName);
|
||||
is(dirInfo.size, -1, "IOUtils::stat reports -1 size for directories")
|
||||
is(fileInfo.path, tempFileName, "IOUtils::stat result contains the path");
|
||||
is(fileInfo.type, "regular", "IOUtils::stat can stat directories");
|
||||
Assert.less(
|
||||
(fileInfo.lastModified - new Date().valueOf()),
|
||||
1000, // Allow for 1 second deviation in case of slow tests.
|
||||
"IOUtils::stat can get the last modification date for a regular file"
|
||||
);
|
||||
|
||||
await cleanup(emptyFileName, tempFileName, tempDirName)
|
||||
});
|
||||
|
||||
add_task(async function test_stat_failures() {
|
||||
info("Test attempt to stat a non-existing file");
|
||||
const notExistsFile = OS.Path.join(tmpDir, "test_stat_not_exists.tmp");
|
||||
|
||||
await Assert.rejects(
|
||||
window.IOUtils.stat(notExistsFile),
|
||||
/Could not stat file\(.*\) because it does not exist/,
|
||||
"IOUtils::stat throws if the target file does not exist"
|
||||
);
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,903 +0,0 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the IOUtils file I/O API</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<!---
|
||||
This implementation is compared against an already well-tested reference
|
||||
implementation of File I/0.
|
||||
-->
|
||||
<script src="resource://gre/modules/FileTestUtils.jsm"></script>
|
||||
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
const { Assert } = ChromeUtils.import("resource://testing-common/Assert.jsm");
|
||||
const { ObjectUtils } = ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm");
|
||||
|
||||
// TODO: Remove this import for OS.File. It is currently being used as a
|
||||
// stop gap for missing IOUtils functionality.
|
||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
|
||||
const tmpDir = OS.Constants.Path.tmpDir;
|
||||
|
||||
add_task(async function test_api_is_available_on_window() {
|
||||
ok(window.IOUtils, "IOUtils is present on the window");
|
||||
});
|
||||
|
||||
add_task(async function test_read_failure() {
|
||||
const doesNotExist = OS.Path.join(tmpDir, "does_not_exist.tmp");
|
||||
await Assert.rejects(
|
||||
window.IOUtils.read(doesNotExist),
|
||||
/Could not open the file at .*/,
|
||||
"IOUtils::read rejects when file does not exist"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_make_directory() {
|
||||
info("Test creating a new directory");
|
||||
const newDirectoryName = OS.Path.join(tmpDir, "test_ioutils_new_dir.tmp.d");
|
||||
await window.IOUtils.makeDirectory(newDirectoryName);
|
||||
ok(
|
||||
await OS.File.exists(newDirectoryName),
|
||||
"IOUtils::makeDirectory can create a new directory"
|
||||
);
|
||||
|
||||
info("Test creating an existing directory");
|
||||
await window.IOUtils.makeDirectory(newDirectoryName, { ignoreExisting: true });
|
||||
ok(
|
||||
await OS.File.exists(newDirectoryName),
|
||||
"IOUtils::makeDirectory can ignore existing directories"
|
||||
);
|
||||
await Assert.rejects(
|
||||
window.IOUtils.makeDirectory(newDirectoryName, { ignoreExisting: false }),
|
||||
/Could not create directory because it already exists at .*/,
|
||||
"IOUtils::makeDirectory can throw if the target dir exists"
|
||||
)
|
||||
|
||||
info("Test creating a nested directory");
|
||||
const parentDirName = OS.Path.join(tmpDir, "test_ioutils_mkdir_parent.tmp.d");
|
||||
const nestedDirName = OS.Path.join(
|
||||
parentDirName,
|
||||
"test_ioutils_mkdir_child.tmp.d"
|
||||
);
|
||||
await Assert.rejects(
|
||||
window.IOUtils.makeDirectory(nestedDirName, { createAncestors: false }),
|
||||
/Could not create directory at .* because the path has missing ancestor components/,
|
||||
"IOUtils::makeDirectory can fail if the target is missing parents"
|
||||
);
|
||||
ok(!await OS.File.exists(nestedDirName), `Expected ${nestedDirName} not to exist`);
|
||||
await window.IOUtils.makeDirectory(nestedDirName, { createAncestors: true });
|
||||
ok(
|
||||
await OS.File.exists(nestedDirName),
|
||||
"IOUtils::makeDirectory can create ancestors of the target directory"
|
||||
);
|
||||
|
||||
await cleanup(newDirectoryName, parentDirName);
|
||||
});
|
||||
|
||||
add_task(async function test_make_directory_failure() {
|
||||
info("Try to create a directory where a file already exists");
|
||||
const notADirFileName = OS.Path.join(tmpDir, "test_ioutils_not_a_dir.tmp");
|
||||
await createFile(notADirFileName);
|
||||
|
||||
await Assert.rejects(
|
||||
window.IOUtils.makeDirectory(notADirFileName, { ignoreExisting: false }),
|
||||
/Could not create directory because the target file\(.*\) exists and is not a directory/,
|
||||
"IOUtils::makeDirectory [ignoreExisting: false] throws when the target is an existing file"
|
||||
);
|
||||
ok(await fileExists(notADirFileName), `Expected ${notADirFileName} to exist`);
|
||||
|
||||
await Assert.rejects(
|
||||
window.IOUtils.makeDirectory(notADirFileName, { ignoreExisting: true }),
|
||||
/Could not create directory because the target file\(.*\) exists and is not a directory/,
|
||||
"IOUtils::makeDirectory [ignoreExisting: true] throws when the target is an existing file"
|
||||
);
|
||||
ok(await fileExists(notADirFileName), `Expected ${notADirFileName} to exist`);
|
||||
|
||||
await cleanup(notADirFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_create_and_remove_file() {
|
||||
info("Test creating and removing a single file");
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_create_and_remove.tmp");
|
||||
await window.IOUtils.writeAtomic(tmpFileName, new Uint8Array(0));
|
||||
ok(await fileExists(tmpFileName), `Expected file ${tmpFileName} to exist`);
|
||||
|
||||
await window.IOUtils.remove(tmpFileName);
|
||||
ok(!await fileExists(tmpFileName), "IOUtils::remove can remove files");
|
||||
|
||||
info("Test creating and removing an empty directory");
|
||||
const tmpDirName = OS.Path.join(tmpDir, "test_ioutils_create_and_remove.tmp.d");
|
||||
await window.IOUtils.makeDirectory(tmpDirName);
|
||||
ok(await dirExists(tmpDirName), `Expected directory ${tmpDirName} to exist`);
|
||||
|
||||
await window.IOUtils.remove(tmpDirName);
|
||||
ok(!await dirExists(tmpDirName), "IOUtils::remove can remove empty directories");
|
||||
});
|
||||
|
||||
add_task(async function test_remove_non_existing() {
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutil_remove_non_existing.tmp");
|
||||
ok(!await fileExists(tmpFileName), `Expected file ${tmpFileName} not to exist`);
|
||||
|
||||
await window.IOUtils.remove(tmpFileName, { ignoreAbsent: true });
|
||||
ok(!await fileExists(tmpFileName), "IOUtils::remove can ignore missing files without error");
|
||||
|
||||
await Assert.rejects(
|
||||
window.IOUtils.remove(tmpFileName, { ignoreAbsent: false }),
|
||||
/Could not remove the file at .* because it does not exist/,
|
||||
"IOUtils::remove can throw an error when target file is missing"
|
||||
);
|
||||
ok(!await fileExists(tmpFileName), `Expected file ${tmpFileName} not to exist`);
|
||||
});
|
||||
|
||||
add_task(async function test_remove_recursive() {
|
||||
const tmpParentDir = OS.Path.join(tmpDir, "test_ioutils_remove.tmp.d");
|
||||
const tmpChildDir = OS.Path.join(tmpParentDir, "child.tmp.d");
|
||||
const tmpTopLevelFileName = OS.Path.join(tmpParentDir, "top.tmp");
|
||||
const tmpNestedFileName = OS.Path.join(tmpChildDir, "nested.tmp");
|
||||
await createDir(tmpChildDir);
|
||||
await createFile(tmpTopLevelFileName, "");
|
||||
await createFile(tmpNestedFileName, "");
|
||||
|
||||
ok(
|
||||
await fileExists(tmpTopLevelFileName),
|
||||
`Expected file ${tmpTopLevelFileName} to exist`
|
||||
);
|
||||
ok(
|
||||
await fileExists(tmpNestedFileName),
|
||||
`Expected file ${tmpNestedFileName} to exist`
|
||||
);
|
||||
|
||||
await Assert.rejects(
|
||||
window.IOUtils.remove(tmpParentDir, { recursive: false }),
|
||||
/Could not remove the non-empty directory at .*/,
|
||||
"IOUtils::remove fails if non-recursively removing directory with contents"
|
||||
);
|
||||
|
||||
await window.IOUtils.remove(tmpParentDir, { recursive: true });
|
||||
ok(
|
||||
!await dirExists(tmpParentDir),
|
||||
"IOUtils::remove can recursively remove a directory"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_write_no_overwrite() {
|
||||
// Make a new file, and try to write to it with overwrites disabled.
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_overwrite.tmp");
|
||||
const untouchableContents = new TextEncoder().encode("Can't touch this!\n");
|
||||
await window.IOUtils.writeAtomic(tmpFileName, untouchableContents);
|
||||
|
||||
const newContents = new TextEncoder().encode("Nah nah nah!\n");
|
||||
await Assert.rejects(
|
||||
window.IOUtils.writeAtomic(tmpFileName, newContents, {
|
||||
noOverwrite: true,
|
||||
}),
|
||||
/Refusing to overwrite the file at */,
|
||||
"IOUtils::writeAtomic rejects writing to existing file if overwrites are disabled"
|
||||
);
|
||||
ok(
|
||||
await fileHasBinaryContents(tmpFileName, untouchableContents),
|
||||
"IOUtils::writeAtomic doesn't change target file when overwrite is refused"
|
||||
);
|
||||
|
||||
const bytesWritten = await window.IOUtils.writeAtomic(
|
||||
tmpFileName,
|
||||
newContents,
|
||||
{ noOverwrite: false /* Default. */ }
|
||||
);
|
||||
is(
|
||||
bytesWritten,
|
||||
newContents.length,
|
||||
"IOUtils::writeAtomic can overwrite files if specified"
|
||||
);
|
||||
|
||||
await cleanup(tmpFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_write_with_backup() {
|
||||
info("Test backup file option with non-existing file");
|
||||
let fileContents = new TextEncoder().encode("Original file contents");
|
||||
let destFileName = OS.Path.join(tmpDir, "test_write_with_backup_option.tmp");
|
||||
let backupFileName = destFileName + ".backup";
|
||||
let bytesWritten =
|
||||
await window.IOUtils.writeAtomic(destFileName, fileContents, {
|
||||
backupFile: backupFileName,
|
||||
});
|
||||
ok(
|
||||
await fileHasTextContents(destFileName, "Original file contents"),
|
||||
"IOUtils::writeAtomic creates a new file with the correct contents"
|
||||
);
|
||||
ok(
|
||||
!await fileExists(backupFileName),
|
||||
"IOUtils::writeAtomic does not create a backup if the target file does not exist"
|
||||
);
|
||||
is(
|
||||
bytesWritten,
|
||||
fileContents.length,
|
||||
"IOUtils::writeAtomic correctly writes to a new file without performing a backup"
|
||||
);
|
||||
|
||||
info("Test backup file option with existing destination");
|
||||
let newFileContents = new TextEncoder().encode("New file contents");
|
||||
ok(await fileExists(destFileName), `Expected ${destFileName} to exist`);
|
||||
bytesWritten =
|
||||
await window.IOUtils.writeAtomic(destFileName, newFileContents, {
|
||||
backupFile: backupFileName,
|
||||
});
|
||||
ok(
|
||||
await fileHasTextContents(backupFileName, "Original file contents"),
|
||||
"IOUtils::writeAtomic can backup an existing file before writing"
|
||||
);
|
||||
ok(
|
||||
await fileHasTextContents(destFileName, "New file contents"),
|
||||
"IOUtils::writeAtomic can create the target with the correct contents"
|
||||
);
|
||||
is(
|
||||
bytesWritten,
|
||||
newFileContents.length,
|
||||
"IOUtils::writeAtomic correctly writes to the target after taking a backup"
|
||||
);
|
||||
|
||||
await cleanup(destFileName, backupFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_write_with_backup_and_tmp() {
|
||||
info("Test backup with tmp and backup file options, non-existing destination");
|
||||
let fileContents = new TextEncoder().encode("Original file contents");
|
||||
let destFileName = OS.Path.join(tmpDir, "test_write_with_backup_and_tmp_options.tmp");
|
||||
let backupFileName = destFileName + ".backup";
|
||||
let tmpFileName = OS.Path.join(tmpDir, "temp_file.tmp");
|
||||
let bytesWritten =
|
||||
await window.IOUtils.writeAtomic(destFileName, fileContents, {
|
||||
backupFile: backupFileName,
|
||||
tmpPath: tmpFileName,
|
||||
});
|
||||
ok(!await fileExists(tmpFileName), "IOUtils::writeAtomic cleans up the tmpFile");
|
||||
ok(
|
||||
!await fileExists(backupFileName),
|
||||
"IOUtils::writeAtomic does not create a backup if the target file does not exist"
|
||||
);
|
||||
ok(
|
||||
await fileHasTextContents(destFileName, "Original file contents"),
|
||||
"IOUtils::writeAtomic can write to the destination when a temporary file is used"
|
||||
);
|
||||
is(
|
||||
bytesWritten,
|
||||
fileContents.length,
|
||||
"IOUtils::writeAtomic can copy tmp file to destination without performing a backup"
|
||||
);
|
||||
|
||||
info("Test backup with tmp and backup file options, existing destination");
|
||||
let newFileContents = new TextEncoder().encode("New file contents");
|
||||
bytesWritten =
|
||||
await window.IOUtils.writeAtomic(destFileName, newFileContents, {
|
||||
backupFile: backupFileName,
|
||||
tmpPath: tmpFileName,
|
||||
});
|
||||
|
||||
ok(!await fileExists(tmpFileName), "IOUtils::writeAtomic cleans up the tmpFile");
|
||||
ok(
|
||||
await fileHasTextContents(backupFileName, "Original file contents"),
|
||||
"IOUtils::writeAtomic can create a backup if the target file exists"
|
||||
);
|
||||
ok(
|
||||
await fileHasTextContents(destFileName, "New file contents"),
|
||||
"IOUtils::writeAtomic can write to the destination when a temporary file is used"
|
||||
);
|
||||
is(
|
||||
bytesWritten,
|
||||
newFileContents.length,
|
||||
"IOUtils::writeAtomic IOUtils::writeAtomic can move tmp file to destination after performing a backup"
|
||||
);
|
||||
|
||||
await cleanup(destFileName, backupFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_partial_read() {
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_partial_read.tmp");
|
||||
const bytes = Uint8Array.of(...new Array(50).keys());
|
||||
const bytesWritten = await window.IOUtils.writeAtomic(tmpFileName, bytes);
|
||||
is(
|
||||
bytesWritten,
|
||||
50,
|
||||
"IOUtils::writeAtomic can write entire byte array to file"
|
||||
);
|
||||
|
||||
// Read just the first 10 bytes.
|
||||
const first10 = bytes.slice(0, 10);
|
||||
const bytes10 = await window.IOUtils.read(tmpFileName, 10);
|
||||
ok(
|
||||
ObjectUtils.deepEqual(bytes10, first10),
|
||||
"IOUtils::read can read part of a file, up to specified max bytes"
|
||||
);
|
||||
|
||||
// Trying to explicitly read nothing isn't useful, but it should still
|
||||
// succeed.
|
||||
const bytes0 = await window.IOUtils.read(tmpFileName, 0);
|
||||
is(bytes0.length, 0, "IOUtils::read can read 0 bytes");
|
||||
|
||||
await cleanup(tmpFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_empty_read_and_write() {
|
||||
// Trying to write an empty file isn't very useful, but it should still
|
||||
// succeed.
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_empty.tmp");
|
||||
const emptyByteArray = new Uint8Array(0);
|
||||
const bytesWritten = await window.IOUtils.writeAtomic(
|
||||
tmpFileName,
|
||||
emptyByteArray
|
||||
);
|
||||
is(bytesWritten, 0, "IOUtils::writeAtomic can create an empty file");
|
||||
|
||||
// Trying to explicitly read nothing isn't useful, but it should still
|
||||
// succeed.
|
||||
const bytes0 = await window.IOUtils.read(tmpFileName, 0);
|
||||
is(bytes0.length, 0, "IOUtils::read can read 0 bytes");
|
||||
|
||||
// Implicitly try to read nothing.
|
||||
const nothing = await window.IOUtils.read(tmpFileName);
|
||||
is(nothing.length, 0, "IOUtils:: read can read empty files");
|
||||
|
||||
await cleanup(tmpFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_full_read_and_write() {
|
||||
// Write a file.
|
||||
info("Test writing to a new binary file");
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_numbers.tmp");
|
||||
const bytes = Uint8Array.of(...new Array(50).keys());
|
||||
const bytesWritten = await window.IOUtils.writeAtomic(tmpFileName, bytes);
|
||||
is(
|
||||
bytesWritten,
|
||||
50,
|
||||
"IOUtils::writeAtomic can write entire byte array to file"
|
||||
);
|
||||
|
||||
// Read it back.
|
||||
info("Test reading a binary file");
|
||||
let fileContents = await window.IOUtils.read(tmpFileName);
|
||||
ok(
|
||||
ObjectUtils.deepEqual(bytes, fileContents) &&
|
||||
bytes.length == fileContents.length,
|
||||
"IOUtils::read can read back entire file"
|
||||
);
|
||||
|
||||
const tooManyBytes = bytes.length + 1;
|
||||
fileContents = await window.IOUtils.read(tmpFileName, tooManyBytes);
|
||||
ok(
|
||||
ObjectUtils.deepEqual(bytes, fileContents) &&
|
||||
fileContents.length == bytes.length,
|
||||
"IOUtils::read can read entire file when requested maxBytes is too large"
|
||||
);
|
||||
|
||||
// Clean up.
|
||||
await cleanup(tmpFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_write_relative_path() {
|
||||
const tmpFileName = "test_ioutils_write_relative_path.tmp";
|
||||
const bytes = Uint8Array.of(...new Array(50).keys());
|
||||
|
||||
info("Test writing a file at a relative destination");
|
||||
await Assert.rejects(
|
||||
window.IOUtils.writeAtomic(tmpFileName, bytes),
|
||||
/Refusing to work with path\(.*\) because only absolute file paths are permitted/,
|
||||
"IOUtils::writeAtomic only works with absolute paths"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_read_relative_path() {
|
||||
const tmpFileName = "test_ioutils_read_relative_path.tmp";
|
||||
|
||||
info("Test reading a file at a relative destination");
|
||||
await Assert.rejects(
|
||||
window.IOUtils.read(tmpFileName),
|
||||
/Refusing to work with path\(.*\) because only absolute file paths are permitted/,
|
||||
"IOUtils::writeAtomic only works with absolute paths"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_move_relative_path() {
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_move_relative_path.tmp");
|
||||
const dest = "relative_to_cwd.tmp";
|
||||
await createFile(tmpFileName, "source");
|
||||
|
||||
info("Test moving a file to a relative destination");
|
||||
await Assert.rejects(
|
||||
window.IOUtils.move(tmpFileName, dest),
|
||||
/Refusing to work with path\(.*\) because only absolute file paths are permitted/,
|
||||
"IOUtils::move only works with absolute paths"
|
||||
);
|
||||
ok(
|
||||
await fileHasTextContents(tmpFileName, "source"),
|
||||
"IOUtils::move doesn't change source file when move fails"
|
||||
);
|
||||
|
||||
await cleanup(tmpFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_move_rename() {
|
||||
// Set up.
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_move_src.tmp");
|
||||
const destFileName = OS.Path.join(tmpDir, "test_ioutils_move_dest.tmp");
|
||||
await createFile(tmpFileName, "dest");
|
||||
// Test.
|
||||
info("Test move to new file in same directory");
|
||||
await window.IOUtils.move(tmpFileName, destFileName);
|
||||
info(`Moved ${tmpFileName} to ${destFileName}`);
|
||||
ok(
|
||||
!await fileExists(tmpFileName)
|
||||
&& await fileHasTextContents(destFileName, "dest"),
|
||||
"IOUtils::move can move source to dest in same directory"
|
||||
)
|
||||
|
||||
// Set up.
|
||||
info("Test move to existing file with no overwrite");
|
||||
await createFile(tmpFileName, "source");
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.move(tmpFileName, destFileName, { noOverwrite: true }),
|
||||
/Could not move source file\(.*\) to destination\(.*\) because the destination already exists and overwrites are not allowed/,
|
||||
"IOUtils::move will refuse to move a file if overwrites are disabled"
|
||||
);
|
||||
ok(
|
||||
await fileExists(tmpFileName)
|
||||
&& await fileHasTextContents(destFileName, "dest"),
|
||||
"Failed IOUtils::move doesn't move the source file"
|
||||
);
|
||||
|
||||
// Test.
|
||||
info("Test move to existing file with overwrite");
|
||||
await window.IOUtils.move(tmpFileName, destFileName, { noOverwrite: false });
|
||||
ok(!await fileExists(tmpFileName), "IOUtils::move moved source");
|
||||
ok(
|
||||
await fileHasTextContents(destFileName, "source"),
|
||||
"IOUtils::move overwrote the destination with the source"
|
||||
);
|
||||
|
||||
// Clean up.
|
||||
await cleanup(tmpFileName, destFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_move_to_dir() {
|
||||
// Set up.
|
||||
info("Test move and rename to non-existing directory");
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_move_to_dir.tmp");
|
||||
const destDir = OS.Path.join(tmpDir, "test_move_to_dir.tmp.d");
|
||||
const dest = OS.Path.join(destDir, "dest.tmp");
|
||||
await createFile(tmpFileName);
|
||||
// Test.
|
||||
ok(!await fileOrDirExists(destDir), "Expected path not to exist");
|
||||
await window.IOUtils.move(tmpFileName, dest);
|
||||
ok(
|
||||
!await fileExists(tmpFileName) && await fileExists(dest),
|
||||
"IOUtils::move creates non-existing parents if needed"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test move and rename to existing directory.")
|
||||
await createFile(tmpFileName);
|
||||
// Test.
|
||||
ok(await dirExists(destDir), `Expected ${destDir} to be a directory`);
|
||||
await window.IOUtils.move(tmpFileName, dest);
|
||||
ok(
|
||||
!await fileExists(tmpFileName)
|
||||
&& await fileExists(dest),
|
||||
"IOUtils::move can move/rename a file into an existing dir"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test move to existing directory without specifying leaf name.")
|
||||
await createFile(tmpFileName);
|
||||
// Test.
|
||||
await window.IOUtils.move(tmpFileName, destDir);
|
||||
ok(await dirExists(destDir), `Expected ${destDir} to be a directory`);
|
||||
ok(
|
||||
!await fileExists(tmpFileName)
|
||||
&& await fileExists(OS.Path.join(destDir, OS.Path.basename(tmpFileName))),
|
||||
"IOUtils::move can move a file into an existing dir"
|
||||
);
|
||||
|
||||
// Clean up.
|
||||
await cleanup(destDir);
|
||||
});
|
||||
|
||||
add_task(async function test_move_dir() {
|
||||
// Set up.
|
||||
info("Test rename an empty directory");
|
||||
const srcDir = OS.Path.join(tmpDir, "test_move_dir.tmp.d");
|
||||
const destDir = OS.Path.join(tmpDir, "test_move_dir_dest.tmp.d");
|
||||
await createDir(srcDir);
|
||||
// Test.
|
||||
await window.IOUtils.move(srcDir, destDir);
|
||||
ok(
|
||||
!await fileOrDirExists(srcDir) && await dirExists(destDir),
|
||||
"IOUtils::move can rename directories"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test move directory and its content into another directory");
|
||||
await createDir(srcDir);
|
||||
await createFile(OS.Path.join(srcDir, "file.tmp"), "foo");
|
||||
// Test.
|
||||
await window.IOUtils.move(srcDir, destDir);
|
||||
const destFile = OS.Path.join(destDir, OS.Path.basename(srcDir), "file.tmp");
|
||||
ok(
|
||||
!await fileOrDirExists(srcDir)
|
||||
&& await dirExists(destDir)
|
||||
&& await dirExists(OS.Path.join(destDir, OS.Path.basename(srcDir)))
|
||||
&& await fileHasTextContents(destFile, "foo"),
|
||||
"IOUtils::move can move a directory and its contents into another one"
|
||||
)
|
||||
|
||||
// Clean up.
|
||||
await cleanup(srcDir, destDir);
|
||||
});
|
||||
|
||||
add_task(async function test_move_failures() {
|
||||
// Set up.
|
||||
info("Test attempt to rename a non-existent source file");
|
||||
const notExistsSrc = OS.Path.join(tmpDir, "not_exists_src.tmp");
|
||||
const notExistsDest = OS.Path.join(tmpDir, "not_exists_dest.tmp");
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.move(notExistsSrc, notExistsDest),
|
||||
/Could not move source file\(.*\) because it does not exist/,
|
||||
"IOUtils::move throws if source file does not exist"
|
||||
);
|
||||
ok(
|
||||
!await fileExists(notExistsSrc) && !await fileExists(notExistsDest),
|
||||
"IOUtils::move fails if source file does not exist"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test attempt to move a directory to a file");
|
||||
const destFile = OS.Path.join(tmpDir, "test_move_failures_file_dest.tmp");
|
||||
const srcDir = OS.Path.join(tmpDir, "test_move_failure_src.tmp.d");
|
||||
await createFile(destFile);
|
||||
await createDir(srcDir);
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.move(srcDir, destFile),
|
||||
/Could not move the source directory\(.*\) to the destination\(.*\) because the destination is not a directory/,
|
||||
"IOUtils::move throws if try to move dir into an existing file"
|
||||
);
|
||||
|
||||
// Clean up.
|
||||
await cleanup(destFile, srcDir);
|
||||
});
|
||||
|
||||
add_task(async function test_stat() {
|
||||
info("Test attempt to stat a regular empty file");
|
||||
const emptyFileName = OS.Path.join(tmpDir, "test_stat_empty.tmp");
|
||||
await createFile(emptyFileName);
|
||||
|
||||
const emptyFileInfo = await window.IOUtils.stat(emptyFileName);
|
||||
is(emptyFileInfo.size, 0, "IOUtils::stat can get correct (empty) file size");
|
||||
is(emptyFileInfo.path, emptyFileName, "IOUtils::stat result contains the path");
|
||||
is(emptyFileInfo.type, "regular", "IOUtils::stat can stat regular (empty) files");
|
||||
Assert.less(
|
||||
(emptyFileInfo.lastModified - new Date().valueOf()),
|
||||
1000, // Allow for 1 second deviation in case of slow tests.
|
||||
"IOUtils::stat can get the last modification date for a regular file"
|
||||
);
|
||||
|
||||
info("Test attempt to stat a regular binary file");
|
||||
const tempFileName = OS.Path.join(tmpDir, "test_stat_binary.tmp");
|
||||
const bytes = Uint8Array.of(...new Array(50).keys());
|
||||
await createFile(tempFileName, bytes);
|
||||
|
||||
const fileInfo = await window.IOUtils.stat(tempFileName);
|
||||
is(fileInfo.size, 50, "IOUtils::stat can get correct file size");
|
||||
is(fileInfo.path, tempFileName, "IOUtils::stat result contains the path");
|
||||
is(fileInfo.type, "regular", "IOUtils::stat can stat regular files");
|
||||
Assert.less(
|
||||
(fileInfo.lastModified - new Date().valueOf()),
|
||||
1000, // Allow for 1 second deviation in case of slow tests.
|
||||
"IOUtils::stat can get the last modification date for a regular file"
|
||||
);
|
||||
|
||||
info("Test attempt to stat a directory");
|
||||
const tempDirName = OS.Path.join(tmpDir, "test_stat_dir.tmp.d");
|
||||
await OS.File.makeDir(tempDirName);
|
||||
|
||||
const dirInfo = await window.IOUtils.stat(tempDirName);
|
||||
is(dirInfo.size, -1, "IOUtils::stat reports -1 size for directories")
|
||||
is(fileInfo.path, tempFileName, "IOUtils::stat result contains the path");
|
||||
is(fileInfo.type, "regular", "IOUtils::stat can stat directories");
|
||||
Assert.less(
|
||||
(fileInfo.lastModified - new Date().valueOf()),
|
||||
1000, // Allow for 1 second deviation in case of slow tests.
|
||||
"IOUtils::stat can get the last modification date for a regular file"
|
||||
);
|
||||
|
||||
await cleanup(emptyFileName, tempFileName, tempDirName)
|
||||
});
|
||||
|
||||
add_task(async function test_stat_failures() {
|
||||
info("Test attempt to stat a non-existing file");
|
||||
const notExistsFile = OS.Path.join(tmpDir, "test_stat_not_exists.tmp");
|
||||
|
||||
await Assert.rejects(
|
||||
window.IOUtils.stat(notExistsFile),
|
||||
/Could not stat file\(.*\) because it does not exist/,
|
||||
"IOUtils::stat throws if the target file does not exist"
|
||||
);
|
||||
});
|
||||
|
||||
add_task(async function test_copy_relative_path() {
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_copy_relative_path.tmp");
|
||||
const dest = "relative_to_cwd.tmp";
|
||||
await createFile(tmpFileName, "source");
|
||||
|
||||
info("Test copy a file to a relative destination");
|
||||
await Assert.rejects(
|
||||
window.IOUtils.copy(tmpFileName, dest),
|
||||
/Refusing to work with path\(.*\) because only absolute file paths are permitted/,
|
||||
"IOUtils::copy only works with absolute paths"
|
||||
);
|
||||
ok(
|
||||
await fileHasTextContents(tmpFileName, "source"),
|
||||
"IOUtils::copy doesn't change source file when copy fails"
|
||||
);
|
||||
|
||||
await cleanup(tmpFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_copy() {
|
||||
// Set up.
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_ioutils_orig.tmp");
|
||||
const destFileName = OS.Path.join(tmpDir, "test_ioutils_copy.tmp");
|
||||
await createFile(tmpFileName, "original");
|
||||
// Test.
|
||||
info("Test copy to new file in same directory");
|
||||
await window.IOUtils.copy(tmpFileName, destFileName);
|
||||
ok(
|
||||
await fileExists(tmpFileName)
|
||||
&& await fileHasTextContents(destFileName, "original"),
|
||||
"IOUtils::copy can copy source to dest in same directory"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test copy to existing file with no overwrite");
|
||||
await createFile(tmpFileName, "new contents");
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.copy(tmpFileName, destFileName, { noOverwrite: true }),
|
||||
/Could not copy source file\(.*\) to destination\(.*\) because the destination already exists and overwrites are not allowed/,
|
||||
"IOUtils::copy will refuse to copy to existing destination if overwrites are disabled"
|
||||
);
|
||||
ok(
|
||||
await fileExists(tmpFileName)
|
||||
&& await fileHasTextContents(destFileName, "original"),
|
||||
"Failed IOUtils::move doesn't move the source file"
|
||||
);
|
||||
|
||||
// Test.
|
||||
info("Test copy to existing file with overwrite");
|
||||
await window.IOUtils.copy(tmpFileName, destFileName, { noOverwrite: false });
|
||||
ok(await fileExists(tmpFileName), "IOUtils::copy retains source");
|
||||
ok(
|
||||
await fileHasTextContents(destFileName, "new contents"),
|
||||
"IOUtils::copy overwrote the destination with the source"
|
||||
);
|
||||
|
||||
// Clean up.
|
||||
await cleanup(tmpFileName, destFileName);
|
||||
});
|
||||
|
||||
add_task(async function test_copy_file_to_dir() {
|
||||
// Set up.
|
||||
info("Test copy file to non-existing directory");
|
||||
const tmpFileName = OS.Path.join(tmpDir, "test_copy_file_to_dir.tmp");
|
||||
const destDir = OS.Path.join(tmpDir, "test_copy_file_to_dir.tmp.d");
|
||||
const dest = OS.Path.join(destDir, "dest.tmp");
|
||||
await createFile(tmpFileName);
|
||||
// Test.
|
||||
ok(!await fileOrDirExists(destDir), "Expected path not to exist");
|
||||
await window.IOUtils.copy(tmpFileName, dest);
|
||||
ok(
|
||||
await fileExists(tmpFileName) && await fileExists(dest),
|
||||
"IOUtils::copy creates non-existing parents if needed"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test copy file to existing directory")
|
||||
await createFile(tmpFileName);
|
||||
// Test.
|
||||
ok(await dirExists(destDir), `Expected ${destDir} to be a directory`);
|
||||
await window.IOUtils.copy(tmpFileName, dest);
|
||||
ok(
|
||||
await fileExists(tmpFileName)
|
||||
&& await fileExists(dest),
|
||||
"IOUtils::copy can copy a file into an existing dir"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test copy file to existing directory without specifying leaf name")
|
||||
await createFile(tmpFileName);
|
||||
// Test.
|
||||
await window.IOUtils.copy(tmpFileName, destDir);
|
||||
ok(await dirExists(destDir), `Expected ${destDir} to be a directory`);
|
||||
ok(
|
||||
await fileExists(tmpFileName)
|
||||
&& await fileExists(OS.Path.join(destDir, OS.Path.basename(tmpFileName))),
|
||||
"IOUtils::copy can copy a file into an existing dir"
|
||||
);
|
||||
|
||||
// Clean up.
|
||||
await cleanup(tmpFileName, destDir);
|
||||
});
|
||||
|
||||
add_task(async function test_copy_dir_recursive() {
|
||||
// Set up.
|
||||
info("Test rename an empty directory");
|
||||
const srcDir = OS.Path.join(tmpDir, "test_copy_dir.tmp.d");
|
||||
const destDir = OS.Path.join(tmpDir, "test_copy_dir_dest.tmp.d");
|
||||
await createDir(srcDir);
|
||||
// Test.
|
||||
await window.IOUtils.copy(srcDir, destDir, { recursive: true });
|
||||
ok(
|
||||
await dirExists(srcDir) && await dirExists(destDir),
|
||||
"IOUtils::copy can recursively copy entire directories"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test copy directory and its content into another directory");
|
||||
await createDir(srcDir);
|
||||
await createFile(OS.Path.join(srcDir, "file.tmp"), "foo");
|
||||
// Test.
|
||||
await window.IOUtils.copy(srcDir, destDir, { recursive: true });
|
||||
const destFile = OS.Path.join(destDir, OS.Path.basename(srcDir), "file.tmp");
|
||||
ok(
|
||||
await dirExists(srcDir)
|
||||
&& await dirExists(destDir)
|
||||
&& await dirExists(OS.Path.join(destDir, OS.Path.basename(srcDir)))
|
||||
&& await fileHasTextContents(destFile, "foo"),
|
||||
"IOUtils::copy can move a directory and its contents into another one"
|
||||
)
|
||||
|
||||
// Clean up.
|
||||
await cleanup(srcDir, destDir);
|
||||
});
|
||||
|
||||
add_task(async function test_copy_failures() {
|
||||
// Set up.
|
||||
info("Test attempt to copy a non-existent source file");
|
||||
const notExistsSrc = OS.Path.join(tmpDir, "test_copy_not_exists_src.tmp");
|
||||
const notExistsDest = OS.Path.join(tmpDir, "test_copy_not_exists_dest.tmp");
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.copy(notExistsSrc, notExistsDest),
|
||||
/Could not copy source file\(.*\) because it does not exist/,
|
||||
"IOUtils::copy throws if source file does not exist"
|
||||
);
|
||||
ok(
|
||||
!await fileExists(notExistsSrc) && !await fileExists(notExistsDest),
|
||||
"IOUtils::copy failure due to missing source file does not affect destination"
|
||||
);
|
||||
|
||||
// Set up.
|
||||
info("Test attempt to copy a directory to a file");
|
||||
const destFile = OS.Path.join(tmpDir, "test_copy_failures_file_dest.tmp");
|
||||
const srcDir = OS.Path.join(tmpDir, "test_copy_failure_src.tmp.d");
|
||||
await createFile(destFile);
|
||||
await createDir(srcDir);
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.copy(srcDir, destFile, { recursive: true }),
|
||||
/Could not copy the source directory\(.*\) to the destination\(.*\) because the destination is not a directory/,
|
||||
"IOUtils::copy throws if try to move dir into an existing file"
|
||||
);
|
||||
ok(await fileHasTextContents(destFile, ""), "IOUtils::copy failure does not affect destination");
|
||||
|
||||
// Set up.
|
||||
info("Test copy directory without recursive option");
|
||||
await createDir(srcDir);
|
||||
// Test.
|
||||
await Assert.rejects(
|
||||
window.IOUtils.copy(srcDir, notExistsDest, { recursive: false }),
|
||||
/Refused to copy source directory\(.*\) to the destination\(.*\)/,
|
||||
"IOUtils::copy throws if try to copy a directory with { recursive: false }"
|
||||
);
|
||||
console.log(`${notExistsDest} exists?`, await fileOrDirExists(notExistsDest))
|
||||
ok(!await fileOrDirExists(notExistsDest), "IOUtils::copy failure does not affect destination");
|
||||
|
||||
// Clean up.
|
||||
await cleanup(destFile, srcDir);
|
||||
});
|
||||
|
||||
|
||||
// Utility functions.
|
||||
|
||||
Uint8Array.prototype.equals = function equals(other) {
|
||||
if (this.byteLength !== other.byteLength) return false;
|
||||
return this.every((val, i) => val === other[i]);
|
||||
}
|
||||
|
||||
async function createFile(location, contents = "") {
|
||||
if (typeof contents === "string") {
|
||||
contents = new TextEncoder().encode(contents);
|
||||
}
|
||||
await window.IOUtils.writeAtomic(location, contents);
|
||||
const exists = await fileExists(location);
|
||||
ok(exists, `Created temporary file at: ${location}`);
|
||||
}
|
||||
|
||||
async function createDir(location) {
|
||||
await window.IOUtils.makeDirectory(location, { ignoreExisting: true, createAncestors: true });
|
||||
const exists = await OS.File.exists(location);
|
||||
ok(exists, `Created temporary directory at: ${location}`);
|
||||
}
|
||||
|
||||
async function fileHasBinaryContents(location, expectedContents) {
|
||||
if (!(expectedContents instanceof Uint8Array)) {
|
||||
throw new TypeError("expectedContents must be a byte array");
|
||||
}
|
||||
info(`Opening ${location} for reading`);
|
||||
const bytes = await window.IOUtils.read(location);
|
||||
return bytes.equals(expectedContents);
|
||||
}
|
||||
|
||||
async function fileHasTextContents(location, expectedContents) {
|
||||
if (typeof expectedContents !== "string") {
|
||||
throw new TypeError("expectedContents must be a string");
|
||||
}
|
||||
info(`Opening ${location} for reading`);
|
||||
const bytes = await window.IOUtils.read(location);
|
||||
const contents = new TextDecoder().decode(bytes);
|
||||
return contents === expectedContents;
|
||||
}
|
||||
|
||||
async function fileExists(file) {
|
||||
try {
|
||||
let { type } = await window.IOUtils.stat(file);
|
||||
return type === "regular";
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function dirExists(dir) {
|
||||
try {
|
||||
let { type } = await window.IOUtils.stat(dir);
|
||||
return type === "directory";
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fileOrDirExists(location) {
|
||||
try {
|
||||
await window.IOUtils.stat(location);
|
||||
return true;
|
||||
} catch (ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function cleanup(...files) {
|
||||
for (const file of files) {
|
||||
await window.IOUtils.remove(file, { ignoreAbsent: true, recursive: true });
|
||||
const exists = await fileOrDirExists(file);
|
||||
ok(!exists, `Removed temporary file: ${file}`);
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче