Skip to content

chore: run some test cases in bun in CI #6635

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,32 @@ jobs:
- name: Run tests
run: deno task test:node

test-bun:
runs-on: ${{ matrix.os }}
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
bun:
- latest
os:
- ubuntu-latest

steps:
- name: Clone repository
uses: actions/checkout@v4

- name: Set up Deno
uses: denoland/setup-deno@v2

- name: Set up Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ matrix.node }}

- name: Run tests
run: deno task test:bun

lint:
runs-on: ${{ matrix.os }}
timeout-minutes: 30
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ coverage/
.vim
_tmp/
!_tmp/.keep

# tsconfig for bun
/tsconfig.json
1 change: 1 addition & 0 deletions _tools/node_test_runner/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
package-lock.json
bun.lock
6 changes: 5 additions & 1 deletion _tools/node_test_runner/run_test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,9 @@ import "../../fs/unstable_umask_test.ts";
import "../../fs/unstable_utime_test.ts";

for (const testDef of testDefinitions) {
test(testDef.name, testDef.fn);
if (testDef.ignore) {
test.skip(testDef.name, testDef.fn);
} else {
test(testDef.name, testDef.fn);
}
}
15 changes: 15 additions & 0 deletions _tools/node_test_runner/tsconfig_for_bun.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"paths": {
"@std/assert": ["./assert/mod.ts"],
"@std/assert/greater-or-equal": ["./assert/greater_or_equal.ts"],
"@std/path": ["./path/mod.ts"],
"@std/internal/format": ["./internal/format.ts"],
"@std/internal/styles": ["./internal/styles.ts"],
"@std/internal/build-message": ["./internal/build_message.ts"],
"@std/internal/diff": ["./internal/diff.ts"],
"@std/internal/diff-str": ["./internal/diff_str.ts"],
"@std/semver": ["./semver/mod.ts"],
}
Comment on lines +3 to +13
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

register function of node:module didn't seem working in Bun (which is used for resolving specifiers like @std/path in Node test runner), but instead Bun allows users to remap specifiers via compilerOptions.paths option. ref https://bun.sh/docs/runtime/modules#path-re-mapping

In the spirit of treating TypeScript as a first-class citizen, the Bun runtime will re-map import paths according to the compilerOptions.paths field in tsconfig.json. This is a major divergence from Node.js, which doesn't support any form of import path re-mapping.

}
}
2 changes: 1 addition & 1 deletion collections/invert_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Deno.test("invert() handles nested input", () => {
// @ts-expect-error - testing invalid input
invertTest({ a: "x", b: Object }, {
"x": "a",
"function Object() { [native code] }": "b",
[Object.toString()]: "b",
});
// @ts-expect-error - testing invalid input
invertTest({ a: "x", b: ["y", "z"] }, { "x": "a", "y,z": "b" });
Expand Down
1 change: 1 addition & 0 deletions deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"test:with-unsafe-proto": "deno test --unstable-http --unstable-webgpu --unstable-fs --unstable-unsafe-proto --doc --allow-all --parallel --coverage --trace-leaks --clean",
"test:browser": "git grep --name-only \"This module is browser compatible.\" | grep -v deno.json | grep -v .github/workflows | grep -v _tools | grep -v encoding/README.md | grep -v media_types/vendor/update.ts | xargs deno check --config browser-compat.tsconfig.json",
"test:node": "(cd _tools/node_test_runner && npm install) && node --import ./_tools/node_test_runner/register_deno_shim.mjs ./_tools/node_test_runner/run_test.mjs",
"test:bun": "(cd _tools/node_test_runner && bun install) && cp _tools/node_test_runner/tsconfig_for_bun.json ./tsconfig.json && bun test --require ./_tools/node_test_runner/register_deno_shim.mjs _tools/node_test_runner/run_test.mjs && rm tsconfig.json",
"fmt:licence-headers": "deno run --allow-read --allow-write ./_tools/check_licence.ts",
"lint:circular": "deno run --allow-env --allow-read --allow-write --allow-net=deno.land,jsr.io ./_tools/check_circular_package_dependencies.ts",
"lint:mod-exports": "deno run --allow-env --allow-read ./_tools/check_mod_exports.ts",
Expand Down
117 changes: 65 additions & 52 deletions fs/_node_fs_file_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { fileURLToPath } from "node:url";
const moduleDir = dirname(fileURLToPath(import.meta.url));
const testdataDir = resolve(moduleDir, "testdata");
const readTestFile = join(testdataDir, "copy_file.txt");
const isBun = navigator.userAgent.includes("Bun/");

Deno.test("FsFile object writes to a newly created file", async () => {
const tempDirPath = await makeTempDir({ prefix: "FsFile_write_" });
Expand Down Expand Up @@ -126,39 +127,47 @@ Deno.test("FsFile object returns the 'stat' of the file handle", async () => {
fh.close();
});

Deno.test("FsFile object handles a ReadableStream", async () => {
const fh = await open(readTestFile);
assert(fh.readable instanceof ReadableStream);
const chunks = [];
for await (const chunk of fh.readable) {
chunks.push(chunk);
}
assertEquals(chunks.length, 1);
if (chunks[0] != null) {
assertEquals(chunks[0].byteLength, 3);
}
});

Deno.test("FsFile object handles a WritableStream", async () => {
const tempDirPath = await makeTempDir({ prefix: "FsFile_WritableStream_" });
const testFile = join(tempDirPath, "testFile.txt");
const fh = await open(testFile, { create: true, write: true });
assert(fh.writable instanceof WritableStream);
const rs = new ReadableStream({
start(controller) {
const encoder = new TextEncoder();
controller.enqueue(encoder.encode("Hello,"));
controller.enqueue(encoder.encode(" Standard"));
controller.enqueue(encoder.encode(" Library"));
controller.close();
},
});
await rs.pipeTo(fh.writable);
const readText = await readTextFile(testFile);
assertEquals(readText, "Hello, Standard Library");

await remove(tempDirPath, { recursive: true });
});
Deno.test(
"FsFile object handles a ReadableStream",
{ ignore: isBun },
async () => {
const fh = await open(readTestFile);
assert(fh.readable instanceof ReadableStream);
const chunks = [];
for await (const chunk of fh.readable) {
chunks.push(chunk);
}
assertEquals(chunks.length, 1);
if (chunks[0] != null) {
assertEquals(chunks[0].byteLength, 3);
}
},
);

Deno.test(
"FsFile object handles a WritableStream",
{ ignore: isBun },
async () => {
const tempDirPath = await makeTempDir({ prefix: "FsFile_WritableStream_" });
const testFile = join(tempDirPath, "testFile.txt");
const fh = await open(testFile, { create: true, write: true });
assert(fh.writable instanceof WritableStream);
const rs = new ReadableStream({
start(controller) {
const encoder = new TextEncoder();
controller.enqueue(encoder.encode("Hello,"));
controller.enqueue(encoder.encode(" Standard"));
controller.enqueue(encoder.encode(" Library"));
controller.close();
},
});
await rs.pipeTo(fh.writable);
const readText = await readTextFile(testFile);
assertEquals(readText, "Hello, Standard Library");

await remove(tempDirPath, { recursive: true });
},
);

Deno.test("FsFile object changes access and modification times with utime", async () => {
const tempFile = await makeTempFile({ prefix: "FsFile_utime_" });
Expand Down Expand Up @@ -232,28 +241,32 @@ Deno.test("FsFile object synchronously reads from an existing file", () => {
fh.close();
});

Deno.test("FsFile object synchronously truncates a file to zero", () => {
const tempDirPath = makeTempDirSync({ prefix: "FsFile_truncateSync_" });
const testFile = join(tempDirPath, "testFile.txt");
let fh = openSync(testFile, { read: true, write: true, create: true });

const encoder = new TextEncoder();
const data = encoder.encode("Hello, Standard Library");
const writeBytes = fh.writeSync(data);
assertEquals(writeBytes, 23);
fh.close();
Deno.test(
"FsFile object synchronously truncates a file to zero",
{ ignore: isBun },
() => {
const tempDirPath = makeTempDirSync({ prefix: "FsFile_truncateSync_" });
const testFile = join(tempDirPath, "testFile.txt");
let fh = openSync(testFile, { read: true, write: true, create: true });

const encoder = new TextEncoder();
const data = encoder.encode("Hello, Standard Library");
const writeBytes = fh.writeSync(data);
assertEquals(writeBytes, 23);
fh.close();

fh = openSync(testFile, { read: true, write: true });
fh.truncateSync();
fh = openSync(testFile, { read: true, write: true });
fh.truncateSync();

const buf = new Uint8Array(10);
const readBytes = fh.readSync(buf);
// Reading a 0 byte file should return null at EOF.
assertEquals(readBytes, null);
fh.close();
const buf = new Uint8Array(10);
const readBytes = fh.readSync(buf);
// Reading a 0 byte file should return null at EOF.
assertEquals(readBytes, null);
fh.close();

removeSync(tempDirPath, { recursive: true });
});
removeSync(tempDirPath, { recursive: true });
},
);

Deno.test("FsFile object synchronously truncates files to multiple sizes", () => {
const tempDirPath = makeTempDirSync({ prefix: "FsFile_truncateSync_" });
Expand Down
10 changes: 6 additions & 4 deletions fs/unstable_chown_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { remove, removeSync } from "./unstable_remove.ts";
import { platform } from "node:os";
import { spawn } from "node:child_process";

const isBun = navigator.userAgent.includes("Bun/");

type IdResult = {
id: string;
code: number;
Expand Down Expand Up @@ -64,7 +66,7 @@ async function getUidAndGid(): Promise<{ uid: number; gid: number }> {

Deno.test({
name: "chown() changes user and group ids",
ignore: platform() === "win32",
ignore: platform() === "win32" || isBun,
fn: async () => {
const { uid, gid } = await getUidAndGid();
const tempFile = await makeTempFile({ prefix: "chown_" });
Expand All @@ -78,7 +80,7 @@ Deno.test({

Deno.test({
name: "chown() handles `null` id arguments",
ignore: platform() === "win32",
ignore: platform() === "win32" || isBun,
fn: async () => {
const { uid, gid } = await getUidAndGid();
const tempFile = await makeTempFile({ prefix: "chown_" });
Expand Down Expand Up @@ -115,7 +117,7 @@ Deno.test({

Deno.test({
name: "chownSync() changes user and group ids",
ignore: platform() === "win32",
ignore: platform() === "win32" || isBun,
fn: async () => {
const { uid, gid } = await getUidAndGid();
const tempFile = makeTempFileSync({ prefix: "chownSync_ " });
Expand All @@ -130,7 +132,7 @@ Deno.test({

Deno.test({
name: "chownSync() handles `null` id arguments",
ignore: platform() === "win32",
ignore: platform() === "win32" || isBun,
fn: async () => {
const { uid, gid } = await getUidAndGid();
const tempFile = makeTempFileSync({ prefix: "chownSync_" });
Expand Down
64 changes: 37 additions & 27 deletions fs/unstable_read_file_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { isDeno } from "./_utils.ts";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const isBun = navigator.userAgent.includes("Bun/");

const moduleDir = dirname(fileURLToPath(import.meta.url));
const testdataDir = resolve(moduleDir, "testdata");
const testFile = join(testdataDir, "copy_file.txt");
Expand Down Expand Up @@ -47,39 +49,47 @@ Deno.test("readFile() handles an AbortSignal", async () => {
assertEquals(error.name, "AbortError");
});

Deno.test("readFile() handles an AbortSignal with a reason", async () => {
const ac = new AbortController();
const reasonErr = new Error();
queueMicrotask(() => ac.abort(reasonErr));

const error = await assertRejects(async () => {
await readFile(testFile, { signal: ac.signal });
}, Error);

if (isDeno) {
assertEquals(error, ac.signal.reason);
} else {
assertEquals(error.cause, ac.signal.reason);
}
});
Deno.test(
"readFile() handles an AbortSignal with a reason",
{ ignore: isBun },
async () => {
const ac = new AbortController();
const reasonErr = new Error();
queueMicrotask(() => ac.abort(reasonErr));

Deno.test("readFile() handles an AbortSignal with a primitive reason value", async () => {
const ac = new AbortController();
const reasonErr = "Some string";
queueMicrotask(() => ac.abort(reasonErr));
const error = await assertRejects(async () => {
await readFile(testFile, { signal: ac.signal });
}, Error);

try {
await readFile(testFile, { signal: ac.signal });
unreachable();
} catch (error) {
if (isDeno) {
assertEquals(error, ac.signal.reason);
} else {
const errorValue = error as Error;
assertEquals(errorValue.cause, ac.signal.reason);
assertEquals(error.cause, ac.signal.reason);
}
}
});
},
);

Deno.test(
"readFile() handles an AbortSignal with a primitive reason value",
{ ignore: isBun },
async () => {
const ac = new AbortController();
const reasonErr = "Some string";
queueMicrotask(() => ac.abort(reasonErr));

try {
await readFile(testFile, { signal: ac.signal });
unreachable();
} catch (error) {
if (isDeno) {
assertEquals(error, ac.signal.reason);
} else {
const errorValue = error as Error;
assertEquals(errorValue.cause, ac.signal.reason);
}
}
},
);

Deno.test("readFile() handles cleanup of an AbortController", async () => {
const ac = new AbortController();
Expand Down
Loading
Loading