diff options
author | Matt Mastracci <matthew@mastracci.com> | 2024-02-10 13:22:13 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-10 20:22:13 +0000 |
commit | f5e46c9bf2f50d66a953fa133161fc829cecff06 (patch) | |
tree | 8faf2f5831c1c7b11d842cd9908d141082c869a5 /cli/tests/unit/files_test.ts | |
parent | d2477f780630a812bfd65e3987b70c0d309385bb (diff) |
chore: move cli/tests/ -> tests/ (#22369)
This looks like a massive PR, but it's only a move from cli/tests ->
tests, and updates of relative paths for files.
This is the first step towards aggregate all of the integration test
files under tests/, which will lead to a set of integration tests that
can run without the CLI binary being built.
While we could leave these tests under `cli`, it would require us to
keep a more complex directory structure for the various test runners. In
addition, we have a lot of complexity to ignore various test files in
the `cli` project itself (cargo publish exclusion rules, autotests =
false, etc).
And finally, the `tests/` folder will eventually house the `test_ffi`,
`test_napi` and other testing code, reducing the size of the root repo
directory.
For easier review, the extremely large and noisy "move" is in the first
commit (with no changes -- just a move), while the remainder of the
changes to actual files is in the second commit.
Diffstat (limited to 'cli/tests/unit/files_test.ts')
-rw-r--r-- | cli/tests/unit/files_test.ts | 1095 |
1 files changed, 0 insertions, 1095 deletions
diff --git a/cli/tests/unit/files_test.ts b/cli/tests/unit/files_test.ts deleted file mode 100644 index 2501ea643..000000000 --- a/cli/tests/unit/files_test.ts +++ /dev/null @@ -1,1095 +0,0 @@ -// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -// deno-lint-ignore-file no-deprecated-deno-api - -import { - assert, - assertEquals, - assertRejects, - assertThrows, -} from "./test_util.ts"; -import { copy } from "@test_util/std/streams/copy.ts"; - -Deno.test(function filesStdioFileDescriptors() { - assertEquals(Deno.stdin.rid, 0); - assertEquals(Deno.stdout.rid, 1); - assertEquals(Deno.stderr.rid, 2); -}); - -Deno.test({ permissions: { read: true } }, async function filesCopyToStdout() { - const filename = "cli/tests/testdata/assets/fixture.json"; - using file = await Deno.open(filename); - assert(file instanceof Deno.File); - assert(file instanceof Deno.FsFile); - assert(file.rid > 2); - const bytesWritten = await copy(file, Deno.stdout); - const fileSize = Deno.statSync(filename).size; - assertEquals(bytesWritten, fileSize); -}); - -Deno.test({ permissions: { read: true } }, async function filesIter() { - const filename = "cli/tests/testdata/assets/hello.txt"; - using file = await Deno.open(filename); - - let totalSize = 0; - for await (const buf of Deno.iter(file)) { - totalSize += buf.byteLength; - } - - assertEquals(totalSize, 12); -}); - -Deno.test( - { permissions: { read: true } }, - async function filesIterCustomBufSize() { - const filename = "cli/tests/testdata/assets/hello.txt"; - using file = await Deno.open(filename); - - let totalSize = 0; - let iterations = 0; - for await (const buf of Deno.iter(file, { bufSize: 6 })) { - totalSize += buf.byteLength; - iterations += 1; - } - - assertEquals(totalSize, 12); - assertEquals(iterations, 2); - }, -); - -Deno.test({ permissions: { read: true } }, function filesIterSync() { - const filename = "cli/tests/testdata/assets/hello.txt"; - using file = Deno.openSync(filename); - - let totalSize = 0; - for (const buf of Deno.iterSync(file)) { - totalSize += buf.byteLength; - } - - assertEquals(totalSize, 12); -}); - -Deno.test( - { permissions: { read: true } }, - function filesIterSyncCustomBufSize() { - const filename = "cli/tests/testdata/assets/hello.txt"; - using file = Deno.openSync(filename); - - let totalSize = 0; - let iterations = 0; - for (const buf of Deno.iterSync(file, { bufSize: 6 })) { - totalSize += buf.byteLength; - iterations += 1; - } - - assertEquals(totalSize, 12); - assertEquals(iterations, 2); - }, -); - -Deno.test(async function readerIter() { - // ref: https://github.com/denoland/deno/issues/2330 - const encoder = new TextEncoder(); - - class TestReader implements Deno.Reader { - #offset = 0; - #buf: Uint8Array; - - constructor(s: string) { - this.#buf = new Uint8Array(encoder.encode(s)); - } - - read(p: Uint8Array): Promise<number | null> { - const n = Math.min(p.byteLength, this.#buf.byteLength - this.#offset); - p.set(this.#buf.slice(this.#offset, this.#offset + n)); - this.#offset += n; - - if (n === 0) { - return Promise.resolve(null); - } - - return Promise.resolve(n); - } - } - - const reader = new TestReader("hello world!"); - - let totalSize = 0; - for await (const buf of Deno.iter(reader)) { - totalSize += buf.byteLength; - } - - assertEquals(totalSize, 12); -}); - -Deno.test(async function readerIterSync() { - // ref: https://github.com/denoland/deno/issues/2330 - const encoder = new TextEncoder(); - - class TestReader implements Deno.ReaderSync { - #offset = 0; - #buf: Uint8Array; - - constructor(s: string) { - this.#buf = new Uint8Array(encoder.encode(s)); - } - - readSync(p: Uint8Array): number | null { - const n = Math.min(p.byteLength, this.#buf.byteLength - this.#offset); - p.set(this.#buf.slice(this.#offset, this.#offset + n)); - this.#offset += n; - - if (n === 0) { - return null; - } - - return n; - } - } - - const reader = new TestReader("hello world!"); - - let totalSize = 0; - for await (const buf of Deno.iterSync(reader)) { - totalSize += buf.byteLength; - } - - assertEquals(totalSize, 12); -}); - -Deno.test( - { - permissions: { read: true, write: true }, - }, - function openSyncMode() { - const path = Deno.makeTempDirSync() + "/test_openSync.txt"; - using _file = Deno.openSync(path, { - write: true, - createNew: true, - mode: 0o626, - }); - const pathInfo = Deno.statSync(path); - if (Deno.build.os !== "windows") { - assertEquals(pathInfo.mode! & 0o777, 0o626 & ~Deno.umask()); - } - }, -); - -Deno.test( - { - permissions: { read: true, write: true }, - }, - async function openMode() { - const path = (await Deno.makeTempDir()) + "/test_open.txt"; - using _file = await Deno.open(path, { - write: true, - createNew: true, - mode: 0o626, - }); - const pathInfo = Deno.statSync(path); - if (Deno.build.os !== "windows") { - assertEquals(pathInfo.mode! & 0o777, 0o626 & ~Deno.umask()); - } - }, -); - -Deno.test( - { - permissions: { read: true, write: true }, - }, - function openSyncUrl() { - const tempDir = Deno.makeTempDirSync(); - const fileUrl = new URL( - `file://${ - Deno.build.os === "windows" ? "/" : "" - }${tempDir}/test_open.txt`, - ); - using _file = Deno.openSync(fileUrl, { - write: true, - createNew: true, - mode: 0o626, - }); - const pathInfo = Deno.statSync(fileUrl); - if (Deno.build.os !== "windows") { - assertEquals(pathInfo.mode! & 0o777, 0o626 & ~Deno.umask()); - } - - Deno.removeSync(tempDir, { recursive: true }); - }, -); - -Deno.test( - { - permissions: { read: true, write: true }, - }, - async function openUrl() { - const tempDir = await Deno.makeTempDir(); - const fileUrl = new URL( - `file://${ - Deno.build.os === "windows" ? "/" : "" - }${tempDir}/test_open.txt`, - ); - using _file = await Deno.open(fileUrl, { - write: true, - createNew: true, - mode: 0o626, - }); - const pathInfo = Deno.statSync(fileUrl); - if (Deno.build.os !== "windows") { - assertEquals(pathInfo.mode! & 0o777, 0o626 & ~Deno.umask()); - } - - Deno.removeSync(tempDir, { recursive: true }); - }, -); - -Deno.test( - { permissions: { write: false } }, - async function writePermFailure() { - const filename = "tests/hello.txt"; - const openOptions: Deno.OpenOptions[] = [{ write: true }, { append: true }]; - for (const options of openOptions) { - await assertRejects(async () => { - await Deno.open(filename, options); - }, Deno.errors.PermissionDenied); - } - }, -); - -Deno.test(async function openOptions() { - const filename = "cli/tests/testdata/assets/fixture.json"; - await assertRejects( - async () => { - await Deno.open(filename, { write: false }); - }, - Error, - "OpenOptions requires at least one option to be true", - ); - - await assertRejects( - async () => { - await Deno.open(filename, { truncate: true, write: false }); - }, - Error, - "'truncate' option requires 'write' option", - ); - - await assertRejects( - async () => { - await Deno.open(filename, { create: true, write: false }); - }, - Error, - "'create' or 'createNew' options require 'write' or 'append' option", - ); - - await assertRejects( - async () => { - await Deno.open(filename, { createNew: true, append: false }); - }, - Error, - "'create' or 'createNew' options require 'write' or 'append' option", - ); -}); - -Deno.test({ permissions: { read: false } }, async function readPermFailure() { - await assertRejects(async () => { - await Deno.open("package.json", { read: true }); - }, Deno.errors.PermissionDenied); -}); - -Deno.test( - { permissions: { write: true } }, - async function writeNullBufferFailure() { - const tempDir = Deno.makeTempDirSync(); - const filename = tempDir + "hello.txt"; - const w = { - write: true, - truncate: true, - create: true, - }; - using file = await Deno.open(filename, w); - - // writing null should throw an error - await assertRejects( - async () => { - // deno-lint-ignore no-explicit-any - await file.write(null as any); - }, - ); // TODO(bartlomieju): Check error kind when dispatch_minimal pipes errors properly - await Deno.remove(tempDir, { recursive: true }); - }, -); - -Deno.test( - { permissions: { write: true, read: true } }, - async function readNullBufferFailure() { - const tempDir = Deno.makeTempDirSync(); - const filename = tempDir + "hello.txt"; - using file = await Deno.open(filename, { - read: true, - write: true, - truncate: true, - create: true, - }); - - // reading into an empty buffer should return 0 immediately - const bytesRead = await file.read(new Uint8Array(0)); - assert(bytesRead === 0); - - // reading file into null buffer should throw an error - await assertRejects(async () => { - // deno-lint-ignore no-explicit-any - await file.read(null as any); - }, TypeError); - // TODO(bartlomieju): Check error kind when dispatch_minimal pipes errors properly - - await Deno.remove(tempDir, { recursive: true }); - }, -); - -Deno.test( - { permissions: { write: false, read: false } }, - async function readWritePermFailure() { - const filename = "tests/hello.txt"; - await assertRejects(async () => { - await Deno.open(filename, { read: true }); - }, Deno.errors.PermissionDenied); - }, -); - -Deno.test( - { permissions: { write: true, read: true } }, - async function openNotFound() { - await assertRejects( - async () => { - await Deno.open("bad_file_name"); - }, - Deno.errors.NotFound, - `open 'bad_file_name'`, - ); - }, -); - -Deno.test( - { permissions: { write: true, read: true } }, - function openSyncNotFound() { - assertThrows( - () => { - Deno.openSync("bad_file_name"); - }, - Deno.errors.NotFound, - `open 'bad_file_name'`, - ); - }, -); - -Deno.test( - { permissions: { read: true, write: true } }, - async function createFile() { - const tempDir = await Deno.makeTempDir(); - const filename = tempDir + "/test.txt"; - const f = await Deno.create(filename); - let fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile); - assert(fileInfo.size === 0); - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - await f.write(data); - fileInfo = Deno.statSync(filename); - assert(fileInfo.size === 5); - f.close(); - - // TODO(bartlomieju): test different modes - await Deno.remove(tempDir, { recursive: true }); - }, -); - -Deno.test( - { permissions: { read: true, write: true } }, - async function createFileWithUrl() { - const tempDir = await Deno.makeTempDir(); - const fileUrl = new URL( - `file://${Deno.build.os === "windows" ? "/" : ""}${tempDir}/test.txt`, - ); - const f = await Deno.create(fileUrl); - let fileInfo = Deno.statSync(fileUrl); - assert(fileInfo.isFile); - assert(fileInfo.size === 0); - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - await f.write(data); - fileInfo = Deno.statSync(fileUrl); - assert(fileInfo.size === 5); - f.close(); - - await Deno.remove(tempDir, { recursive: true }); - }, -); - -Deno.test( - { permissions: { read: true, write: true } }, - async function createSyncFile() { - const tempDir = await Deno.makeTempDir(); - const filename = tempDir + "/test.txt"; - const f = Deno.createSync(filename); - let fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile); - assert(fileInfo.size === 0); - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - await f.write(data); - fileInfo = Deno.statSync(filename); - assert(fileInfo.size === 5); - f.close(); - - // TODO(bartlomieju): test different modes - await Deno.remove(tempDir, { recursive: true }); - }, -); - -Deno.test( - { permissions: { read: true, write: true } }, - async function createSyncFileWithUrl() { - const tempDir = await Deno.makeTempDir(); - const fileUrl = new URL( - `file://${Deno.build.os === "windows" ? "/" : ""}${tempDir}/test.txt`, - ); - const f = Deno.createSync(fileUrl); - let fileInfo = Deno.statSync(fileUrl); - assert(fileInfo.isFile); - assert(fileInfo.size === 0); - const enc = new TextEncoder(); - const data = enc.encode("Hello"); - await f.write(data); - fileInfo = Deno.statSync(fileUrl); - assert(fileInfo.size === 5); - f.close(); - - await Deno.remove(tempDir, { recursive: true }); - }, -); - -Deno.test( - { permissions: { read: true, write: true } }, - async function openModeWrite() { - const tempDir = Deno.makeTempDirSync(); - const encoder = new TextEncoder(); - const filename = tempDir + "hello.txt"; - const data = encoder.encode("Hello world!\n"); - let file = await Deno.open(filename, { - create: true, - write: true, - truncate: true, - }); - // assert file was created - let fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile); - assertEquals(fileInfo.size, 0); - // write some data - await file.write(data); - fileInfo = Deno.statSync(filename); - assertEquals(fileInfo.size, 13); - // assert we can't read from file - let thrown = false; - try { - const buf = new Uint8Array(20); - await file.read(buf); - } catch (_e) { - thrown = true; - } finally { - assert(thrown, "'w' mode shouldn't allow to read file"); - } - file.close(); - // assert that existing file is truncated on open - file = await Deno.open(filename, { - write: true, - truncate: true, - }); - file.close(); - const fileSize = Deno.statSync(filename).size; - assertEquals(fileSize, 0); - await Deno.remove(tempDir, { recursive: true }); - }, -); - -Deno.test( - { permissions: { read: true, write: true } }, - async function openModeWriteRead() { - const tempDir = Deno.makeTempDirSync(); - const encoder = new TextEncoder(); - const filename = tempDir + "hello.txt"; - const data = encoder.encode("Hello world!\n"); - - using file = await Deno.open(filename, { - write: true, - truncate: true, - create: true, - read: true, - }); - const seekPosition = 0; - // assert file was created - let fileInfo = Deno.statSync(filename); - assert(fileInfo.isFile); - assertEquals(fileInfo.size, 0); - // write some data - await file.write(data); - fileInfo = Deno.statSync(filename); - assertEquals(fileInfo.size, 13); - - const buf = new Uint8Array(20); - // seeking from beginning of a file - const cursorPosition = await file.seek(seekPosition, Deno.SeekMode.Start); - assertEquals(seekPosition, cursorPosition); - const result = await file.read(buf); - assertEquals(result, 13); - - await Deno.remove(tempDir, { recursive: true }); - }, -); - -Deno.test({ permissions: { read: true } }, async function seekStart() { - const filename = "cli/tests/testdata/assets/hello.txt"; - using file = await Deno.open(filename); - const seekPosition = 6; - // Deliberately move 1 step forward - await file.read(new Uint8Array(1)); // "H" - // Skipping "Hello " - // seeking from beginning of a file plus seekPosition - const cursorPosition = await file.seek(seekPosition, Deno.SeekMode.Start); - assertEquals(seekPosition, cursorPosition); - const buf = new Uint8Array(6); - await file.read(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); -}); - -Deno.test({ permissions: { read: true } }, async function seekStartBigInt() { - const filename = "cli/tests/testdata/assets/hello.txt"; - using file = await Deno.open(filename); - const seekPosition = 6n; - // Deliberately move 1 step forward - await file.read(new Uint8Array(1)); // "H" - // Skipping "Hello " - // seeking from beginning of a file plus seekPosition - const cursorPosition = await file.seek(seekPosition, Deno.SeekMode.Start); - assertEquals(seekPosition, BigInt(cursorPosition)); - const buf = new Uint8Array(6); - await file.read(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); -}); - -Deno.test({ permissions: { read: true } }, function seekSyncStart() { - const filename = "cli/tests/testdata/assets/hello.txt"; - using file = Deno.openSync(filename); - const seekPosition = 6; - // Deliberately move 1 step forward - file.readSync(new Uint8Array(1)); // "H" - // Skipping "Hello " - // seeking from beginning of a file plus seekPosition - const cursorPosition = file.seekSync(seekPosition, Deno.SeekMode.Start); - assertEquals(seekPosition, cursorPosition); - const buf = new Uint8Array(6); - file.readSync(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); -}); - -Deno.test({ permissions: { read: true } }, async function seekCurrent() { - const filename = "cli/tests/testdata/assets/hello.txt"; - using file = await Deno.open(filename); - // Deliberately move 1 step forward - await file.read(new Uint8Array(1)); // "H" - // Skipping "ello " - const seekPosition = 5; - // seekPosition is relative to current cursor position after read - const cursorPosition = await file.seek(seekPosition, Deno.SeekMode.Current); - assertEquals(seekPosition + 1, cursorPosition); - const buf = new Uint8Array(6); - await file.read(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); -}); - -Deno.test({ permissions: { read: true } }, function seekSyncCurrent() { - const filename = "cli/tests/testdata/assets/hello.txt"; - using file = Deno.openSync(filename); - // Deliberately move 1 step forward - file.readSync(new Uint8Array(1)); // "H" - // Skipping "ello " - const seekPosition = 5; - // seekPosition is relative to current cursor position after read - const cursorPosition = file.seekSync(seekPosition, Deno.SeekMode.Current); - assertEquals(seekPosition + 1, cursorPosition); - const buf = new Uint8Array(6); - file.readSync(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); -}); - -Deno.test({ permissions: { read: true } }, async function seekEnd() { - const filename = "cli/tests/testdata/assets/hello.txt"; - using file = await Deno.open(filename); - const seekPosition = -6; - // seek from end of file that has 12 chars, 12 - 6 = 6 - const cursorPosition = await file.seek(seekPosition, Deno.SeekMode.End); - assertEquals(6, cursorPosition); - const buf = new Uint8Array(6); - await file.read(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); -}); - -Deno.test({ permissions: { read: true } }, function seekSyncEnd() { - const filename = "cli/tests/testdata/assets/hello.txt"; - using file = Deno.openSync(filename); - const seekPosition = -6; - // seek from end of file that has 12 chars, 12 - 6 = 6 - const cursorPosition = file.seekSync(seekPosition, Deno.SeekMode.End); - assertEquals(6, cursorPosition); - const buf = new Uint8Array(6); - file.readSync(buf); - const decoded = new TextDecoder().decode(buf); - assertEquals(decoded, "world!"); -}); - -Deno.test({ permissions: { read: true } }, async function seekMode() { - const filename = "cli/tests/testdata/assets/hello.txt"; - using file = await Deno.open(filename); - await assertRejects( - async () => { - await file.seek(1, -1 as unknown as Deno.SeekMode); - }, - TypeError, - "Invalid seek mode", - ); - - // We should still be able to read the file - // since it is still open. - const buf = new Uint8Array(1); - await file.read(buf); // "H" - assertEquals(new TextDecoder().decode(buf), "H"); -}); - -Deno.test( - { permissions: { read: true, write: true } }, - function fileTruncateSyncSuccess() { - const filename = Deno.makeTempDirSync() + "/test_fileTruncateSync.txt"; - using file = Deno.openSync(filename, { - create: true, - read: true, - write: true, - }); - - file.truncateSync(20); - assertEquals(Deno.readFileSync(filename).byteLength, 20); - file.truncateSync(5); - assertEquals(Deno.readFileSync(filename).byteLength, 5); - file.truncateSync(-5); - assertEquals(Deno.readFileSync(filename).byteLength, 0); - - Deno.removeSync(filename); - }, -); - -Deno.test( - { permissions: { read: true, write: true } }, - async function fileTruncateSuccess() { - const filename = Deno.makeTempDirSync() + "/test_fileTruncate.txt"; - using file = await Deno.open(filename, { - create: true, - read: true, - write: true, - }); - - await file.truncate(20); - assertEquals((await Deno.readFile(filename)).byteLength, 20); - await file.truncate(5); - assertEquals((await Deno.readFile(filename)).byteLength, 5); - await file.truncate(-5); - assertEquals((await Deno.readFile(filename)).byteLength, 0); - - await Deno.remove(filename); - }, -); - -Deno.test({ permissions: { read: true } }, function fileStatSyncSuccess() { - using file = Deno.openSync("README.md"); - const fileInfo = file.statSync(); - assert(fileInfo.isFile); - assert(!fileInfo.isSymlink); - assert(!fileInfo.isDirectory); - assert(fileInfo.size); - assert(fileInfo.atime); - assert(fileInfo.mtime); - // The `birthtime` field is not available on Linux before kernel version 4.11. - assert(fileInfo.birthtime || Deno.build.os === "linux"); -}); - -Deno.test(async function fileStatSuccess() { - using file = await Deno.open("README.md"); - const fileInfo = await file.stat(); - assert(fileInfo.isFile); - assert(!fileInfo.isSymlink); - assert(!fileInfo.isDirectory); - assert(fileInfo.size); - assert(fileInfo.atime); - assert(fileInfo.mtime); - // The `birthtime` field is not available on Linux before kernel version 4.11. - assert(fileInfo.birthtime || Deno.build.os === "linux"); -}); - -Deno.test({ permissions: { read: true } }, async function readableStream() { - const filename = "cli/tests/testdata/assets/hello.txt"; - const file = await Deno.open(filename); - assert(file.readable instanceof ReadableStream); - const chunks = []; - for await (const chunk of file.readable) { - chunks.push(chunk); - } - assertEquals(chunks.length, 1); - assertEquals(chunks[0].byteLength, 12); -}); - -Deno.test( - { permissions: { read: true } }, - async function readableStreamTextEncoderPipe() { - const filename = "cli/tests/testdata/assets/hello.txt"; - const file = await Deno.open(filename); - const readable = file.readable.pipeThrough(new TextDecoderStream()); - const chunks = []; - for await (const chunk of readable) { - chunks.push(chunk); - } - assertEquals(chunks.length, 1); - assertEquals(chunks[0].length, 12); - }, -); - -Deno.test( - { permissions: { read: true, write: true } }, - async function writableStream() { - const path = await Deno.makeTempFile(); - const file = await Deno.open(path, { write: true }); - assert(file.writable instanceof WritableStream); - const readable = new ReadableStream({ - start(controller) { - controller.enqueue(new TextEncoder().encode("hello ")); - controller.enqueue(new TextEncoder().encode("world!")); - controller.close(); - }, - }); - await readable.pipeTo(file.writable); - const res = await Deno.readTextFile(path); - assertEquals(res, "hello world!"); - }, -); - -Deno.test( - { permissions: { read: true, write: true } }, - async function readTextFileNonUtf8() { - const path = await Deno.makeTempFile(); - using file = await Deno.open(path, { write: true }); - await file.write(new TextEncoder().encode("hello ")); - await file.write(new Uint8Array([0xC0])); - - const res = await Deno.readTextFile(path); - const resSync = Deno.readTextFileSync(path); - assertEquals(res, resSync); - assertEquals(res, "hello \uFFFD"); - }, -); - -Deno.test( - { permissions: { read: true } }, - async function fsFileExplicitResourceManagement() { - let file2: Deno.FsFile; - - { - using file = await Deno.open("cli/tests/testdata/assets/hello.txt"); - file2 = file; - - const stat = file.statSync(); - assert(stat.isFile); - } - - assertThrows(() => file2.statSync(), Deno.errors.BadResource); - }, -); - -Deno.test( - { permissions: { read: true } }, - async function fsFileExplicitResourceManagementManualClose() { - using file = await Deno.open("cli/tests/testdata/assets/hello.txt"); - file.close(); - assertThrows(() => file.statSync(), Deno.errors.BadResource); // definitely closed - // calling [Symbol.dispose] after manual close is a no-op - }, -); - -Deno.test( - { permissions: { read: true, write: true } }, - function fsFileDatasyncSyncSuccess() { - const filename = Deno.makeTempDirSync() + "/test_fdatasyncSync.txt"; - const file = Deno.openSync(filename, { - read: true, - write: true, - create: true, - }); - const data = new Uint8Array(64); - file.writeSync(data); - file.syncDataSync(); - assertEquals(Deno.readFileSync(filename), data); - file.close(); - Deno.removeSync(filename); - }, -); - -Deno.test( - { permissions: { read: true, write: true } }, - async function fsFileDatasyncSuccess() { - const filename = (await Deno.makeTempDir()) + "/test_fdatasync.txt"; - const file = await Deno.open(filename, { - read: true, - write: true, - create: true, - }); - const data = new Uint8Array(64); - await file.write(data); - await file.syncData(); - assertEquals(await Deno.readFile(filename), data); - file.close(); - await Deno.remove(filename); - }, -); - -Deno.test( - { permissions: { read: true, write: true } }, - function fsFileSyncSyncSuccess() { - const filename = Deno.makeTempDirSync() + "/test_fsyncSync.txt"; - const file = Deno.openSync(filename, { - read: true, - write: true, - create: true, - }); - const size = 64; - file.truncateSync(size); - file.syncSync(); - assertEquals(file.statSync().size, size); - file.close(); - Deno.removeSync(filename); - }, -); - -Deno.test( - { permissions: { read: true, write: true } }, - async function fsFileSyncSuccess() { - const filename = (await Deno.makeTempDir()) + "/test_fsync.txt"; - const file = await Deno.open(filename, { - read: true, - write: true, - create: true, - }); - const size = 64; - await file.truncate(size); - await file.sync(); - assertEquals((await file.stat()).size, size); - file.close(); - await Deno.remove(filename); - }, -); - -Deno.test( - { permissions: { read: true, run: true, hrtime: true } }, - async function fsFileLockFileSync() { - await runFlockTests({ sync: true }); - }, -); - -Deno.test( - { permissions: { read: true, run: true, hrtime: true } }, - async function fsFileLockFileAsync() { - await runFlockTests({ sync: false }); - }, -); - -async function runFlockTests(opts: { sync: boolean }) { - assertEquals( - await checkFirstBlocksSecond({ - firstExclusive: true, - secondExclusive: false, - sync: opts.sync, - }), - true, - "exclusive blocks shared", - ); - assertEquals( - await checkFirstBlocksSecond({ - firstExclusive: false, - secondExclusive: true, - sync: opts.sync, - }), - true, - "shared blocks exclusive", - ); - assertEquals( - await checkFirstBlocksSecond({ - firstExclusive: true, - secondExclusive: true, - sync: opts.sync, - }), - true, - "exclusive blocks exclusive", - ); - assertEquals( - await checkFirstBlocksSecond({ - firstExclusive: false, - secondExclusive: false, - sync: opts.sync, - // need to wait for both to enter the lock to prevent the case where the - // first process enters and exits the lock before the second even enters - waitBothEnteredLock: true, - }), - false, - "shared does not block shared", - ); -} - -async function checkFirstBlocksSecond(opts: { - firstExclusive: boolean; - secondExclusive: boolean; - sync: boolean; - waitBothEnteredLock?: boolean; -}) { - const firstProcess = runFlockTestProcess({ - exclusive: opts.firstExclusive, - sync: opts.sync, - }); - const secondProcess = runFlockTestProcess({ - exclusive: opts.secondExclusive, - sync: opts.sync, - }); - try { - const sleep = (time: number) => new Promise((r) => setTimeout(r, time)); - - await Promise.all([ - firstProcess.waitStartup(), - secondProcess.waitStartup(), - ]); - - await firstProcess.enterLock(); - await firstProcess.waitEnterLock(); - - await secondProcess.enterLock(); - await sleep(100); - - if (!opts.waitBothEnteredLock) { - await firstProcess.exitLock(); - } - - await secondProcess.waitEnterLock(); - - if (opts.waitBothEnteredLock) { - await firstProcess.exitLock(); - } - - await secondProcess.exitLock(); - - // collect the final output - const firstPsTimes = await firstProcess.getTimes(); - const secondPsTimes = await secondProcess.getTimes(); - return firstPsTimes.exitTime < secondPsTimes.enterTime; - } finally { - await firstProcess.close(); - await secondProcess.close(); - } -} - -function runFlockTestProcess(opts: { exclusive: boolean; sync: boolean }) { - const path = "cli/tests/testdata/assets/lock_target.txt"; - const scriptText = ` - const file = Deno.openSync("${path}"); - - // ready signal - Deno.stdout.writeSync(new Uint8Array(1)); - // wait for enter lock signal - Deno.stdin.readSync(new Uint8Array(1)); - - // entering signal - Deno.stdout.writeSync(new Uint8Array(1)); - // lock and record the entry time - ${ - opts.sync - ? `file.lockSync(${opts.exclusive ? "true" : "false"});` - : `await file.lock(${opts.exclusive ? "true" : "false"});` - } - const enterTime = new Date().getTime(); - // entered signal - Deno.stdout.writeSync(new Uint8Array(1)); - - // wait for exit lock signal - Deno.stdin.readSync(new Uint8Array(1)); - - // record the exit time and wait a little bit before releasing - // the lock so that the enter time of the next process doesn't - // occur at the same time as this exit time - const exitTime = new Date().getTime(); - await new Promise(resolve => setTimeout(resolve, 100)); - - // release the lock - ${opts.sync ? "file.unlockSync();" : "await file.unlock();"} - - // exited signal - Deno.stdout.writeSync(new Uint8Array(1)); - - // output the enter and exit time - console.log(JSON.stringify({ enterTime, exitTime })); -`; - - const process = new Deno.Command(Deno.execPath(), { - args: ["eval", "--unstable", scriptText], - stdin: "piped", - stdout: "piped", - stderr: "null", - }).spawn(); - - const waitSignal = async () => { - const reader = process.stdout.getReader({ mode: "byob" }); - await reader.read(new Uint8Array(1)); - reader.releaseLock(); - }; - const signal = async () => { - const writer = process.stdin.getWriter(); - await writer.write(new Uint8Array(1)); - writer.releaseLock(); - }; - - return { - async waitStartup() { - await waitSignal(); - }, - async enterLock() { - await signal(); - await waitSignal(); // entering signal - }, - async waitEnterLock() { - await waitSignal(); - }, - async exitLock() { - await signal(); - await waitSignal(); - }, - getTimes: async () => { - const { stdout } = await process.output(); - const text = new TextDecoder().decode(stdout); - return JSON.parse(text) as { - enterTime: number; - exitTime: number; - }; - }, - close: async () => { - await process.status; - await process.stdin.close(); - }, - }; -} |