// This is an impossible sequence of bytes in an UTF-8 encoded file.
// See section3.5.3 of this text:
// https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
const invalidUTF8 = Uint8Array.of(0xfe, 0xfe, 0xff, 0xff);
add_task(async function test_read_utf8_failure() {
info("Test attempt to read non-existent file (UTF8)");
const doesNotExist = PathUtils.join(PathUtils.tempDir, "does_not_exist.tmp");
await Assert.rejects(
IOUtils.readUTF8(doesNotExist),
/NotFoundError: Could not open `.*'/, "IOUtils::readUTF8 rejects when file does not exist"
);
// Deliberately write the invalid byte sequence to file.
await IOUtils.write(invalidUTF8File, invalidUTF8);
await Assert.rejects(
IOUtils.readUTF8(invalidUTF8File),
/NotReadableError: Could not read `.*': file is not UTF-8 encoded/, "IOUtils::readUTF8 will reject when reading a file that is not valid UTF-8"
);
await cleanup(invalidUTF8File);
});
add_task(async function test_write_utf8_no_overwrite() {
// Make a new file, and try to write to it with overwrites disabled.
const tmpFileName = PathUtils.join(PathUtils.tempDir, "test_ioutils_write_utf8_overwrite.tmp");
const untouchableContents = "Can't touch this!\n";
await IOUtils.writeUTF8(tmpFileName, untouchableContents);
const newContents = "Nah nah nah!\n";
await Assert.rejects(
IOUtils.writeUTF8(tmpFileName, newContents, {
mode: "create",
}),
/NoModificationAllowedError: Could not write to `.*': refusing to overwrite file, `mode' is not "overwrite"/, "IOUtils::writeUTF8 rejects writing to existing file if overwrites are disabled"
);
ok(
await fileHasTextContents(tmpFileName, untouchableContents), "IOUtils::writeUTF8 doesn't change target file when overwrite is refused"
);
const bytesWritten = await IOUtils.writeUTF8(
tmpFileName,
newContents,
{ mode: "overwrite" }
);
is(
bytesWritten,
newContents.length, "IOUtils::writeUTF8 can overwrite files if specified"
);
ok(
await fileHasTextContents(tmpFileName, newContents), "IOUtils::writeUTF8 overwrites with the expected contents"
);
await cleanup(tmpFileName);
});
add_task(async function test_write_with_backup() {
info("Test backup file option with non-existing file");
let fileContents = "Original file contents";
let destFileName = PathUtils.join(PathUtils.tempDir, "test_write_utf8_with_backup_option.tmp");
let backupFileName = destFileName + ".backup";
let bytesWritten =
await IOUtils.writeUTF8(destFileName, fileContents, {
backupFile: backupFileName,
});
ok(
await fileHasTextContents(destFileName, "Original file contents"), "IOUtils::writeUTF8 creates a new file with the correct contents"
);
ok(
!await fileExists(backupFileName), "IOUtils::writeUTF8 does not create a backup if the target file does not exist"
);
is(
bytesWritten,
fileContents.length, "IOUtils::write correctly writes to a new file without performing a backup"
);
info("Test backup file option with existing destination");
let newFileContents = "New file contents";
ok(await fileExists(destFileName), `Expected ${destFileName} to exist`);
bytesWritten =
await IOUtils.writeUTF8(destFileName, newFileContents, {
backupFile: backupFileName,
});
ok(
await fileHasTextContents(backupFileName, "Original file contents"), "IOUtils::writeUTF8 can backup an existing file before writing"
);
ok(
await fileHasTextContents(destFileName, "New file contents"), "IOUtils::writeUTF8 can create the target with the correct contents"
);
is(
bytesWritten,
newFileContents.length, "IOUtils::writeUTF8 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 = "Original file contents";
let destFileName = PathUtils.join(PathUtils.tempDir, "test_write_utf8_with_backup_and_tmp_options.tmp");
let backupFileName = destFileName + ".backup";
let tmpFileName = PathUtils.join(PathUtils.tempDir, "temp_file.tmp");
let bytesWritten =
await IOUtils.writeUTF8(destFileName, fileContents, {
backupFile: backupFileName,
tmpPath: tmpFileName,
});
ok(!await fileExists(tmpFileName), "IOUtils::writeUTF8 cleans up the tmpFile");
ok(
!await fileExists(backupFileName), "IOUtils::writeUTF8 does not create a backup if the target file does not exist"
);
ok(
await fileHasTextContents(destFileName, "Original file contents"), "IOUtils::writeUTF8 can write to the destination when a temporary file is used"
);
is(
bytesWritten,
fileContents.length, "IOUtils::writeUTF8 can copy tmp file to destination without performing a backup"
);
info("Test backup with tmp and backup file options, existing destination");
let newFileContents = "New file contents";
bytesWritten =
await IOUtils.writeUTF8(destFileName, newFileContents, {
backupFile: backupFileName,
tmpPath: tmpFileName,
});
ok(!await fileExists(tmpFileName), "IOUtils::writeUTF8 cleans up the tmpFile");
ok(
await fileHasTextContents(backupFileName, "Original file contents"), "IOUtils::writeUTF8 can create a backup if the target file exists"
);
ok(
await fileHasTextContents(destFileName, "New file contents"), "IOUtils::writeUTF8 can write to the destination when a temporary file is used"
);
is(
bytesWritten,
newFileContents.length, "IOUtils::writeUTF8 can move tmp file to destination after performing a backup"
);
await cleanup(destFileName, backupFileName);
});
add_task(async function test_empty_read_and_write_utf8() {
const tmpFileName = PathUtils.join(PathUtils.tempDir, "test_ioutils_empty_utf8.tmp");
const emptyString = ""
const bytesWritten = await IOUtils.writeUTF8(
tmpFileName,
emptyString
);
is(bytesWritten, 0, "IOUtils::writeUTF8 can create an empty file");
add_task(async function test_full_read_and_write_utf8() {
// Write a file.
info("Test writing emoji file");
const tmpFileName = PathUtils.join(PathUtils.tempDir, "test_ioutils_emoji.tmp");
// Make sure non-ASCII text is supported for writing and reading back.
// For fun, a sampling of space-separated emoji characters from different
// Unicode versions, including multi-byte glyphs that are rendered using
// ZWJ sequences.
const emoji = "☕️ ⚧️ ️ ☠️ ";
const expectedBytes = 71;
const bytesWritten = await IOUtils.writeUTF8(tmpFileName, emoji);
is(
bytesWritten,
expectedBytes, "IOUtils::writeUTF8 can write emoji to file"
);
// Read it back.
info("Test reading emoji from file");
let fileContents = await IOUtils.readUTF8(tmpFileName);
ok(
emoji == fileContents &&
emoji.length == fileContents.length, "IOUtils::readUTF8 can read back entire file"
);
// Clean up.
await cleanup(tmpFileName);
});
add_task(async function test_write_utf8_relative_path() {
const tmpFileName = "test_ioutils_write_utf8_relative_path.tmp";
info("Test writing a file at a relative destination");
await Assert.rejects(
IOUtils.writeUTF8(tmpFileName, "foo"),
/OperationError: Could not write to `.*': could not parse path \(NS_ERROR_FILE_UNRECOGNIZED_PATH\)/, "IOUtils::writeUTF8 only works with absolute paths"
);
});
add_task(async function test_read_utf8_relative_path() {
const tmpFileName = "test_ioutils_read_utf8_relative_path.tmp";
info("Test reading a file at a relative destination");
await Assert.rejects(
IOUtils.readUTF8(tmpFileName),
/OperationError: Could not read `.*': could not parse path \(NS_ERROR_FILE_UNRECOGNIZED_PATH\)/, "IOUtils::readUTF8 only works with absolute paths"
);
});
add_task(async function test_utf8_lz4() {
const tmpFileName = PathUtils.join(PathUtils.tempDir, "test_ioutils_utf8_lz4.tmp");
await Assert.rejects(
IOUtils.readUTF8(tmpFileName, { decompress: true }),
/NotReadableError: Could not read `.*': could not decompress file: invalid LZ4 header: wrong magic number: `01 01 01 01 01 01 01 01 01 01 01 01' \(NS_ERROR_FILE_CORRUPTED\)/, "IOUtils::readUTF8 fails to decompress LZ4 data with a bad header"
);
info("Test UTF-8 decompression of short byte buffer");
const elevenBytes = Uint8Array.of(...new Array(11).fill(1));
await IOUtils.write(tmpFileName, elevenBytes, { compress: false });
await Assert.rejects(
IOUtils.readUTF8(tmpFileName, { decompress: true }),
/NotReadableError: Could not read `.*': could not decompress file: buffer is too small \(NS_ERROR_FILE_CORRUPTED\)/, "IOUtils::readUTF8 fails to decompress LZ4 data with missing header"
);
info("Test UTF-8 decompression of valid header, but corrupt contents");
const headerFor10bytes = [109, 111, 122, 76, 122, 52, 48, 0, 10, 0, 0, 0] // "mozlz40\0" + 4 byte length
const badContents = new Array(11).fill(255); // Bad leading byte, followed by uncompressed stream.
const goodHeaderBadContents = Uint8Array.of(...headerFor10bytes, ...badContents);
await IOUtils.write(tmpFileName, goodHeaderBadContents, { compress: false });
await Assert.rejects(
IOUtils.readUTF8(tmpFileName, { decompress: true }),
/NotReadableError: Could not read `.*': could not decompress file: the file may be corrupt \(NS_ERROR_FILE_CORRUPTED\)/, "IOUtils::readUTF8 fails to read corrupt LZ4 contents with a correct header"
);
info("Testing decompression of an empty file (no header)");
{
const n = await IOUtils.writeUTF8(tmpFileName, "");
ok(n === 0, "Overwrote with empty file");
}
await Assert.rejects(
IOUtils.readUTF8(tmpFileName, { decompress: true }),
/NotReadableError: Could not read `.*': could not decompress file: buffer is too small \(NS_ERROR_FILE_CORRUPTED\)/, "IOUtils::readUTF8 fails to decompress empty files"
);
await cleanup(tmpFileName);
});
add_task(async function test_skipBOM() {
const tmpFileName = PathUtils.join(PathUtils.tempDir, "test_ioutils_readutf8_bom.tmp");
const raw = `\uFEFFstring`;
await IOUtils.writeUTF8(tmpFileName, raw);
is(
await IOUtils.readUTF8(tmpFileName), "string", "IOUtils.readUTF8 should skip BOM by default"
);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.