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:
Keefer Rourke 2020-08-10 16:10:40 +00:00
Родитель 1743e71bc9
Коммит fd57fb1ae0
13 изменённых файлов: 1056 добавлений и 962 удалений

Просмотреть файл

@ -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>