mirror of
https://github.com/oven-sh/bun
synced 2026-02-11 03:18:53 +00:00
await FileHandle functions (#9451)
* Await FileHandle functions * Update fs.promises.ts * use await using = await * Make this more robust + fix tests --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
This commit is contained in:
@@ -218,267 +218,292 @@ const real_export = {
|
||||
};
|
||||
export default real_export;
|
||||
|
||||
// Partially taken from https://github.com/nodejs/node/blob/c25878d370/lib/internal/fs/promises.js#L148
|
||||
class FileHandle extends EventEmitter {
|
||||
constructor(fd) {
|
||||
super();
|
||||
this[kFd] = fd ? fd : -1;
|
||||
this[kRefs] = 1;
|
||||
this[kClosePromise] = null;
|
||||
}
|
||||
|
||||
getAsyncId() {
|
||||
throw new Error("BUN TODO FileHandle.getAsyncId");
|
||||
}
|
||||
|
||||
get fd() {
|
||||
return this[kFd];
|
||||
}
|
||||
|
||||
appendFile(data, options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.writeFile, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return real_export.writeFile(fd, data, options);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
chmod(mode) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.fchmod, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return real_export.fchmod(fd, mode);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
chown(uid, gid) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.fchown, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return real_export.fchown(fd, uid, gid);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
datasync() {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.fdatasync, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return real_export.fdatasync(fd);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
sync() {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.fsync, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return real_export.fsync(fd);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
read(buffer, offset, length, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.read, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
const bytesRead = real_export.read(fd, buffer, offset, length, position);
|
||||
return { bytesRead, buffer };
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
readv(buffers, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.readv, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
const bytesRead = real_export.readv(fd, buffers, position);
|
||||
return { bytesRead, buffers };
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
readFile(options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.readFile, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return real_export.readFile(fd, options);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
readLines(options = undefined) {
|
||||
throw new Error("BUN TODO FileHandle.readLines");
|
||||
}
|
||||
|
||||
stat(options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.fstat, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return real_export.fstat(fd, options);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
truncate(len = 0) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.ftruncate, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return real_export.ftruncate(fd, len);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
utimes(atime, mtime) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.futimes, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return real_export.futimes(fd, atime, mtime);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
write(buffer, offset, length, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.write, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return real_export.write(fd, buffer, offset, length, position);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
writev(buffers, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.writev, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return real_export.writev(fd, buffers, position);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
writeFile(data, options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(real_export.writeFile, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return real_export.writeFile(fd, data, options);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this[kFd] === -1) {
|
||||
return PromiseResolve();
|
||||
{
|
||||
const {
|
||||
writeFile,
|
||||
readFile,
|
||||
fchmod,
|
||||
fchown,
|
||||
fdatasync,
|
||||
fsync,
|
||||
read,
|
||||
readv,
|
||||
fstat,
|
||||
ftruncate,
|
||||
futimes,
|
||||
write,
|
||||
writev,
|
||||
close,
|
||||
} = real_export;
|
||||
// Partially taken from https://github.com/nodejs/node/blob/c25878d370/lib/internal/fs/promises.js#L148
|
||||
// These functions await the result so that errors propagate correctly with
|
||||
// async stack traces and so that the ref counting is correct.
|
||||
var FileHandle = class FileHandle extends EventEmitter {
|
||||
constructor(fd) {
|
||||
super();
|
||||
this[kFd] = fd ? fd : -1;
|
||||
this[kRefs] = 1;
|
||||
this[kClosePromise] = null;
|
||||
}
|
||||
|
||||
if (this[kClosePromise]) {
|
||||
getAsyncId() {
|
||||
throw new Error("BUN TODO FileHandle.getAsyncId");
|
||||
}
|
||||
|
||||
get fd() {
|
||||
return this[kFd];
|
||||
}
|
||||
|
||||
async appendFile(data, options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(writeFile, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await writeFile(fd, data, options);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async chmod(mode) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(fchmod, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fchmod(fd, mode);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async chown(uid, gid) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(fchown, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fchown(fd, uid, gid);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async datasync() {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(fdatasync, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fdatasync(fd);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async sync() {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(fsync, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fsync(fd);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async read(buffer, offset, length, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(read, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return { buffer, bytesRead: await read(fd, buffer, offset, length, position) };
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async readv(buffers, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(readv, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await readv(fd, buffers, position);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async readFile(options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(readFile, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await readFile(fd, options);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
readLines(options = undefined) {
|
||||
throw new Error("BUN TODO FileHandle.readLines");
|
||||
}
|
||||
|
||||
async stat(options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(fstat, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await fstat(fd, options);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async truncate(len = 0) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(ftruncate, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await ftruncate(fd, len);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async utimes(atime, mtime) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(futimes, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await futimes(fd, atime, mtime);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async write(buffer, offset, length, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(write, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return { buffer, bytesWritten: await write(fd, buffer, offset, length, position) };
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async writev(buffers, position) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(writev, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await writev(fd, buffers, position);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async writeFile(data, options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(writeFile, fd);
|
||||
|
||||
try {
|
||||
this[kRef]();
|
||||
return await writeFile(fd, data, options);
|
||||
} finally {
|
||||
this[kUnref]();
|
||||
}
|
||||
}
|
||||
|
||||
async close() {
|
||||
if (this[kFd] === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this[kClosePromise]) {
|
||||
return this[kClosePromise];
|
||||
}
|
||||
|
||||
this[kRefs]--;
|
||||
if (this[kRefs] === 0) {
|
||||
this[kClosePromise] = SafePromisePrototypeFinally.$call(close(this[kFd]), () => {
|
||||
this[kClosePromise] = undefined;
|
||||
});
|
||||
} else {
|
||||
this[kClosePromise] = SafePromisePrototypeFinally.$call(
|
||||
new Promise((resolve, reject) => {
|
||||
this[kCloseResolve] = resolve;
|
||||
this[kCloseReject] = reject;
|
||||
}),
|
||||
() => {
|
||||
this[kClosePromise] = undefined;
|
||||
this[kCloseReject] = undefined;
|
||||
this[kCloseResolve] = undefined;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
this.emit("close");
|
||||
return this[kClosePromise];
|
||||
}
|
||||
|
||||
this[kRefs]--;
|
||||
if (this[kRefs] === 0) {
|
||||
this[kClosePromise] = SafePromisePrototypeFinally.$call(real_export.close(this[kFd]), () => {
|
||||
this[kClosePromise] = undefined;
|
||||
});
|
||||
} else {
|
||||
this[kClosePromise] = SafePromisePrototypeFinally.$call(
|
||||
new Promise((resolve, reject) => {
|
||||
this[kCloseResolve] = resolve;
|
||||
this[kCloseReject] = reject;
|
||||
}),
|
||||
() => {
|
||||
this[kClosePromise] = undefined;
|
||||
this[kCloseReject] = undefined;
|
||||
this[kCloseResolve] = undefined;
|
||||
},
|
||||
);
|
||||
async [SymbolAsyncDispose]() {
|
||||
return this.close();
|
||||
}
|
||||
|
||||
this.emit("close");
|
||||
return this[kClosePromise];
|
||||
}
|
||||
readableWebStream(options = kEmptyObject) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(fs.createReadStream, fd);
|
||||
|
||||
async [SymbolAsyncDispose]() {
|
||||
return this.close();
|
||||
}
|
||||
|
||||
readableWebStream(options = kEmptyObject) {
|
||||
throw new Error("BUN TODO FileHandle.readableWebStream");
|
||||
}
|
||||
|
||||
createReadStream(options = undefined) {
|
||||
throw new Error("BUN TODO FileHandle.createReadStream");
|
||||
}
|
||||
|
||||
createWriteStream(options = undefined) {
|
||||
throw new Error("BUN TODO FileHandle.createWriteStream");
|
||||
}
|
||||
|
||||
[kTransfer]() {
|
||||
throw new Error("BUN TODO FileHandle.kTransfer");
|
||||
}
|
||||
|
||||
[kTransferList]() {
|
||||
throw new Error("BUN TODO FileHandle.kTransferList");
|
||||
}
|
||||
|
||||
[kDeserialize]({ handle }) {
|
||||
throw new Error("BUN TODO FileHandle.kDeserialize");
|
||||
}
|
||||
|
||||
[kRef]() {
|
||||
this[kRefs]++;
|
||||
}
|
||||
|
||||
[kUnref]() {
|
||||
this[kRefs]--;
|
||||
if (this[kRefs] === 0) {
|
||||
PromisePrototypeThen(this.close(), this[kCloseResolve], this[kCloseReject]);
|
||||
return Bun.file(fd).stream();
|
||||
}
|
||||
}
|
||||
|
||||
createReadStream(options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(fs.createReadStream, fd);
|
||||
return require("node:fs").createReadStream("", { fd, highWaterMark: 64 * 1024, ...(options || {}) });
|
||||
}
|
||||
|
||||
createWriteStream(options) {
|
||||
const fd = this[kFd];
|
||||
throwEBADFIfNecessary(fs.createWriteStream, fd);
|
||||
return require("node:fs").createWriteStream("", { fd, ...(options || {}) });
|
||||
}
|
||||
|
||||
[kTransfer]() {
|
||||
throw new Error("BUN TODO FileHandle.kTransfer");
|
||||
}
|
||||
|
||||
[kTransferList]() {
|
||||
throw new Error("BUN TODO FileHandle.kTransferList");
|
||||
}
|
||||
|
||||
[kDeserialize]({ handle }) {
|
||||
throw new Error("BUN TODO FileHandle.kDeserialize");
|
||||
}
|
||||
|
||||
[kRef]() {
|
||||
this[kRefs]++;
|
||||
}
|
||||
|
||||
[kUnref]() {
|
||||
const refCount = this[kRefs]--;
|
||||
if (refCount === 1) {
|
||||
PromisePrototypeThen.$call(this.close(), this[kCloseResolve], this[kCloseReject]);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function throwEBADFIfNecessary(fn, fd) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @known-failing-on-windows: 1 failing
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import { describe, expect, it, spyOn } from "bun:test";
|
||||
import { dirname, resolve, relative } from "node:path";
|
||||
import { promisify } from "node:util";
|
||||
import { bunEnv, bunExe, gc, getMaxFD, isIntelMacOS, isWindows } from "harness";
|
||||
@@ -40,7 +40,7 @@ import fs, {
|
||||
fdatasyncSync,
|
||||
} from "node:fs";
|
||||
|
||||
import _promises from "node:fs/promises";
|
||||
import _promises, { type FileHandle } from "node:fs/promises";
|
||||
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
@@ -60,6 +60,108 @@ function mkdirForce(path: string) {
|
||||
if (!existsSync(path)) mkdirSync(path, { recursive: true });
|
||||
}
|
||||
|
||||
describe("FileHandle", () => {
|
||||
it("FileHandle#read returns object", async () => {
|
||||
await using fd = await fs.promises.open(__filename);
|
||||
const buf = Buffer.alloc(10);
|
||||
expect(await fd.read(buf, 0, 10, 0)).toEqual({ bytesRead: 10, buffer: buf });
|
||||
});
|
||||
|
||||
it("FileHandle#readv returns object", async () => {
|
||||
await using fd = await fs.promises.open(__filename);
|
||||
const buffers = [Buffer.alloc(10), Buffer.alloc(10)];
|
||||
expect(await fd.readv(buffers, 0)).toEqual({ bytesRead: 20, buffers });
|
||||
});
|
||||
|
||||
it("FileHandle#write throws EBADF when closed", async () => {
|
||||
let handle: FileHandle;
|
||||
let spy;
|
||||
{
|
||||
await using fd = await fs.promises.open(__filename);
|
||||
handle = fd;
|
||||
spy = spyOn(handle, "close");
|
||||
const buffers = [Buffer.alloc(10), Buffer.alloc(10)];
|
||||
expect(await fd.readv(buffers, 0)).toEqual({ bytesRead: 20, buffers });
|
||||
}
|
||||
expect(handle.close).toHaveBeenCalled();
|
||||
expect(async () => await handle.read(Buffer.alloc(10))).toThrow("Bad file descriptor");
|
||||
});
|
||||
|
||||
it("FileHandle#write returns object", async () => {
|
||||
await using fd = await fs.promises.open(`${tmpdir()}/${Date.now()}.writeFile.txt`, "w");
|
||||
const buf = Buffer.from("test");
|
||||
expect(await fd.write(buf, 0, 4, 0)).toEqual({ bytesWritten: 4, buffer: buf });
|
||||
});
|
||||
|
||||
it("FileHandle#writev returns object", async () => {
|
||||
await using fd = await fs.promises.open(`${tmpdir()}/${Date.now()}.writeFile.txt`, "w");
|
||||
const buffers = [Buffer.from("test"), Buffer.from("test")];
|
||||
expect(await fd.writev(buffers, 0)).toEqual({ bytesWritten: 8, buffers });
|
||||
});
|
||||
|
||||
it("FileHandle#readFile returns buffer", async () => {
|
||||
await using fd = await fs.promises.open(__filename);
|
||||
const buf = await fd.readFile();
|
||||
expect(buf instanceof Buffer).toBe(true);
|
||||
});
|
||||
|
||||
it("FileHandle#readableWebStream", async () => {
|
||||
await using fd = await fs.promises.open(__filename);
|
||||
const stream = fd.readableWebStream();
|
||||
const reader = stream.getReader();
|
||||
const chunk = await reader.read();
|
||||
expect(chunk.value instanceof Uint8Array).toBe(true);
|
||||
reader.releaseLock();
|
||||
await stream.cancel();
|
||||
});
|
||||
|
||||
it("FileHandle#createReadStream", async () => {
|
||||
await using fd = await fs.promises.open(__filename);
|
||||
const readable = fd.createReadStream();
|
||||
const data = await new Promise(resolve => {
|
||||
let data = "";
|
||||
readable.on("data", chunk => {
|
||||
data += chunk;
|
||||
});
|
||||
readable.on("end", () => {
|
||||
resolve(data);
|
||||
});
|
||||
});
|
||||
|
||||
expect(data).toBe(readFileSync(__filename, "utf8"));
|
||||
});
|
||||
|
||||
it("FileHandle#writeFile", async () => {
|
||||
const path = `${tmpdir()}/${Date.now()}.writeFile.txt`;
|
||||
await using fd = await fs.promises.open(path, "w");
|
||||
await fd.writeFile("File written successfully");
|
||||
expect(readFileSync(path, "utf8")).toBe("File written successfully");
|
||||
});
|
||||
|
||||
it("FileHandle#createWriteStream", async () => {
|
||||
const path = `${tmpdir()}/${Date.now()}.createWriteStream.txt`;
|
||||
{
|
||||
await using fd = await fs.promises.open(path, "w");
|
||||
const stream = fd.createWriteStream();
|
||||
stream.write("Test file written successfully");
|
||||
stream.end();
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
stream.on("error", e => {
|
||||
reject(e);
|
||||
});
|
||||
|
||||
stream.on("finish", () => {
|
||||
expect(readFileSync(path, "utf8")).toBe("Test file written successfully");
|
||||
resolve(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
expect(readFileSync(path, "utf8")).toBe("Test file written successfully");
|
||||
});
|
||||
});
|
||||
|
||||
it("fdatasyncSync", () => {
|
||||
const fd = openSync(import.meta.path, "w", 0o664);
|
||||
fdatasyncSync(fd);
|
||||
@@ -2159,18 +2261,6 @@ describe("fs/promises", () => {
|
||||
it("opendir should have a path property, issue#4995", async () => {
|
||||
expect((await fs.promises.opendir(".")).path).toBe(".");
|
||||
});
|
||||
|
||||
it("FileHandle#read returns object", async () => {
|
||||
const fd = await fs.promises.open(__filename);
|
||||
const buf = Buffer.alloc(10);
|
||||
expect(await fd.read(buf, 0, 10, 0)).toEqual({ bytesRead: 10, buffer: buf });
|
||||
});
|
||||
|
||||
it("FileHandle#readv returns object", async () => {
|
||||
const fd = await fs.promises.open(__filename);
|
||||
const buffers = [Buffer.alloc(10), Buffer.alloc(10)];
|
||||
expect(await fd.readv(buffers, 0, 20, 0)).toEqual({ bytesRead: 20, buffers });
|
||||
});
|
||||
});
|
||||
|
||||
it("stat on a large file", () => {
|
||||
|
||||
Reference in New Issue
Block a user