// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. /// import { assertEquals, assertFalse, assertMatch, assertStrictEquals, } from "@std/assert"; import { read, readSync } from "node:fs"; import { open, openSync } from "node:fs"; import { Buffer } from "node:buffer"; import * as path from "@std/path"; import { closeSync } from "node:fs"; async function readTest( testData: string, buffer: NodeJS.ArrayBufferView, offset: number, length: number, position: number | null = null, expected: ( fd: number, bytesRead: number | null, data: ArrayBufferView | undefined, ) => void, ) { let fd1 = 0; await new Promise<{ fd: number; bytesRead: number | null; data: ArrayBufferView | undefined; }>((resolve, reject) => { open(testData, "r", (err, fd) => { if (err) reject(err); read(fd, buffer, offset, length, position, (err, bytesRead, data) => { if (err) reject(err); resolve({ fd, bytesRead, data }); }); }); }) .then(({ fd, bytesRead, data }) => { fd1 = fd; expected(fd, bytesRead, data); }) .finally(() => closeSync(fd1)); } Deno.test({ name: "readSuccess", async fn() { const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); const testData = path.resolve(moduleDir, "testdata", "hello.txt"); const buf = Buffer.alloc(1024); await readTest( testData, buf, buf.byteOffset, buf.byteLength, null, (_fd, bytesRead, data) => { assertStrictEquals(bytesRead, 11); assertEquals(data instanceof Buffer, true); assertMatch((data as Buffer).toString(), /hello world/); }, ); }, }); Deno.test({ name: "[std/node/fs] Read only five bytes, so that the position moves to five", async fn() { const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); const testData = path.resolve(moduleDir, "testdata", "hello.txt"); const buf = Buffer.alloc(5); await readTest( testData, buf, buf.byteOffset, 5, null, (_fd, bytesRead, data) => { assertStrictEquals(bytesRead, 5); assertEquals(data instanceof Buffer, true); assertEquals((data as Buffer).toString(), "hello"); }, ); }, }); Deno.test({ name: "[std/node/fs] position option of fs.read() specifies where to begin reading from in the file", async fn() { const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); const testData = path.resolve(moduleDir, "testdata", "hello.txt"); const fd = openSync(testData, "r"); const buf = Buffer.alloc(5); const positions = [6, 0, -1, null]; const expected = [ [119, 111, 114, 108, 100], [104, 101, 108, 108, 111], [104, 101, 108, 108, 111], [32, 119, 111, 114, 108], ]; for (const [i, position] of positions.entries()) { await new Promise((resolve) => { read( fd, { buffer: buf, offset: buf.byteOffset, length: buf.byteLength, position, }, (err, bytesRead, data) => { assertEquals(err, null); assertStrictEquals(bytesRead, 5); assertEquals( data, Buffer.from(expected[i]), ); return resolve(true); }, ); }); } closeSync(fd); }, }); Deno.test({ name: "[std/node/fs] Read fs.read(fd, options, cb) signature", async fn() { const { promise, reject, resolve } = Promise.withResolvers(); const file = Deno.makeTempFileSync(); Deno.writeTextFileSync(file, "hi there"); const fd = openSync(file, "r+"); const buf = Buffer.alloc(11); read( fd, { buffer: buf, offset: buf.byteOffset, length: buf.byteLength, position: null, }, (err, bytesRead, data) => { try { assertEquals(err, null); assertStrictEquals(bytesRead, 8); assertEquals( data, Buffer.from([104, 105, 32, 116, 104, 101, 114, 101, 0, 0, 0]), ); } catch (e) { reject(e); return; } resolve(); }, ); closeSync(fd); await promise; }, }); Deno.test({ name: "[std/node/fs] Read fs.read(fd, cb) signature", async fn() { const { promise, resolve, reject } = Promise.withResolvers(); const file = Deno.makeTempFileSync(); Deno.writeTextFileSync(file, "hi deno"); const fd = openSync(file, "r+"); read(fd, (err, bytesRead, data) => { try { assertEquals(err, null); assertStrictEquals(bytesRead, 7); assertStrictEquals(data?.byteLength, 16384); } catch (e) { reject(e); return; } resolve(); }); closeSync(fd); await promise; }, }); Deno.test({ name: "SYNC: readSuccess", fn() { const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); const testData = path.resolve(moduleDir, "testdata", "hello.txt"); const buffer = Buffer.alloc(1024); const fd = openSync(testData, "r"); const bytesRead = readSync( fd, buffer, buffer.byteOffset, buffer.byteLength, null, ); assertStrictEquals(bytesRead, 11); closeSync(fd); }, }); Deno.test({ name: "[std/node/fs] Read only two bytes, so that the position moves to two", fn() { const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); const testData = path.resolve(moduleDir, "testdata", "hello.txt"); const buffer = Buffer.alloc(2); const fd = openSync(testData, "r"); const bytesRead = readSync(fd, buffer, buffer.byteOffset, 2, null); assertStrictEquals(bytesRead, 2); closeSync(fd); }, }); Deno.test({ name: "[std/node/fs] position option of fs.readSync() specifies where to begin reading from in the file", fn() { const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); const testData = path.resolve(moduleDir, "testdata", "hello.txt"); const fd = openSync(testData, "r"); const buf = Buffer.alloc(5); const positions = [6, 0, -1, null]; const expected = [ [119, 111, 114, 108, 100], [104, 101, 108, 108, 111], [104, 101, 108, 108, 111], [32, 119, 111, 114, 108], ]; for (const [i, position] of positions.entries()) { const bytesRead = readSync( fd, buf, buf.byteOffset, buf.byteLength, position, ); assertStrictEquals(bytesRead, 5); assertEquals( buf, Buffer.from(expected[i]), ); } closeSync(fd); }, }); Deno.test({ name: "[std/node/fs] Read fs.readSync(fd, buffer[, options]) signature", fn() { const file = Deno.makeTempFileSync(); Deno.writeTextFileSync(file, "hello deno"); const buffer = Buffer.alloc(1024); const fd = openSync(file, "r+"); const bytesRead = readSync(fd, buffer, { length: buffer.byteLength, offset: buffer.byteOffset, position: null, }); assertStrictEquals(bytesRead, 10); closeSync(fd); }, }); Deno.test({ name: "[std/node/fs] fs.read is async", async fn(t) { const file = await Deno.makeTempFile(); await Deno.writeTextFile(file, "abc"); await t.step("without position option", async () => { const { promise, resolve } = Promise.withResolvers(); let called = false; const fd = openSync(file, "r"); read(fd, () => { called = true; closeSync(fd); resolve(); }); assertFalse(called); await promise; }); await t.step("with position option", async () => { const { promise, resolve } = Promise.withResolvers(); let called = false; const buffer = Buffer.alloc(2); const fd = openSync(file, "r"); read(fd, { position: 1, buffer, offset: 0, length: 2 }, () => { called = true; closeSync(fd); resolve(); }); assertFalse(called); await promise; }); await Deno.remove(file); }, }); Deno.test({ name: "SYNC: read with no offsetOropts argument", fn() { const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); const testData = path.resolve(moduleDir, "testdata", "hello.txt"); const buffer = Buffer.alloc(1024); const fd = openSync(testData, "r"); const _bytesRead = readSync( fd, buffer, ); closeSync(fd); }, }); Deno.test({ name: "accepts non Uint8Array buffer", async fn() { const moduleDir = path.dirname(path.fromFileUrl(import.meta.url)); const testData = path.resolve(moduleDir, "testdata", "hello.txt"); const buffer = new ArrayBuffer(1024); const buf = new Int8Array(buffer); await readTest( testData, buf, buf.byteOffset, buf.byteLength, null, (_fd, bytesRead, data) => { assertStrictEquals(bytesRead, 11); assertEquals(data instanceof Buffer, true); assertMatch((data as Buffer).toString(), /hello world/); }, ); const fd = openSync(testData, "r"); try { const nRead = readSync(fd, buf); const expected = new TextEncoder().encode("hello world"); assertEquals(buf.slice(0, nRead), new Int8Array(expected.buffer)); } finally { closeSync(fd); } }, });