diff --git a/test/bundler/bun-build-api.test.ts b/test/bundler/bun-build-api.test.ts index c7ca18450820..015646fb4a13 100644 --- a/test/bundler/bun-build-api.test.ts +++ b/test/bundler/bun-build-api.test.ts @@ -1061,14 +1061,12 @@ describe("Bun.build", () => { // descriptor in the resolver. A second in-process Bun.build() used to reuse a // descriptor the first build had already closed, failing with EBADF. The test // host must also import the package so its fd is cached before the builds run. - test.concurrent.skipIf(isWindows)( - "repeated in-process builds of a symlinked package do not reuse a closed fd", - async () => { - using dir = tempDir("build-symlink-fd-cache", { - "vendor/pkg/package.json": `{"name":"pkg","version":"1.0.0","type":"module","exports":"./index.js"}`, - "vendor/pkg/index.js": `export const value = 1;\n`, - "entry.ts": `import { value } from "pkg";\nconsole.log(value);\n`, - "repro.test.ts": ` + test.concurrent("repeated in-process builds of a symlinked package do not reuse a closed fd", async () => { + using dir = tempDir("build-symlink-fd-cache", { + "vendor/pkg/package.json": `{"name":"pkg","version":"1.0.0","type":"module","exports":"./index.js"}`, + "vendor/pkg/index.js": `export const value = 1;\n`, + "entry.ts": `import { value } from "pkg";\nconsole.log(value);\n`, + "repro.test.ts": ` import { it } from "bun:test"; import { value } from "pkg"; void value; @@ -1082,22 +1080,21 @@ describe("Bun.build", () => { console.log("ALL_BUILDS_OK"); }); `, - }); - mkdirSync(join(String(dir), "node_modules"), { recursive: true }); - symlinkSync("../vendor/pkg", join(String(dir), "node_modules", "pkg")); + }); + mkdirSync(join(String(dir), "node_modules"), { recursive: true }); + symlinkSync("../vendor/pkg", join(String(dir), "node_modules", "pkg")); - await using proc = Bun.spawn({ - cmd: [bunExe(), "test", "repro.test.ts"], - env: bunEnv, - cwd: String(dir), - stdout: "pipe", - stderr: "pipe", - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect(stdout + stderr).toContain("ALL_BUILDS_OK"); - expect(exitCode).toBe(0); - }, - ); + await using proc = Bun.spawn({ + cmd: [bunExe(), "test", "repro.test.ts"], + env: bunEnv, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stdout + stderr).toContain("ALL_BUILDS_OK"); + expect(exitCode).toBe(0); + }); test.concurrent("errors are returned as an array", async () => { const x = await buildNoThrow({ diff --git a/test/cli/bake/bake.test.ts b/test/cli/bake/bake.test.ts index 734d25ce2cc3..83bee919ada6 100644 --- a/test/cli/bake/bake.test.ts +++ b/test/cli/bake/bake.test.ts @@ -464,8 +464,7 @@ test.concurrent.each([ expect(response).toBe("200 1"); }); -// Creating a symlink on Windows needs a privilege that CI does not have. -test.concurrent.skipIf(isWindows)("bun bake serves pages that are symlinks or in a symlinked folder", async () => { +test.concurrent("bun bake serves pages that are symlinks or in a symlinked folder", async () => { using dir = tempDir("bake-command-symlink", { "shared/page.html": `

Shared

`, "shared/docs/intro.html": `

Intro

`, @@ -792,9 +791,8 @@ test.concurrent("bun bake runs the SIGTERM handlers before it starts again for b await output.waitFor(/SIGTERM before restart/); }); -// On a terminal, `bun bake` adds its own SIGTERM listener, which exits. Bun.spawn's `terminal` is -// POSIX only. -test.concurrent.skipIf(isWindows)( +// On a terminal, `bun bake` adds its own SIGTERM listener, which exits. +test.concurrent( "on a terminal, bun bake runs every SIGTERM listener before it starts again for bake.config.ts", async () => { const server = (version: string) => ` diff --git a/test/cli/check/check.test.ts b/test/cli/check/check.test.ts index 57ca2153d3a0..9aa861824a1d 100644 --- a/test/cli/check/check.test.ts +++ b/test/cli/check/check.test.ts @@ -2911,7 +2911,7 @@ export const a: number[] = [file, one, two]; }); // Opening such a pipe blocks until a writer opens it. package.json is opened to look for a "check" script. - test.skipIf(isWindows)("doesn't hang when package.json is a symlink to a named pipe with no writer", async () => { + test("doesn't hang when package.json is a symlink to a named pipe with no writer", async () => { using dir = project({ "a.ts": `export {};\n` }); expect(Bun.spawnSync(["mkfifo", join(String(dir), "pipe")]).exitCode).toBe(0); symlinkSync("pipe", join(String(dir), "package.json")); @@ -3035,7 +3035,7 @@ export const a: number[] = [file, one, two]; expect(exitCode).toBe(1); }); - test.skipIf(process.platform === "win32")("ignores a named pipe in the project directory", async () => { + test("ignores a named pipe in the project directory", async () => { using dir = project({ "a.ts": `export const a: string = 1;\n` }); // Reading a pipe without a writer blocks forever. expect(Bun.spawnSync(["mkfifo", join(String(dir), "pipe.ts")]).exitCode).toBe(0); @@ -24276,28 +24276,25 @@ describe.concurrent("--check", () => { }); // The path is compared as `bun run` spells it. - test.skipIf(isWindows)( - "`bun check` type checks in a `check` script started by a Bun that sets only BUN_INTERNAL_CHECK_SCRIPTS", - async () => { - using dir = project({ - "package.json": JSON.stringify({ scripts: { check: "echo the script ran" } }), - "tsconfig.json": tsconfig, - "a.ts": `export const a: number = "1";\n`, - }); - const cwd = String(dir); - const results = await Promise.all([ - run(cwd, ["check", "--pretty", "false"], { BUN_INTERNAL_CHECK_SCRIPTS: `${Buffer.byteLength(cwd)}:${cwd}` }), - // Another package's path. - run(cwd, ["check", "--pretty", "false"], { BUN_INTERNAL_CHECK_SCRIPTS: `2:/x` }), - ]); - expect( - results.map(it => [it.stdout.includes("TS2322"), it.stdout.includes("the script ran"), it.exitCode]), - ).toEqual([ - [true, false, 1], - [false, true, 0], - ]); - }, - ); + test("`bun check` type checks in a `check` script started by a Bun that sets only BUN_INTERNAL_CHECK_SCRIPTS", async () => { + using dir = project({ + "package.json": JSON.stringify({ scripts: { check: "echo the script ran" } }), + "tsconfig.json": tsconfig, + "a.ts": `export const a: number = "1";\n`, + }); + const cwd = String(dir); + const results = await Promise.all([ + run(cwd, ["check", "--pretty", "false"], { BUN_INTERNAL_CHECK_SCRIPTS: `${Buffer.byteLength(cwd)}:${cwd}` }), + // Another package's path. + run(cwd, ["check", "--pretty", "false"], { BUN_INTERNAL_CHECK_SCRIPTS: `2:/x` }), + ]); + expect( + results.map(it => [it.stdout.includes("TS2322"), it.stdout.includes("the script ran"), it.exitCode]), + ).toEqual([ + [true, false, 1], + [false, true, 0], + ]); + }); test("--check with --filter, --parallel or --sequential checks the project before the scripts", async () => { using dir = project({ diff --git a/test/cli/create/create-jsx.test.ts b/test/cli/create/create-jsx.test.ts index b9883c9ac35a..60944d07edb6 100644 --- a/test/cli/create/create-jsx.test.ts +++ b/test/cli/create/create-jsx.test.ts @@ -126,7 +126,7 @@ for (const development of [true, false]) { }); }); - test.todoIf(isCI || isWindows)("dev server", async () => { + test.todoIf(isCI)("dev server", async () => { console.log({ dir }); await using process = Bun.spawn([bunExe(), "create", "./index.jsx"], { cwd: dir, @@ -193,7 +193,7 @@ for (const development of [true, false]) { }); }); - test.todoIf(isCI || isWindows)("dev server", async () => { + test.todoIf(isCI)("dev server", async () => { const process = Bun.spawn([bunExe(), "create", "./index.tsx"], { cwd: dir, env: env, @@ -259,7 +259,7 @@ for (const development of [true, false]) { }); }); - test.todoIf(isCI || isWindows)("dev server", async () => { + test.todoIf(isCI)("dev server", async () => { const process = Bun.spawn([bunExe(), "create", "./index.tsx"], { cwd: dir, env: env, diff --git a/test/cli/format/format.test.ts b/test/cli/format/format.test.ts index bf602778695e..a61b8cf2b154 100644 --- a/test/cli/format/format.test.ts +++ b/test/cli/format/format.test.ts @@ -521,20 +521,17 @@ describe.concurrent("bun fmt", () => { expect(result.exitCode).toBe(0); }); - // A file name can have at most 255 bytes. On Windows the path would also be too long. - test.skipIf(isWindows)( - "can have the longest possible name, and is still replaced rather than written in place", - async () => { - const name = `${Buffer.alloc(252, "a")}.ts`; - using dir = tempDir("bun-format", { [name]: ugly }); - const before = statSync(join(String(dir), name)).ino; - const { exitCode } = await write(String(dir), []); - expect(read(String(dir), name)).toBe(formatted); - expect(statSync(join(String(dir), name)).ino).not.toBe(before); - expect(readdirSync(String(dir))).toEqual([name]); - expect(exitCode).toBe(0); - }, - ); + // A file name can have at most 255 bytes. + test("can have the longest possible name, and is still replaced rather than written in place", async () => { + const name = `${Buffer.alloc(252, "a")}.ts`; + using dir = tempDir("bun-format", { [name]: ugly }); + const before = statSync(join(String(dir), name)).ino; + const { exitCode } = await write(String(dir), []); + expect(read(String(dir), name)).toBe(formatted); + expect(statSync(join(String(dir), name)).ino).not.toBe(before); + expect(readdirSync(String(dir))).toEqual([name]); + expect(exitCode).toBe(0); + }); // For example in a container with a user's project mounted into it. test.skipIf(!isRoot)("keeps its owner if root formats it", async () => { @@ -552,7 +549,7 @@ describe.concurrent("bun fmt", () => { }); // Prettier doesn't need write access to the directory. - test.skipIf(isWindows || isRoot)("can be in a read-only directory", async () => { + test.skipIf(isRoot)("can be in a read-only directory", async () => { using dir = tempDir("bun-format", { "src/a.js": ugly }); chmodSync(join(String(dir), "src"), 0o555); try { @@ -5731,15 +5728,12 @@ exports.format = async text => "by Prettier"; expect(result.exitCode).toBe(2); }); - test.skipIf(isWindows)( - "/dev/null works as an ignore file without patterns and as a configuration without options", - async () => { - const files = { ".prettierignore": "a.js\n", ".prettierrc": `{ "semi": false }\n`, "a.js": formatted }; - expect(await different(files, ["."])).toEqual([]); - expect(await different(files, ["--ignore-path", "/dev/null", "."])).toEqual(["a.js"]); - expect(await different(files, ["--ignore-path", "/dev/null", "--config", "/dev/null", "."])).toEqual([]); - }, - ); + test("/dev/null works as an ignore file without patterns and as a configuration without options", async () => { + const files = { ".prettierignore": "a.js\n", ".prettierrc": `{ "semi": false }\n`, "a.js": formatted }; + expect(await different(files, ["."])).toEqual([]); + expect(await different(files, ["--ignore-path", "/dev/null", "."])).toEqual(["a.js"]); + expect(await different(files, ["--ignore-path", "/dev/null", "--config", "/dev/null", "."])).toEqual([]); + }); // This means "never break a line". Prettier accepts every integer from 0 up, and Infinity. test.each([ @@ -6162,7 +6156,7 @@ exports.format = async text => "by Prettier"; expect(result.exitCode).toBe(0); }); - test.skipIf(isWindows)("a named pipe whose name ends in .js is skipped, like in Prettier", async () => { + test("a named pipe whose name ends in .js is skipped, like in Prettier", async () => { const before = (dir: string) => expect(Bun.spawnSync(["mkfifo", join(dir, "pipe.js")]).exitCode).toBe(0); expect(await different({ "a.js": ugly }, [], { before })).toEqual(["a.js"]); }); @@ -6634,7 +6628,7 @@ exports.format = async text => "by Prettier"; // A case's guard used to be printed without a stack check, so the stack overflowed at depths between the printer's // limit and the parser's. The threads that format files have stack to spare, so this uses the main thread with half // of its usual stack. - test.skipIf(isWindows || isDebug || isASAN).concurrent.each([4_500, 4_612, 4_728, 4_846, 4_967, 5_091])( + test.skipIf(isDebug || isASAN).concurrent.each([4_500, 4_612, 4_728, 4_846, 4_967, 5_091])( "Flow's match with %d nested guards, on a 4 MB stack", async depth => { using dir = tempDir("bun-format-small-stack", {}); diff --git a/test/cli/format/oxfmt-cli/oxfmt-cli.test.ts b/test/cli/format/oxfmt-cli/oxfmt-cli.test.ts index e4e58c346444..eab664bb18f1 100644 --- a/test/cli/format/oxfmt-cli/oxfmt-cli.test.ts +++ b/test/cli/format/oxfmt-cli/oxfmt-cli.test.ts @@ -3,7 +3,7 @@ // `--list-different` list. // `cases.json` is written by `test/cli/lint/oracle/driver/oxfmt-fixtures.mjs --record`. import { afterAll, describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; import { readFileSync, symlinkSync } from "node:fs"; import { join } from "node:path"; import { endChildren, spawn } from "../../children"; @@ -21,7 +21,7 @@ describe.concurrent("bun fmt behaves like oxfmt", () => { for (const it of cases) { const files = { ...(fixtures as Record)[it.name], ...it.gitignore } as Files; const links = Object.entries(files).filter(([, file]) => typeof file !== "string") as [string, { link: string }][]; - test.skipIf(isWindows && links.length > 0)(`${it.name}/${it.index}: ${it.args.join(" ")}`, async () => { + test(`${it.name}/${it.index}: ${it.args.join(" ")}`, async () => { const texts = Object.entries(files).filter(([, file]) => typeof file === "string") as [string, string][]; // Without a configuration file, `bun fmt` behaves like Prettier. An empty one above the fixtures makes it behave // like oxfmt. diff --git a/test/cli/format/prettier-cli/prettier-cli.test.ts b/test/cli/format/prettier-cli/prettier-cli.test.ts index 68df7a389d7e..84b1c5495e21 100644 --- a/test/cli/format/prettier-cli/prettier-cli.test.ts +++ b/test/cli/format/prettier-cli/prettier-cli.test.ts @@ -4,7 +4,7 @@ // input. `bun fmt` writes unless told otherwise, so Prettier was given `--write` where the command line only prints. // `cases.json` is written by `test/cli/lint/oracle/driver/prettier-fixtures.mjs --record`. import { afterAll, describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; import { readFileSync, symlinkSync } from "node:fs"; import { join } from "node:path"; import { endChildren, spawn } from "../../children"; @@ -25,7 +25,7 @@ describe.concurrent("bun fmt behaves like Prettier", () => { const links = Object.entries(files).filter(([, file]) => typeof file !== "string") as [string, { link: string }][]; // Links, or a name that Windows doesn't allow. const needsPosix = links.length > 0 || Object.keys(files).some(name => /[<>:"|?*\\]/.test(name)); - test.skipIf(isWindows && needsPosix)(`${it.test}: ${it.directory} $ ${it.args.join(" ")}`, async () => { + test(`${it.test}: ${it.directory} $ ${it.args.join(" ")}`, async () => { const texts = Object.entries(files).filter(([, file]) => typeof file === "string") as [string, string][]; using dir = tempDir("prettier-cli", Object.fromEntries(texts.map(([name, text]) => [`${top}/${name}`, text]))); for (const [name, { link }] of links) symlinkSync(link, join(String(dir), top, name)); diff --git a/test/cli/format/tailwind/tailwind.test.ts b/test/cli/format/tailwind/tailwind.test.ts index 7c167152d3c3..126d0815f026 100644 --- a/test/cli/format/tailwind/tailwind.test.ts +++ b/test/cli/format/tailwind/tailwind.test.ts @@ -5,7 +5,7 @@ // // The order comes from the project's `tailwindcss` package. Here that's a stub that knows the classes in order.json. import { afterAll, describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isLinux, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, isLinux, tempDir } from "harness"; import { chmodSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { endChildren, spawn } from "../../children"; @@ -163,7 +163,7 @@ if (now.join() !== before.join()) throw new Error("There is " + now.filter(it => }); // As with Nix, an image layer owned by root, or a sandbox. Skipped as root, which can write anywhere. - test.skipIf(isWindows || process.getuid?.() === 0)("works with read-only packages", async () => { + test.skipIf(process.getuid?.() === 0)("works with read-only packages", async () => { using dir = tempDir("bun-format-tailwind", { ...version4, ".oxfmtrc.json": '{ "sortTailwindcss": {} }\n', diff --git a/test/cli/hot/watch.test.ts b/test/cli/hot/watch.test.ts index 4a6a5c2908b7..aeb08fd0a7ca 100644 --- a/test/cli/hot/watch.test.ts +++ b/test/cli/hot/watch.test.ts @@ -1,10 +1,10 @@ import { spawn } from "bun"; import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, forEachLine, isBroken, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, forEachLine, tempDir } from "harness"; import { writeFile } from "node:fs/promises"; import { join } from "node:path"; -describe.todoIf(isBroken && isWindows)("--watch works", async () => { +describe("--watch works", async () => { for (const watchedFile of ["entry.js", "tmp.js"]) { test(`with ${watchedFile}`, async () => { await using tmpdir_ = tempDir("watch-fixture", { diff --git a/test/cli/inspect/BunFrontendDevServer.test.ts b/test/cli/inspect/BunFrontendDevServer.test.ts index a5301cac32e9..f72b0ab3f142 100644 --- a/test/cli/inspect/BunFrontendDevServer.test.ts +++ b/test/cli/inspect/BunFrontendDevServer.test.ts @@ -3,7 +3,7 @@ import { decodeSerializedError } from "bake/error-serialization.ts"; import { Subprocess, spawn } from "bun"; import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import fs from "fs"; -import { bunExe, bunEnv as env, isPosix, tempDir, tmpdirSync } from "harness"; +import { bunExe, bunEnv as env, tempDir, tmpdirSync } from "harness"; import path, { join } from "node:path"; import { InspectorSession, connect } from "./junit-reporter"; import { SocketFramer } from "./socket-framer"; @@ -72,7 +72,7 @@ class BunFrontendDevServerSession extends InspectorSession { } } -describe.if(isPosix)("BunFrontendDevServer inspector protocol", () => { +describe("BunFrontendDevServer inspector protocol", () => { let devServerProcess: Subprocess<"ignore", "pipe", "pipe">; let serverUrl: URL; let session: BunFrontendDevServerSession; diff --git a/test/cli/inspect/test-reporter.test.ts b/test/cli/inspect/test-reporter.test.ts index 607d04a5ea37..5b898a46739f 100644 --- a/test/cli/inspect/test-reporter.test.ts +++ b/test/cli/inspect/test-reporter.test.ts @@ -1,6 +1,6 @@ import { Subprocess, spawn, write } from "bun"; import { afterEach, describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isPosix, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; import { join } from "node:path"; import { InspectorSession, connect } from "./junit-reporter"; import { SocketFramer } from "./socket-framer"; @@ -165,7 +165,7 @@ class TestReporterSession extends InspectorSession { } } -describe.if(isPosix)("TestReporter inspector protocol", () => { +describe("TestReporter inspector protocol", () => { let proc: Subprocess<"ignore", "pipe", "pipe"> | undefined; let socket: ReturnType extends Promise ? T : never; diff --git a/test/cli/install/bun-add-filter.test.ts b/test/cli/install/bun-add-filter.test.ts index e3b8a3a9289a..aca9546b44d1 100644 --- a/test/cli/install/bun-add-filter.test.ts +++ b/test/cli/install/bun-add-filter.test.ts @@ -2408,7 +2408,7 @@ test.concurrent("remove --filter --dry-run touches nothing", async () => { expect(await exists(join(dir, "node_modules", "no-deps", "package.json"))).toBeTrue(); }); -test.concurrent.skipIf(isWindows || isRoot)( +test.concurrent.skipIf(isRoot)( "an unwritable target is reported by name, the rest is still written, exit code 1", async () => { const dir = await makeMonorepo(); diff --git a/test/cli/install/bun-install-git-deps.test.ts b/test/cli/install/bun-install-git-deps.test.ts index 47c5ebc990a4..9b07689f74f8 100644 --- a/test/cli/install/bun-install-git-deps.test.ts +++ b/test/cli/install/bun-install-git-deps.test.ts @@ -675,7 +675,7 @@ function expectOneStoreEntry(project: string, aliases: string[]) { // folder taken moves it away (`move_to_cache_directory` in extract_tarball.rs) while another one is still reading its // package.json. Occasionally that one creates a package with no name and no dependencies. for (const linker of ["isolated", "hoisted"] as const) { - test.concurrent.skipIf(isWindows)( + test.concurrent( `${linker} linker installs github: refs that point to one commit as one package`, async () => { using dir = tempDir(`github-one-commit-${linker}`, {}); @@ -739,7 +739,7 @@ for (const linker of ["isolated", "hoisted"] as const) { // The same for a repository whose package.json has no name, or that has no package.json. The package is named after the // repository, for every ref. -test.concurrent.skipIf(isWindows).each([ +test.concurrent.each([ ["a package.json without a name", { "package.json": JSON.stringify({ version: "1.0.0" }) }], ["no package.json", {}], ])( @@ -2826,7 +2826,7 @@ test.concurrent.each([ }); // It's neither a URL nor an scp-style path, so git would read it as a directory in the project and clone that. -test.concurrent.skipIf(isWindows)("does not hand a bare word to git clone", async () => { +test.concurrent("does not hand a bare word to git clone", async () => { const { calls, stderr, exitCode } = await failingGitCalls("git+:latest"); expect(calls).toEqual([]); // It's a git dependency, so the registry wasn't asked for a `git+:latest` tag. @@ -3495,7 +3495,7 @@ test.concurrent("bun update moves a git dependency without a committish to the n }); // Ubuntu 22.04 has git 2.34, Debian 11 has 2.30. -test.concurrent.skipIf(isWindows)("clones with a git older than 2.39, which refuses --bare with --origin", async () => { +test.concurrent("clones with a git older than 2.39, which refuses --bare with --origin", async () => { using dir = tempDir("git-dep-old-git", { "bin/git": `#!/bin/sh for arg in "$@"; do @@ -3932,7 +3932,7 @@ test.concurrent("a git dependency with a submodule, under the user's own setting }); // How git fails when it can't use the clone depends on its version. -test.concurrent.skipIf(isWindows)("clones again after a git fetch that did not fail at the remote", async () => { +test.concurrent("clones again after a git fetch that did not fail at the remote", async () => { using dir = tempDir("git-dep-fetch-exit-code", {}); const root = String(dir); const bare = await makeSharedRepo(root, [{ name: "dep", branch: "main" }]); diff --git a/test/cli/install/bun-install-lifecycle-scripts.test.ts b/test/cli/install/bun-install-lifecycle-scripts.test.ts index b2b134c55469..4c1b624a7eb8 100644 --- a/test/cli/install/bun-install-lifecycle-scripts.test.ts +++ b/test/cli/install/bun-install-lifecycle-scripts.test.ts @@ -3073,7 +3073,7 @@ describe.concurrent.each(["hoisted", "isolated"] as const)("the install after a // One at a time, so that the third script to start is the one that fails. Two have finished and aren't run again, and // one hasn't started. - test.skipIf(isWindows).each([ + test.each([ { title: "preinstall", script: "preinstall", args: [] }, { title: "install", script: "install", args: [] }, { title: "postinstall", script: "postinstall", args: [] }, @@ -3103,7 +3103,7 @@ describe.concurrent.each(["hoisted", "isolated"] as const)("the install after a expect(await install("third.txt", 100, ...args)).toEqual({ errors: [], ran: [], exitCode: 0 }); }); - test.skipIf(isWindows)("all at the same time: every script runs in the end", async () => { + test("all at the same time: every script runs in the end", async () => { const { ctx, install } = await setup("postinstall"); using _ = ctx; @@ -3166,7 +3166,7 @@ describe.concurrent.each(["hoisted", "isolated"] as const)("the install after a // The root's `dep` is a different package. With the hoisted linker, each parent has its own copy, and the first one // fails before the walk reaches the last. - test.skipIf(isWindows)("copies that the failed install hadn't reached", async () => { + test("copies that the failed install hadn't reached", async () => { const parents = Array.from({ length: 40 }, (_, i) => `parent-${i}`); using ctx = await setupScriptPackages(linker, { scripted: { scripts: { postinstall: `echo ran >> "$RUNS" && test -z "$FAIL"` } }, diff --git a/test/cli/install/bun-install-native-binlink.test.ts b/test/cli/install/bun-install-native-binlink.test.ts index edbe55172151..f114c3f36762 100644 --- a/test/cli/install/bun-install-native-binlink.test.ts +++ b/test/cli/install/bun-install-native-binlink.test.ts @@ -120,7 +120,7 @@ describe.concurrent("native binlink optimization", () => { // bunx takes the bin of a package from that package's own package.json. Here the entry in `.bin` leads into // the platform package, and the file of the main package keeps the mode of its tarball. - test.skipIf(isWindows).each(["a folder", "a link to another folder"])( + test.each(["a folder", "a link to another folder"])( `bunx runs the platform-specific bin with linker ${linker} and node_modules %s`, async nodeModules => { let env = { ...bunEnv }; @@ -294,7 +294,7 @@ describe.concurrent("native binlink optimization", () => { } // The link leaves node_modules, so its text depends on where node_modules really is. - test.skipIf(isWindows)("in a workspace whose node_modules is a link to another folder", async () => { + test("in a workspace whose node_modules is a link to another folder", async () => { const { packageDir, packageJson } = await verdaccio.createTestDir(); const env = { ...bunEnv, BUN_INSTALL_CACHE_DIR: join(packageDir, ".bun-cache") }; const app = join(packageDir, "packages", "app"); diff --git a/test/cli/install/bun-install-patch.test.ts b/test/cli/install/bun-install-patch.test.ts index b396c5f6645e..af11dc683469 100644 --- a/test/cli/install/bun-install-patch.test.ts +++ b/test/cli/install/bun-install-patch.test.ts @@ -6,7 +6,6 @@ import { bunExe, DirectoryTree, isRoot, - isWindows, normalizeBunSnapshot as normalizeBunSnapshot_, runBunInstall, tempDir, @@ -470,7 +469,7 @@ diff --git a/package.json b/package.json // The new directories used to get the file's mode (0644), which only root can enter // (CAP_DAC_OVERRIDE), so as root the install runs without that capability. const setpriv = isRoot ? Bun.which("setpriv") : null; - test.skipIf(isWindows || (isRoot && !setpriv))( + test.skipIf(isRoot && !setpriv)( "should apply a patch that adds a file in a directory the package does not have", async () => { await using project = createProject("patch-new-dir", { @@ -1313,11 +1312,7 @@ describe("patchedDependencies path longer than the path buffer", () => { }); const [, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - // The path is handed to the OS as is. POSIX rejects anything longer than - // PATH_MAX with ENAMETOOLONG; Windows may report it as missing instead. - if (!isWindows) { - expect(stderr).toContain("error: failed to read patch file: ENAMETOOLONG: "); - } + expect(stderr).toContain("error: failed to read patch file: ENAMETOOLONG: "); expect(stderr).toContain(`${longName}.patch`); expect(exitCode).toBe(1); }); diff --git a/test/cli/install/bun-install-registry.test.ts b/test/cli/install/bun-install-registry.test.ts index 67ffcbc8e71d..dc281d7c93bf 100644 --- a/test/cli/install/bun-install-registry.test.ts +++ b/test/cli/install/bun-install-registry.test.ts @@ -20,7 +20,6 @@ import { bunEnv, bunExe, isDebug, - isFlaky, isLinux, isMacOS, isRoot, @@ -4005,7 +4004,7 @@ describe.concurrent("binaries", () => { expect(exitCode).toBe(0); }); - test.skipIf(isWindows)("a stale link, a file and a directory in .bin are replaced", async () => { + test("a stale link, a file and a directory in .bin are replaced", async () => { const { packageDir, packageJson, env } = await setupTest(); const binDir = join(packageDir, "node_modules", ".bin"); await Promise.all([ @@ -7862,7 +7861,7 @@ describe.concurrent("hoisting", async () => { }); }); - test.serial.todoIf(isFlaky && isWindows)("text lockfile is hoisted", async () => { + test.serial("text lockfile is hoisted", async () => { const { packageDir, packageJson, env } = await setupTest(); // Each dependency depends on 'hoist-lockfile-shared'. // 1 - "*" diff --git a/test/cli/install/bun-install-staging.test.ts b/test/cli/install/bun-install-staging.test.ts index 983d49e458cc..6d6900373d12 100644 --- a/test/cli/install/bun-install-staging.test.ts +++ b/test/cli/install/bun-install-staging.test.ts @@ -792,29 +792,26 @@ for (const linker of ["hoisted", "isolated"]) { // It replaces a link at node_modules/@scope by a directory now, and the isolated // linker moves a node_modules it did not make aside first. for (const linker of ["hoisted", "isolated"]) { - test.concurrent.skipIf(isWindows)( - `${linker} linker: a dangling node_modules/@scope symlink does not hang the install`, - async () => { - const depTarball = await new Bun.Archive( - { "package/package.json": JSON.stringify({ name: "@scope/dep", version: "1.0.0" }) }, - { compress: "gzip" }, - ).bytes(); - using dir = tempDir(`staging-dangling-scope-${linker}`, { - "package.json": JSON.stringify({ name: "app", dependencies: { "@scope/dep": "file:./dep-1.0.0.tgz" } }), - "dep-1.0.0.tgz": Buffer.from(depTarball), - "node_modules": {}, - "bunfig.toml": ({ root }) => Bun.TOML.stringify({ install: { cache: join(root, ".bun-cache"), linker } }), - }); - const root = String(dir); - symlinkSync(join(root, "does-not-exist"), join(root, "node_modules", "@scope")); + test.concurrent(`${linker} linker: a dangling node_modules/@scope symlink does not hang the install`, async () => { + const depTarball = await new Bun.Archive( + { "package/package.json": JSON.stringify({ name: "@scope/dep", version: "1.0.0" }) }, + { compress: "gzip" }, + ).bytes(); + using dir = tempDir(`staging-dangling-scope-${linker}`, { + "package.json": JSON.stringify({ name: "app", dependencies: { "@scope/dep": "file:./dep-1.0.0.tgz" } }), + "dep-1.0.0.tgz": Buffer.from(depTarball), + "node_modules": {}, + "bunfig.toml": ({ root }) => Bun.TOML.stringify({ install: { cache: join(root, ".bun-cache"), linker } }), + }); + const root = String(dir); + symlinkSync(join(root, "does-not-exist"), join(root, "node_modules", "@scope")); - const { stderr, exitCode } = await install(root); - expect(stderr).not.toContain("error"); - expect(readdirSync(join(root, "node_modules", "@scope"))).toEqual(["dep"]); - expect(lstatSync(join(root, "node_modules", "@scope")).isDirectory()).toBe(true); - expect(exitCode).toBe(0); - }, - ); + const { stderr, exitCode } = await install(root); + expect(stderr).not.toContain("error"); + expect(readdirSync(join(root, "node_modules", "@scope"))).toEqual(["dep"]); + expect(lstatSync(join(root, "node_modules", "@scope")).isDirectory()).toBe(true); + expect(exitCode).toBe(0); + }); } // When linking fails midway, bun sees the error itself instead of being killed, @@ -1152,7 +1149,7 @@ describe("hoisted linker: packages linked by the thread pool", () => { }); // The pool keeps the node_modules of a batch open. Each `host-N` has one, for its `inner`. - test.concurrent.skipIf(isWindows)("a low limit on open files does not fail the install", async () => { + test.concurrent("a low limit on open files does not fail the install", async () => { const hosts = Array.from({ length: 60 }, (_, i) => `host-${i}`); const files = ["index.js"]; using registry = serveRegistry({ diff --git a/test/cli/install/bun-install.test.ts b/test/cli/install/bun-install.test.ts index 011399d13c7a..f49622a93d86 100644 --- a/test/cli/install/bun-install.test.ts +++ b/test/cli/install/bun-install.test.ts @@ -2146,7 +2146,7 @@ describe.concurrent("bun-install", () => { // buffer (4096 bytes on Linux, 1024 on macOS), so aliases a few bytes short of it used to crash // the install right there. A name over 255 bytes no longer gets that far: it is refused when it is read. // On Windows the buffer is far larger than any path the OS accepts. - describe.concurrent.skipIf(isWindows)("dependency alias that fills the path buffer", () => { + describe.concurrent("dependency alias that fills the path buffer", () => { const alias = Buffer.alloc((isLinux ? 4096 : 1024) - 6, "a").toString(); async function installIntoExistingNodeModules(cwd: string) { @@ -16065,7 +16065,7 @@ it.concurrent(doesNotCreateCacheEntryOutside, async () => { // collide under the seed-0 std.Wyhash that keys the folder-resolution dedupe // map must each resolve to their own package, not share one identity. // https://github.com/oven-sh/bun/issues/32741 -it.concurrent.skipIf(isWindows)("file: deps with colliding abs-path hashes resolve to distinct packages", async () => { +it.concurrent("file: deps with colliding abs-path hashes resolve to distinct packages", async () => { using dir = tempDir("folder-resolution-collision", { "package.json": JSON.stringify({ name: "victim", version: "0.0.0" }), }); @@ -16119,7 +16119,7 @@ it.concurrent.skipIf(isWindows)("file: deps with colliding abs-path hashes resol }); // The lockfile interns every string over 8 bytes in a pool keyed by Wyhash11(0, bytes). -it.concurrent.skipIf(isWindows)("two specifiers whose hashes collide stay two strings", async () => { +it.concurrent("two specifiers whose hashes collide stay two strings", async () => { const specA = "./t/m3daaaaaaaaaaaaaaaaaaaaaaaaa.7aQs_ePaaaaaaaaw9Aaaaaaaaaaaaaa.tgz"; const specB = "./t/m3daaaaaaaaaaaaaaaaaaaaaaaaa.7aQs_ePbaaaaaaaw9Aaaaaaaaaaaaaa.tgz"; expect(specA).not.toBe(specB); diff --git a/test/cli/install/bun-link.test.ts b/test/cli/install/bun-link.test.ts index 0f98bb499114..30fcb1383c8a 100644 --- a/test/cli/install/bun-link.test.ts +++ b/test/cli/install/bun-link.test.ts @@ -1026,7 +1026,7 @@ describe("global bin dir", () => { "elsewhere.txt": "", }); const binDir = join(String(bunInstall), "bin"); - if (!isWindows) await symlink(join(String(bunInstall), "elsewhere.txt"), join(binDir, "foreign")); + await symlink(join(String(bunInstall), "elsewhere.txt"), join(binDir, "foreign")); // Bins are linked in map order: the taken names first, then the free one. using pkg = tempDir( "link-pkg", @@ -1040,7 +1040,7 @@ describe("global bin dir", () => { expect(await readFile(join(binDir, binEntry("bun")), "utf8")).toBe("the bun binary"); if (isWindows) expect(await exists(join(binDir, "bun.bunx"))).toBeFalse(); expect(await readFile(join(binDir, binEntry("adir"), "inner.txt"), "utf8")).toBe("keep"); - if (!isWindows) expect(await readlink(join(binDir, "foreign"))).toBe(join(String(bunInstall), "elsewhere.txt")); + expect(await readlink(join(binDir, "foreign"))).toBe(join(String(bunInstall), "elsewhere.txt")); expect(exitCode).toBe(1); }); @@ -1126,7 +1126,7 @@ describe("global bin dir", () => { await writeFile(join(binDir, binEntry("bun")), "the bun binary"); await mkdir(join(binDir, binEntry("adir"))); await writeFile(join(binDir, binEntry("adir"), "inner.txt"), "keep"); - if (!isWindows) await symlink(join(String(bunInstall), "elsewhere.txt"), join(binDir, "foreign")); + await symlink(join(String(bunInstall), "elsewhere.txt"), join(binDir, "foreign")); await writeFile( join(String(pkg), "package.json"), pkgFiles("own-bin", { "own-bin": "cli.js", bun: "cli.js", adir: "cli.js", foreign: "cli.js" })["package.json"], @@ -1139,7 +1139,7 @@ describe("global bin dir", () => { if (isWindows) expect(await exists(join(binDir, "own-bin.bunx"))).toBeFalse(); expect(await readFile(join(binDir, binEntry("bun")), "utf8")).toBe("the bun binary"); expect(await readFile(join(binDir, binEntry("adir"), "inner.txt"), "utf8")).toBe("keep"); - if (!isWindows) expect(await readlink(join(binDir, "foreign"))).toBe(join(String(bunInstall), "elsewhere.txt")); + expect(await readlink(join(binDir, "foreign"))).toBe(join(String(bunInstall), "elsewhere.txt")); expect(result.exitCode).toBe(0); }); }); diff --git a/test/cli/install/bun-pack.test.ts b/test/cli/install/bun-pack.test.ts index 81778503351c..86085e4bcd4d 100644 --- a/test/cli/install/bun-pack.test.ts +++ b/test/cli/install/bun-pack.test.ts @@ -127,7 +127,7 @@ test.concurrent("the archive is not padded to a full tar record", async () => { // pack only reads package.json, so a file that is readable but not writable (a read-only // checkout, a file owned by another user) must not stop it. -test.concurrent.skipIf(isWindows)("read-only package.json", async () => { +test.concurrent("read-only package.json", async () => { using dir = tempDir("pack-read-only", { "package.json": JSON.stringify({ name: "pack-read-only", version: "1.2.3" }), "index.js": "console.log('hello ./index.js')", @@ -1243,7 +1243,7 @@ describe.concurrent("something already exists at the tarball's destination", () }); // Serial: `unprivilegedSpawnOptions` may change and restore the modes of directories above the test's own. - test.serial.skipIf(isWindows)("a writable file in a read-only folder", async () => { + test.serial("a writable file in a read-only folder", async () => { using dir = tempDir("pack-over-locked", { ...files, "pkg/out/pack-over-1.0.0.tgz": "an earlier tarball" }); using unprivileged = unprivilegedSpawnOptions(String(dir)); await chmod(join(dir, "pkg", "out"), 0o555); @@ -2984,7 +2984,7 @@ describe.concurrent("bundledDependencies", () => { // The isolated linker's layout: node_modules has links into a store, where a package's dependencies are links next to // it. `y` goes next to `x`, unless the packed package has its own `y` that it doesn't bundle. - test.skipIf(isWindows).each([ + test.each([ { name: "x", own: {}, y: "node_modules/y" }, { name: "@s/x", own: {}, y: "node_modules/y" }, { name: "x", own: { y: "2.0.0" }, y: "node_modules/x/node_modules/y" }, @@ -3028,7 +3028,7 @@ describe.concurrent("bundledDependencies", () => { }); // Like npm: a link to a folder that is no installed copy is packed by that folder's own rules. - test.skipIf(isWindows).each([ + test.each([ ["", "project"], [", also below a folder named node_modules", "node_modules/project"], ])("a bundled workspace is packed with its own files and .npmignore%s", async (_, project) => { @@ -3416,7 +3416,7 @@ describe.concurrent("bundledDependencies", () => { }); // The layout of pnpm and of the isolated linker: each package is next to links to its dependencies. - describe.skipIf(isWindows)("two versions of two packages that depend on each other", () => { + describe("two versions of two packages that depend on each other", () => { async function packLinked(graph: Record>) { const tree: Record = { "package.json": JSON.stringify({ @@ -3501,7 +3501,7 @@ describe.concurrent("bundledDependencies", () => { ]); }); - test.skipIf(isWindows)("bundled dependencies that link to each other end the lookup", async () => { + test("bundled dependencies that link to each other end the lookup", async () => { using dir = tempDir("pack-bundled-dep-cycle", { "package.json": JSON.stringify({ name: "pack-bundled-dep-cycle", @@ -3537,7 +3537,7 @@ describe.concurrent("bundledDependencies", () => { }); // This is what `bun install` creates for a workspace that's a dependency. - test.skipIf(isWindows)("a scoped bundled dependency that is a link is followed", async () => { + test("a scoped bundled dependency that is a link is followed", async () => { using dir = tempDir("pack-bundled-dep-scoped-link", { "package.json": JSON.stringify({ name: "pack-bundled-dep-scoped-link", @@ -3561,7 +3561,7 @@ describe.concurrent("bundledDependencies", () => { }); // The lookup used to keep every directory it found open. - test.skipIf(isWindows)("more dependencies than file descriptors", async () => { + test("more dependencies than file descriptors", async () => { const names = Array.from({ length: 299 }, (_, i) => `d${i}`); using dir = tempDir("pack-bundled-dep-emfile", { "package.json": JSON.stringify({ diff --git a/test/cli/install/bun-patch.test.ts b/test/cli/install/bun-patch.test.ts index 3602b698aecb..c839540d90a3 100644 --- a/test/cli/install/bun-patch.test.ts +++ b/test/cli/install/bun-patch.test.ts @@ -1070,10 +1070,9 @@ describe.concurrent.each([ const repo = join(String(dir), "gitrepo"); // The checkout in the cache has the symlinks, and the copies in node_modules don't. That used to end up in the // patch as `deleted file mode 120000`, which no install can apply. - if (!isWindows) { - symlinkSync("index.js", join(repo, "main.js")); - symlinkSync("v2", join(repo, "docs", "latest")); - } + + symlinkSync("index.js", join(repo, "main.js")); + symlinkSync("v2", join(repo, "docs", "latest")); // keep git away from the machine's global/system config (autocrlf, gpgsign) const gitEnv = { ...bunEnv, GIT_CONFIG_NOSYSTEM: "1", GIT_CONFIG_GLOBAL: join(String(dir), "no-gitconfig") }; diff --git a/test/cli/install/bun-pm.test.ts b/test/cli/install/bun-pm.test.ts index 20dd76928c8c..3ae7ed6b8902 100644 --- a/test/cli/install/bun-pm.test.ts +++ b/test/cli/install/bun-pm.test.ts @@ -1900,7 +1900,7 @@ describe.concurrent("bun pm cache rm", () => { }); // Serial: `unprivilegedSpawnOptions` may change and restore the modes of directories above the test's own. - test.serial.skipIf(isWindows)("a cache directory in a read-only parent is emptied", async () => { + test.serial("a cache directory in a read-only parent is emptied", async () => { using dir = tempDir("pm-cache-rm-read-only-parent", { ...sideFiles, ...projectFiles, diff --git a/test/cli/install/bun-prune.test.ts b/test/cli/install/bun-prune.test.ts index e92cc053d564..53c21973d5a2 100644 --- a/test/cli/install/bun-prune.test.ts +++ b/test/cli/install/bun-prune.test.ts @@ -1156,7 +1156,7 @@ test.concurrent( }, ); -test.concurrent.skipIf(isWindows)("a symlinked .bin directory is never cleaned through", async () => { +test.concurrent("a symlinked .bin directory is never cleaned through", async () => { const dir = await setup({ name: "foo", dependencies: { "no-deps": "1.0.0" } }); const nm = join(dir, "node_modules"); expect(existsSync(join(nm, ".bin"))).toBeFalse(); diff --git a/test/cli/install/bun-publish.test.ts b/test/cli/install/bun-publish.test.ts index c39f7786d8ba..79ae7bb8d636 100644 --- a/test/cli/install/bun-publish.test.ts +++ b/test/cli/install/bun-publish.test.ts @@ -1568,7 +1568,7 @@ describe.concurrent("--dry-run", () => { // publish only reads package.json, so a file that is readable but not writable (a // read-only checkout, a file owned by another user) must not stop it. - test.skipIf(isWindows)("read-only package.json", async () => { + test("read-only package.json", async () => { using dir = tempDir("publish-read-only", { "package.json": JSON.stringify({ name: "dry-run-read-only", version: "4.4.4" }), "index.js": "", diff --git a/test/cli/install/bun-run-bunfig.test.ts b/test/cli/install/bun-run-bunfig.test.ts index f8aecb74304c..093114ea31a5 100644 --- a/test/cli/install/bun-run-bunfig.test.ts +++ b/test/cli/install/bun-run-bunfig.test.ts @@ -55,7 +55,6 @@ describe.each(["bun run", "bun"])(`%s`, cmd => { }); describe.each(["bun", "system", "default"])(`run.shell = "%s"`, shellStr => { - if (isWindows && shellStr === "system") return; // windows always uses the bun shell now const shell = shellStr === "default" ? (isWindows ? "bun" : "system") : shellStr; const command_not_found = isWindows && shell === "system" ? "is not recognized as an internal or external command" : "command not found"; diff --git a/test/cli/install/bun-run.test.ts b/test/cli/install/bun-run.test.ts index 1c8918227511..0a90692c8661 100644 --- a/test/cli/install/bun-run.test.ts +++ b/test/cli/install/bun-run.test.ts @@ -378,8 +378,7 @@ describe.concurrent("bun run", () => { const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); expect(stderr).toContain(`Could not change directory to "${cwdArg}"`); - // Windows leaves the verdict on a path this long to SetCurrentDirectoryW. - if (!isWindows) expect(stderr).toContain("ENAMETOOLONG"); + expect(stderr).toContain("ENAMETOOLONG"); expect(stdout).toBe(""); expect(proc.signalCode).toBeNull(); expect(exitCode).toBe(1); @@ -772,7 +771,7 @@ describe.concurrent("bun run", () => { expect(exitCode).toBe(0); }); - it.skipIf(isWindows)("doesn't change an `npm run` that's only quoted text", async () => { + it("doesn't change an `npm run` that's only quoted text", async () => { const { dir, stdout, exitCode } = await runStart("echo 'step: npm run build -w app' && echo 'ok; done'"); using _ = dir; expect(stdout).toContain("-w app"); diff --git a/test/cli/install/bun-workspaces-self-contained.test.ts b/test/cli/install/bun-workspaces-self-contained.test.ts index a8c2d56d16d8..6fbecd607fdc 100644 --- a/test/cli/install/bun-workspaces-self-contained.test.ts +++ b/test/cli/install/bun-workspaces-self-contained.test.ts @@ -129,13 +129,13 @@ describe.each([ expect(existsSync(join(desktopNm, "@barn", "moo", "package.json"))).toBeTrue(); expect(readlinkSync(join(desktopNm, "shared"))).toContain("shared"); // … as real files, not hardlinks into the cache - if (!isWindows) { - expect(lstatSync(join(desktopNm, "bar")).isSymbolicLink()).toBeFalse(); - expect(lstatSync(join(desktopNm, "bar", "package.json")).isSymbolicLink()).toBeFalse(); - expect(statSync(join(desktopNm, "bar", "package.json")).nlink).toBe(1); - // control: the root's copy of the same package *is* hardlinked from the cache - expect(statSync(join(package_dir, "node_modules", "bar", "package.json")).nlink).toBeGreaterThan(1); - } + + expect(lstatSync(join(desktopNm, "bar")).isSymbolicLink()).toBeFalse(); + expect(lstatSync(join(desktopNm, "bar", "package.json")).isSymbolicLink()).toBeFalse(); + expect(statSync(join(desktopNm, "bar", "package.json")).nlink).toBe(1); + // control: the root's copy of the same package *is* hardlinked from the cache + expect(statSync(join(package_dir, "node_modules", "bar", "package.json")).nlink).toBeGreaterThan(1); + // even though `bar` also exists at the root for the root package / other workspaces expect(existsSync(join(package_dir, "node_modules", "bar", "package.json"))).toBeTrue(); // the other workspace hoists as usual @@ -155,9 +155,8 @@ describe.each([ expect(again.err).not.toContain("error:"); expect(again.code).toBe(0); expect(await readdirSorted(desktopNm)).toEqual(["@barn", "bar", "baz", "shared"]); - if (!isWindows) { - expect(statSync(join(desktopNm, "bar", "package.json")).nlink).toBe(1); - } + + expect(statSync(join(desktopNm, "bar", "package.json")).nlink).toBe(1); }); }); diff --git a/test/cli/install/bun-workspaces.test.ts b/test/cli/install/bun-workspaces.test.ts index 4a2a612072b0..00d9ceaecaeb 100644 --- a/test/cli/install/bun-workspaces.test.ts +++ b/test/cli/install/bun-workspaces.test.ts @@ -4299,9 +4299,8 @@ test.concurrent("a copyfile install over a workspace's hardlinked files does not const nestedPkgJson = join(packageDir, "packages", "pkg1", "node_modules", "no-deps", "package.json"); await install("hardlink"); expect(readJson(nestedPkgJson)).toEqual({ name: "no-deps", version: "2.0.0" }); - if (!isWindows) { - expect(statSync(nestedPkgJson).nlink).toBeGreaterThan(1); - } + + expect(statSync(nestedPkgJson).nlink).toBeGreaterThan(1); await rm(join(packageDir, "node_modules"), { recursive: true, force: true }); await install("copyfile"); @@ -4311,10 +4310,9 @@ test.concurrent("a copyfile install over a workspace's hardlinked files does not name: "no-deps", version: "1.0.0", }); - if (!isWindows) { - // a new file, not the cache file's inode - expect(statSync(nestedPkgJson).nlink).toBe(1); - } + + // a new file, not the cache file's inode + expect(statSync(nestedPkgJson).nlink).toBe(1); const cacheDir = join(packageDir, ".bun-cache"); const cached = (await readdirSorted(cacheDir)).filter(name => name.startsWith("no-deps@2.0.0@")); @@ -4324,8 +4322,7 @@ test.concurrent("a copyfile install over a workspace's hardlinked files does not }); for (const linker of ["hoisted", "isolated"]) { - // symlinkSync to a directory needs Developer Mode or elevation on Windows. - test.concurrent.skipIf(isWindows)(`workspace globs match a symlinked member directory (${linker})`, async () => { + test.concurrent(`workspace globs match a symlinked member directory (${linker})`, async () => { using ctx = await setupTest(); const { packageDir, env } = ctx; // Its own version of no-deps is installed in its own node_modules, behind the link. @@ -4378,7 +4375,7 @@ for (const [link, target] of [ ["packages/s", "real/deeper/s"], ["packages", "real/deeper"], ]) { - test.concurrent.skipIf(isWindows)(`isolated: a member finds its dependencies when ${link} is a symlink`, async () => { + test.concurrent(`isolated: a member finds its dependencies when ${link} is a symlink`, async () => { using ctx = await setupTest(); const { packageDir, env } = ctx; await Promise.all([ @@ -4572,7 +4569,7 @@ describe("a workspace outside the project", () => { }); }); -describe.skipIf(isWindows)("in a workspace that's a link to a folder somewhere else", () => { +describe("in a workspace that's a link to a folder somewhere else", () => { /** `cwd` and `pwd` are relative to the directory that holds `repo` and `other`. */ async function install(cwd: string, pwd: string) { using ctx = await setupTest(); @@ -4747,7 +4744,7 @@ describe.skipIf(isWindows)("in a workspace that's a link to a folder somewhere e }); }); -test.concurrent.skipIf(isWindows)("`**` in a workspace glob does not go through a symlinked directory", async () => { +test.concurrent("`**` in a workspace glob does not go through a symlinked directory", async () => { using ctx = await setupTest(); const { packageDir, env } = ctx; await Promise.all([ @@ -5034,7 +5031,7 @@ describe("links of a renamed or removed workspace", () => { // the isolated linker looped forever. On POSIX a symlinked root package.json // made workspace discovery run in the symlink target's directory. describe("aliased project root", () => { - test.concurrent.skipIf(isWindows)("workspaces install when the root package.json is a symlink", async () => { + test.concurrent("workspaces install when the root package.json is a symlink", async () => { using ctx = await setupTest(); const { packageDir, env } = ctx; await Promise.all([ diff --git a/test/cli/install/bunx-directories-bin.test.ts b/test/cli/install/bunx-directories-bin.test.ts index fd2702b6d456..1a991000eeec 100644 --- a/test/cli/install/bunx-directories-bin.test.ts +++ b/test/cli/install/bunx-directories-bin.test.ts @@ -49,7 +49,7 @@ const rejected: [label: string, value: (dir: string) => string, picked: string][ ]; for (const [label, value, picked] of rejected) { - test.concurrent.skipIf(isWindows)(`bunx ignores a directories.bin that is ${label}`, async () => { + test.concurrent(`bunx ignores a directories.bin that is ${label}`, async () => { using dir = tempDir("bunx-directories-bin-rejected", { "package.json": JSON.stringify({ name: "app", dependencies: { tool: "file:./tool" } }), "tool/package.json": "", diff --git a/test/cli/install/bunx.test.ts b/test/cli/install/bunx.test.ts index 5c7841e88ab7..58267d695c53 100644 --- a/test/cli/install/bunx.test.ts +++ b/test/cli/install/bunx.test.ts @@ -3033,8 +3033,7 @@ describe("a package in node_modules", () => { }); }); - // On Windows the entry is no link, so it does not say whose it is. - it.concurrent.skipIf(isWindows).each([[["@corp/tool"]], [["-p", "@corp/tool", "tool"]]])( + it.concurrent.each([[["@corp/tool"]], [["-p", "@corp/tool", "tool"]]])( "bunx %j runs the bin of that package where another one has the entry in .bin", async args => { using box = sandbox(); @@ -3126,7 +3125,7 @@ describe("flags", () => { const expected = { stdout: `local ${flags}\n`, stderr: "", exitCode: 0, requests: [] }; expect(await box.run([...flags.split(" "), "local-tool", ...flags.split(" ")])).toEqual(expected); - if (isWindows) return; + symlinkSync(bunExe(), join(box.dir, isDebug ? "bun-debug" : "bun")); const env = { ...box.env, PATH: box.dir + delimiter + box.env.PATH }; expect(await box.run(["run", "--silent", "tool"], { env, bunx: [bunExe()] })).toEqual(expected); diff --git a/test/cli/install/npmrc.test.ts b/test/cli/install/npmrc.test.ts index 9ec486cdaaa5..c0878e93e061 100644 --- a/test/cli/install/npmrc.test.ts +++ b/test/cli/install/npmrc.test.ts @@ -300,7 +300,7 @@ registry = http://localhost:${registry.port}/ expect(result.exitCode).toBe(0); }); - it.skipIf(isWindows)("bun install prints nothing when $NPM_CONFIG_USERCONFIG is a directory", async () => { + it("bun install prints nothing when $NPM_CONFIG_USERCONFIG is a directory", async () => { using dir = tempDir("npmrc-userconfig-directory", { ...pkg, "home/.keep": "", "elsewhere/ci.npmrc/.keep": "" }); const directory = join(String(dir), "elsewhere", "ci.npmrc"); const result = await bun(String(dir), { NPM_CONFIG_USERCONFIG: directory }, "install"); @@ -5235,7 +5235,7 @@ describe.concurrent("`ca` and `cafile` are one setting: a layer that sets either // Like npm, Bun skips a config file that it can't read, e.g. a root-owned ~/.npmrc in a Docker image that runs as another // user. Not concurrent: `unprivilegedSpawnOptions` may change and restore the modes of directories above the test's own. -describe.skipIf(isWindows)("unreadable config files are skipped", () => { +describe("unreadable config files are skipped", () => { /** Runs bun in `/project` with `/home` as the home directory, as a user that file modes apply to. */ async function run( cmd: string[], diff --git a/test/cli/install/redacted-config-logs.test.ts b/test/cli/install/redacted-config-logs.test.ts index a5f631a9cd34..f3058563f1b7 100644 --- a/test/cli/install/redacted-config-logs.test.ts +++ b/test/cli/install/redacted-config-logs.test.ts @@ -1,6 +1,6 @@ import { iniInternals } from "bun:internal-for-testing"; import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, hostedGitRepository, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, hostedGitRepository, tempDir } from "harness"; import { join } from "path"; test("registry url password is sent as Basic auth and left out of request error output", async () => { @@ -1221,7 +1221,7 @@ describe.concurrent("the userinfo of a git URL is masked as a whole", () => { }); // git names the URL it wants a password for, and bun passes on what git says. - test.skipIf(isWindows)("git's own output", async () => { + test("git's own output", async () => { await using server = Bun.serve({ port: 0, hostname: "127.0.0.1", @@ -1242,7 +1242,7 @@ describe.concurrent("the userinfo of a git URL is masked as a whole", () => { }); // When no form of the URL can be cloned, each is named above what git said of it. - test.skipIf(isWindows)("every form of a URL that cannot be cloned", async () => { + test("every form of a URL that cannot be cloned", async () => { const url = (scheme: string, userinfo: string) => `${scheme}://${userinfo}@example.com/org/lib.git`; await using server = Bun.serve({ port: 0, diff --git a/test/cli/install/symlink-path-traversal.test.ts b/test/cli/install/symlink-path-traversal.test.ts index 744a3b289d6e..2971b69c29a8 100644 --- a/test/cli/install/symlink-path-traversal.test.ts +++ b/test/cli/install/symlink-path-traversal.test.ts @@ -770,108 +770,104 @@ describe.concurrent.skipIf(isWindows)("symlink path traversal protection", () => } }); -it.skipIf(isWindows)( - "rejects symlink targets that climb through other symlinks from the same archive (.bun-tag write stays inside the package)", - async () => { - // A tarball can ship symlinks whose targets each normalize to a path - // inside the package (`l1 -> .`, `l2 -> l1/..`, `l3 -> l2/..`, ...). - // Lexically every hop is "safe", but the kernel resolves each link before - // applying `..`, so each hop climbs one directory until it clamps at `/`. - // A final `.bun-tag -> lN/` entry then - // makes the post-extraction `.bun-tag` marker write (O_CREAT|O_TRUNC) land - // on an arbitrary file. The extractor must reject symlink targets with a - // `..` component that follows a named component, and must not follow a - // pre-existing `.bun-tag` when writing the marker. - const victimPath = join(systemTmpDir, `bun-tag-victim-${Math.random().toString(36).slice(2, 10)}.txt`); - await writeFile(victimPath, "original-content"); - - const chainLength = 30; - const entries: Parameters[0] = [ - { name: "test-package/", type: "dir" }, - { - name: "test-package/package.json", - type: "file", - content: JSON.stringify({ name: "test-package", version: "1.0.0" }), - }, - { name: "test-package/l1", type: "symlink", linkname: "." }, - ]; - for (let i = 2; i <= chainLength; i++) { - // Normalizes to "" (inside the package), but resolves one directory - // above wherever l(i-1) resolves to. - entries.push({ name: `test-package/l${i}`, type: "symlink", linkname: `l${i - 1}/..` }); - } - // After `chainLength` hops the chain is clamped at `/`, so this resolves to - // the absolute victim path while still normalizing to a path inside the - // package directory. - entries.push({ - name: "test-package/.bun-tag", - type: "symlink", - linkname: `l${chainLength}/${victimPath.replace(/^\/+/, "")}`, - }); - const tarball = createTarball(entries); - - const server = Bun.serve({ - port: 0, - fetch(req) { - const url = new URL(req.url); - if (url.pathname.includes("/tarball/") || url.pathname.endsWith(".tar.gz")) { - return new Response(tarball, { headers: { "Content-Type": "application/gzip" } }); - } - if (url.pathname.includes("/repos/")) { - return Response.json({ default_branch: "main" }); - } - return new Response("Not Found", { status: 404 }); - }, - }); +it("rejects symlink targets that climb through other symlinks from the same archive (.bun-tag write stays inside the package)", async () => { + // A tarball can ship symlinks whose targets each normalize to a path + // inside the package (`l1 -> .`, `l2 -> l1/..`, `l3 -> l2/..`, ...). + // Lexically every hop is "safe", but the kernel resolves each link before + // applying `..`, so each hop climbs one directory until it clamps at `/`. + // A final `.bun-tag -> lN/` entry then + // makes the post-extraction `.bun-tag` marker write (O_CREAT|O_TRUNC) land + // on an arbitrary file. The extractor must reject symlink targets with a + // `..` component that follows a named component, and must not follow a + // pre-existing `.bun-tag` when writing the marker. + const victimPath = join(systemTmpDir, `bun-tag-victim-${Math.random().toString(36).slice(2, 10)}.txt`); + await writeFile(victimPath, "original-content"); + + const chainLength = 30; + const entries: Parameters[0] = [ + { name: "test-package/", type: "dir" }, + { + name: "test-package/package.json", + type: "file", + content: JSON.stringify({ name: "test-package", version: "1.0.0" }), + }, + { name: "test-package/l1", type: "symlink", linkname: "." }, + ]; + for (let i = 2; i <= chainLength; i++) { + // Normalizes to "" (inside the package), but resolves one directory + // above wherever l(i-1) resolves to. + entries.push({ name: `test-package/l${i}`, type: "symlink", linkname: `l${i - 1}/..` }); + } + // After `chainLength` hops the chain is clamped at `/`, so this resolves to + // the absolute victim path while still normalizing to a path inside the + // package directory. + entries.push({ + name: "test-package/.bun-tag", + type: "symlink", + linkname: `l${chainLength}/${victimPath.replace(/^\/+/, "")}`, + }); + const tarball = createTarball(entries); + + const server = Bun.serve({ + port: 0, + fetch(req) { + const url = new URL(req.url); + if (url.pathname.includes("/tarball/") || url.pathname.endsWith(".tar.gz")) { + return new Response(tarball, { headers: { "Content-Type": "application/gzip" } }); + } + if (url.pathname.includes("/repos/")) { + return Response.json({ default_branch: "main" }); + } + return new Response("Not Found", { status: 404 }); + }, + }); - try { - using dir = tempDir("bun-tag-symlink-chain-test", {}); - const installDir = String(dir); + try { + using dir = tempDir("bun-tag-symlink-chain-test", {}); + const installDir = String(dir); - await writeFile( - join(installDir, "package.json"), - JSON.stringify({ - name: "test-app", - version: "1.0.0", - dependencies: { "test-package": "github:user/repo#main" }, - }), - ); + await writeFile( + join(installDir, "package.json"), + JSON.stringify({ + name: "test-app", + version: "1.0.0", + dependencies: { "test-package": "github:user/repo#main" }, + }), + ); - const proc = spawn({ - cmd: [bunExe(), "install"], - cwd: installDir, - stdout: "pipe", - stderr: "pipe", - env: { - ...env, - GITHUB_API_URL: `http://localhost:${server.port}`, - BUN_INSTALL_CACHE_DIR: join(installDir, ".bun-cache"), - }, - }); + const proc = spawn({ + cmd: [bunExe(), "install"], + cwd: installDir, + stdout: "pipe", + stderr: "pipe", + env: { + ...env, + GITHUB_API_URL: `http://localhost:${server.port}`, + BUN_INSTALL_CACHE_DIR: join(installDir, ".bun-cache"), + }, + }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - // The file outside the extraction directory must be untouched: same - // content, not truncated, not replaced with the github tag string. - expect(await Bun.file(victimPath).text()).toBe("original-content"); + // The file outside the extraction directory must be untouched: same + // content, not truncated, not replaced with the github tag string. + expect(await Bun.file(victimPath).text()).toBe("original-content"); - // The legitimate package contents are still installed. - const pkgDir = join(installDir, "node_modules", "test-package"); - await access(join(pkgDir, "package.json")); + // The legitimate package contents are still installed. + const pkgDir = join(installDir, "node_modules", "test-package"); + await access(join(pkgDir, "package.json")); - if (exitCode !== 0) { - console.error("Install failed with exit code:", exitCode); - console.error("stdout:", stdout); - console.error("stderr:", stderr); - } - expect(exitCode).toBe(0); - } finally { - server.stop(); - await rm(victimPath, { force: true }); + if (exitCode !== 0) { + console.error("Install failed with exit code:", exitCode); + console.error("stdout:", stdout); + console.error("stderr:", stderr); } - }, - 60000, -); + expect(exitCode).toBe(0); + } finally { + server.stop(); + await rm(victimPath, { force: true }); + } +}, 60000); it.skipIf(isWindows)( "does not change permissions of a file reached through a symlinked bin target", @@ -1076,7 +1072,7 @@ it.skipIf(isWindows)( // The cache folder and the bun.lock entry of a `github:` package are named after the root // directory of its tarball: the first component of the first entry's path, whatever that // entry is. A tarball with a Content-Length and a chunked one go through different readers. -describe.concurrent.skipIf(isWindows)("the root directory of a GitHub tarball", () => { +describe.concurrent("the root directory of a GitHub tarball", () => { const readers = ["buffered", "streaming"] as const; async function install(reader: (typeof readers)[number], root: string, first: string[] = []) { @@ -1305,7 +1301,7 @@ describe.concurrent("links below node_modules with the hoisted linker", () => { }); // People do link the project's own `node_modules` (a Docker volume, Nix, Bazel). - it.skipIf(isWindows)("still installs through a link at the project's own node_modules", async () => { + it("still installs through a link at the project's own node_modules", async () => { using root = tempDir("nm-root", {}); const volume = join(String(root), "volume"); const repo = join(String(root), "repo"); @@ -1367,7 +1363,7 @@ describe.concurrent("links below node_modules with the hoisted linker", () => { }); // `lodash` is found installed through the link before `zod` is written. - it.skipIf(isWindows)("keeps what's behind a workspace's link when one package changes", async () => { + it("keeps what's behind a workspace's link when one package changes", async () => { using root = tempDir("nm-member-change", {}); const repo = await workspaceWithNestedDep(String(root)); for (const major of [1, 2, 3]) { @@ -1399,7 +1395,7 @@ describe.concurrent("links below node_modules with the hoisted linker", () => { expect(exitCode).toBe(0); }); - it.skipIf(isWindows)("keeps a workspace member's link when it replaces the isolated linker's tree", async () => { + it("keeps a workspace member's link when it replaces the isolated linker's tree", async () => { using root = tempDir("nm-member-switch", {}); const repo = await workspaceWithNestedDep(String(root)); expect((await runBun(repo, ["install", "--ignore-scripts", "--linker", "isolated"])).exitCode).toBe(0); @@ -1512,7 +1508,7 @@ describe.concurrent("links below node_modules with the hoisted linker", () => { // The links are planted after the install, so only `bun remove` itself is between its deletes and the victim. It // deletes the leftover `@scope/` and the dangling links in `.bin`. - it.skipIf(isWindows)("bun remove does not delete through node_modules/@scope or .bin", async () => { + it("bun remove does not delete through node_modules/@scope or .bin", async () => { using dir = tempDir("nm-remove-planted", { "package.json": JSON.stringify({ name: "remove-scope-symlink-app", @@ -1792,7 +1788,7 @@ describe.concurrent("links below node_modules with the isolated linker", () => { } // People do link the project's own `node_modules` (a Docker volume, Nix, Bazel). - it.skipIf(isWindows)("still installs through a link at the project's own node_modules", async () => { + it("still installs through a link at the project's own node_modules", async () => { using root = tempDir("nm-root", {}); const volume = join(String(root), "volume"); await mkdir(volume); diff --git a/test/cli/lint/config-files.test.ts b/test/cli/lint/config-files.test.ts index fd4e36209e43..027407209285 100644 --- a/test/cli/lint/config-files.test.ts +++ b/test/cli/lint/config-files.test.ts @@ -6597,7 +6597,7 @@ describe.skipIf(!hasRunner)("Windows paths, on every platform", () => { // This is what ESLint 10.12.0, ESLint 8.57.1 and oxlint 1.87.0 do with the same files. Without a config file, bun lint // behaves like oxlint. -describe.skipIf(isWindows).concurrent("a symlink that's dangling or points to itself or a parent directory", () => { +describe.concurrent("a symlink that's dangling or points to itself or a parent directory", () => { const flavors: Record> = { "eslint.config.js": { "eslint.config.js": `export default [${JSON.stringify(noVar)}];` }, ".eslintrc.json": { ".eslintrc.json": rc(noVar) }, diff --git a/test/cli/lint/git.test.ts b/test/cli/lint/git.test.ts index eb0e7fe36196..e3fdbebd08ea 100644 --- a/test/cli/lint/git.test.ts +++ b/test/cli/lint/git.test.ts @@ -290,7 +290,7 @@ describe.concurrent.each(commands)("bun %s", command => { await commit(join(root, "inner")); write(root, "inner/a.js", before); await git(join(root, "inner"), "add", "-A"); - if (!isWindows) symlinkSync("a.js", join(root, "link.js")); + symlinkSync("a.js", join(root, "link.js")); write(root, "b.js", before); await git(root, "add", "-A"); @@ -444,7 +444,7 @@ describe.concurrent.each(commands)("bun %s", command => { expect(await git(root, "show", ":sub/bad.js")).toBe("\n" + before); }); - test.skipIf(isWindows)("works in a pre-commit hook, on the index of that commit", async () => { + test("works in a pre-commit hook, on the index of that commit", async () => { using repo = await repository({ "a.js": tidy, "b.js": tidy }); const { root } = repo; const hook = join(root, ".git/hooks/pre-commit"); @@ -463,7 +463,7 @@ describe.concurrent.each(commands)("bun %s", command => { expect(await git(root, "status", "--porcelain")).toBe(""); }); - test.skipIf(isWindows)("git never runs programs named in the repository's config", async () => { + test("git never runs programs named in the repository's config", async () => { using repo = await repository({ "a.js": tidy, "b.js": tidy, ".gitattributes": "*.js diff=mine\n" }); const { root } = repo; const program = join(root, "program.sh"); @@ -643,33 +643,25 @@ describe.concurrent("bun lint --staged reads the staged version of a partly stag expect(exitCode).toBe(1); }); - test.skipIf(isWindows)( - "for a file passed through a symlinked path, and a package.json with a byte order mark", - async () => { - const module = { - "package.json": '\uFEFF{ "type": "module" }', - "tsconfig.json": '{ "compilerOptions": { "module": "nodenext", "lib": ["es2015"], "types": [] } }', - }; - using repo = await repository({ ...module, "a.ts": tidy }); - const { root } = repo; - write(root, "a.ts", "export async function f(a: number) {\n await a;\n return import.meta;\n}\n"); - write(root, "package.json", module["package.json"] + "\n"); - await git(root, "add", "-A"); - write(root, "a.ts", tidy); - write(root, "package.json", module["package.json"]); - using links = tempDir("bun-lint-git-link", {}); - symlinkSync(root, join(String(links), "link")); - const { stdout, exitCode } = await bun(root, [ - "lint", - "--staged", - ...flags, - join(String(links), "link", "a.ts"), - ]); - expect(stdout).toContain("a.ts:2:3: "); - expect(stdout).toMatch(/^1 problem$/m); - expect(exitCode).toBe(1); - }, - ); + test("for a file passed through a symlinked path, and a package.json with a byte order mark", async () => { + const module = { + "package.json": '\uFEFF{ "type": "module" }', + "tsconfig.json": '{ "compilerOptions": { "module": "nodenext", "lib": ["es2015"], "types": [] } }', + }; + using repo = await repository({ ...module, "a.ts": tidy }); + const { root } = repo; + write(root, "a.ts", "export async function f(a: number) {\n await a;\n return import.meta;\n}\n"); + write(root, "package.json", module["package.json"] + "\n"); + await git(root, "add", "-A"); + write(root, "a.ts", tidy); + write(root, "package.json", module["package.json"]); + using links = tempDir("bun-lint-git-link", {}); + symlinkSync(root, join(String(links), "link")); + const { stdout, exitCode } = await bun(root, ["lint", "--staged", ...flags, join(String(links), "link", "a.ts")]); + expect(stdout).toContain("a.ts:2:3: "); + expect(stdout).toMatch(/^1 problem$/m); + expect(exitCode).toBe(1); + }); }); test("for a file that no tsconfig.json includes", async () => { @@ -797,7 +789,7 @@ describe.concurrent("bun lint --staged lints what will be committed", () => { expect(await git(repo.root, "show", ":gone.js")).toBe(before); }); - test.skipIf(isWindows)("a staged file that was replaced by a symlink on disk is linted", async () => { + test("a staged file that was replaced by a symlink on disk is linted", async () => { using repo = await repository({ "target.js": clean }); write(repo.root, "f.js", broken); await git(repo.root, "add", "-A"); @@ -946,20 +938,17 @@ test.concurrent("bun lint --staged reads a conflicted file from disk", async () expect(exitCode).toBe(0); }); -test.skipIf(isWindows).concurrent.each(commands)( - "bun %s --staged skips a file that is staged as a symlink", - async command => { - using repo = await repository({ "target.js": tidy, "a.js": tidy, "b.js": tidy }); - const { root } = repo; - rmSync(join(root, "a.js")); - symlinkSync("target.js", join(root, "a.js")); - write(root, "b.js", before); - await git(root, "add", "-A"); - rmSync(join(root, "a.js")); - write(root, "a.js", tidy); - expect(await selected(root, root, [command, "--staged"])).toEqual(["b.js"]); - }, -); +test.concurrent.each(commands)("bun %s --staged skips a file that is staged as a symlink", async command => { + using repo = await repository({ "target.js": tidy, "a.js": tidy, "b.js": tidy }); + const { root } = repo; + rmSync(join(root, "a.js")); + symlinkSync("target.js", join(root, "a.js")); + write(root, "b.js", before); + await git(root, "add", "-A"); + rmSync(join(root, "a.js")); + write(root, "a.js", tidy); + expect(await selected(root, root, [command, "--staged"])).toEqual(["b.js"]); +}); test.concurrent("bun fmt --staged doesn't count a file it skips", async () => { using repo = await repository({ "partly.js": tidy, "whole.js": tidy, "tidy.js": tidy }); diff --git a/test/cli/lint/help.test.ts b/test/cli/lint/help.test.ts index f8958ff7a439..7b98ec5c22eb 100644 --- a/test/cli/lint/help.test.ts +++ b/test/cli/lint/help.test.ts @@ -491,7 +491,7 @@ note: package.json has a "${command}" script, so bun ${command} runs that script }, ); - test.skipIf(isWindows)("`..` after a symlink resolves to the directory that contains the symlink", async () => { + test("`..` after a symlink resolves to the directory that contains the symlink", async () => { const files = { "project/package.json": script["package.json"], "else/where/deep/b.ts": source }; const before = (dir: string) => symlinkSync("../else/where/deep", join(dir, "project/link")); expect(await whatRan(files, ["--cwd=link/..", command], "project", { before })).toBe("the script"); @@ -685,7 +685,7 @@ STARTED=1 exec "${bunExe()}" ${again.join(" ")} }); }); - test.skipIf(isWindows)("a backslash in a directory name isn't a separator", async () => { + test("a backslash in a directory name isn't a separator", async () => { const { stdout, exitCode } = await bun( { "a\\b/package.json": script["package.json"] }, [command, "--help"], @@ -697,7 +697,7 @@ STARTED=1 exec "${bunExe()}" ${again.join(" ")} // Skipped for `bun check`. Like TypeScript, it treats a backslash as a separator everywhere, so it can't find files // in a directory like this. - describe.skipIf(isWindows || command === "check")("a backslash in a directory name with --cwd", () => { + describe.skipIf(command === "check")("a backslash in a directory name with --cwd", () => { // The directory that `a\b` would resolve to if the backslash were a separator. const other = { "a/b/other.ts": source, "a/b/sub/other.ts": source }; diff --git a/test/cli/lint/init.test.ts b/test/cli/lint/init.test.ts index 75fb5519a009..abec9786575f 100644 --- a/test/cli/lint/init.test.ts +++ b/test/cli/lint/init.test.ts @@ -203,7 +203,7 @@ describe.concurrent.each(["lint", "fmt"] as const)("bun %s --init", command => { } }); - describe.skipIf(isWindows)("a bunfig.toml that is a symlink", () => { + describe("a bunfig.toml that is a symlink", () => { const project = { "project/.git/HEAD": "ref: refs/heads/main\n", "project/a.js": "a;\n" }; test("isn't written if it points outside the repository", async () => { diff --git a/test/cli/lint/lint.test.ts b/test/cli/lint/lint.test.ts index f3c250536739..c5edb068b019 100644 --- a/test/cli/lint/lint.test.ts +++ b/test/cli/lint/lint.test.ts @@ -1419,7 +1419,7 @@ describe.concurrent("bun lint", () => { }); }); - test.skipIf(isWindows)("a named pipe whose name ends in .js is skipped", async () => { + test("a named pipe whose name ends in .js is skipped", async () => { const { stdout, exitCode } = await lint({ "eslint.config.js": basic, "a.js": bad }, ["-f", "unix", "."], { before: dir => expect(Bun.spawnSync(["mkfifo", join(dir, "pipe.js")]).exitCode).toBe(0), }); @@ -1429,7 +1429,7 @@ describe.concurrent("bun lint", () => { }); // Opening a pipe like this blocks until a writer opens it. - test.skipIf(isWindows).each([".gitignore", ".git/info/exclude", ".eslintignore", "package.json"])( + test.each([".gitignore", ".git/info/exclude", ".eslintignore", "package.json"])( "doesn't hang when %s is a link to a named pipe without a writer", async name => { const { raw } = await lint({ "project/a.js": bad, "project/.git/info/a": "" }, ["-f", "json", "."], { @@ -7256,18 +7256,15 @@ export default { meta: { name: "q" }, rules: { marks, marked } };\n`, }); // ESLint doesn't print a report in that case. - test.skipIf(process.getuid?.() === 0 || isWindows)( - "still prints the report if a file can't be written", - async () => { - const files = { "eslint.config.js": basic, "a.js": "var a = 1;\nexport { a };\n", "b.js": "debugger;\n" }; - const before = (dir: string) => chmodSync(join(dir, "a.js"), 0o444); - const result = await lint(files, ["--fix", "-f", "unix", "a.js", "b.js"], { reads: ["a.js"], before }); - expect(result.files["a.js"]).toBe(files["a.js"]); - expect(result.stdout).toContain("[Error/no-debugger]"); - expect(result.stderr).toMatch(/Cannot write .*a\.js: (EACCES|EPERM)/); - expect(result.exitCode).toBe(2); - }, - ); + test.skipIf(process.getuid?.() === 0)("still prints the report if a file can't be written", async () => { + const files = { "eslint.config.js": basic, "a.js": "var a = 1;\nexport { a };\n", "b.js": "debugger;\n" }; + const before = (dir: string) => chmodSync(join(dir, "a.js"), 0o444); + const result = await lint(files, ["--fix", "-f", "unix", "a.js", "b.js"], { reads: ["a.js"], before }); + expect(result.files["a.js"]).toBe(files["a.js"]); + expect(result.stdout).toContain("[Error/no-debugger]"); + expect(result.stderr).toMatch(/Cannot write .*a\.js: (EACCES|EPERM)/); + expect(result.exitCode).toBe(2); + }); const files = { "eslint.config.js": basic, "a.js": bad, "b.js": "export const b = 1;\n" }; @@ -7989,7 +7986,7 @@ export default { meta: { name: "q" }, rules: { marks, marked } };\n`, expect(result.files).toEqual({ "a.js": `\uFEFF${bad}` }); }); - test.skipIf(isWindows)("writes through a symlink instead of replacing it", async () => { + test("writes through a symlink instead of replacing it", async () => { const result = await lint(files, ["--fix", "link.js"], { reads: ["a.js", "link.js"], before: dir => symlinkSync("a.js", join(dir, "link.js")), @@ -8098,7 +8095,7 @@ export default { meta: { name: "q" }, rules: { marks, marked } };\n`, }); // ESLint doesn't need write access to the directory. - test.skipIf(isWindows || isRoot)("fixes a file in a read-only directory", async () => { + test.skipIf(isRoot)("fixes a file in a read-only directory", async () => { using dir = tempDir("bun-lint", { "eslint.config.js": files["eslint.config.js"], "src/a.js": files["a.js"] }); chmodSync(join(String(dir), "src"), 0o555); try { @@ -8582,7 +8579,7 @@ export default { meta: { name: "q" }, rules: { marks, marked } };\n`, const prunes = flag === "--prune-suppressions"; const missingFileError = prunes ? "The suppressions file does not exist" : symlinkError; - test.skipIf(isWindows)("refuses to write through a symlink that points outside the repository", async () => { + test("refuses to write through a symlink that points outside the repository", async () => { const options = { cwd: "project", before: link("project/f", "outside/f"), reads: ["outside/f"], mayFail: true }; const [toFile, toNothing] = await Promise.all([ lint({ ...tree, "outside/f": before }, flags("f"), options), @@ -8632,7 +8629,7 @@ export default { meta: { name: "q" }, rules: { marks, marked } };\n`, expect(result.exitCode).toBe(exitCode); }); - test.skipIf(isWindows)("writes through a symlink that points inside the repository and keeps it", async () => { + test("writes through a symlink that points inside the repository and keeps it", async () => { using dir = tempDir("bun-lint", { ...tree, [prunes ? "project/real/f" : "project/real/other"]: before }); link("project/f", "project/real/f")(String(dir)); const result = await run(join(String(dir), "project"), flags("f")); @@ -8642,7 +8639,7 @@ export default { meta: { name: "q" }, rules: { marks, marked } };\n`, }); }); - test.skipIf(isWindows)("refuses the default suppressions file if it links outside the repository", async () => { + test("refuses the default suppressions file if it links outside the repository", async () => { const name = "eslint-suppressions.json"; const options = { cwd: "project", diff --git a/test/cli/run/as-node.test.ts b/test/cli/run/as-node.test.ts index 4d291b398033..245aef980d51 100644 --- a/test/cli/run/as-node.test.ts +++ b/test/cli/run/as-node.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test"; import { join } from "path"; -import { bunEnv, bunExe, fakeNodeRun, isMacOS, isWindows, tempDir } from "../../harness"; +import { bunEnv, bunExe, fakeNodeRun, isMacOS, tempDir } from "../../harness"; describe("fake node cli", () => { test("the node cli actually works", () => { @@ -31,8 +31,7 @@ describe("fake node cli", () => { }); expect(fakeNodeRun(temp, "run").stdout).toBe("pass"); }); - // A Windows command line is too short for a path longer than the path buffer. - test.skipIf(isWindows)("reports module not found for a relative script path longer than PATH_MAX", () => { + test("reports module not found for a relative script path longer than PATH_MAX", () => { const long = Array(21).fill(Buffer.alloc(200, "a").toString()).join("/") + ".js"; using temp = tempDir("fake-node-long", {}); const { exitCode, stdout, stderr } = Bun.spawnSync({ diff --git a/test/cli/run/env.test.ts b/test/cli/run/env.test.ts index 4b0f401c9d90..9a393b148dc5 100644 --- a/test/cli/run/env.test.ts +++ b/test/cli/run/env.test.ts @@ -796,7 +796,7 @@ describe.concurrent(".env that is not a regular file", () => { expect(script.exitCode).toBe(0); }); - test.skipIf(isWindows)("unix socket behind a symlink", async () => { + test("unix socket behind a symlink", async () => { using dir = tempDir("dotenv-sock", files); using listener = Bun.listen({ unix: path.join(String(dir), "sock"), diff --git a/test/cli/run/filter-workspace.test.ts b/test/cli/run/filter-workspace.test.ts index 031bd4ee5686..3d177c7f66fc 100644 --- a/test/cli/run/filter-workspace.test.ts +++ b/test/cli/run/filter-workspace.test.ts @@ -1572,7 +1572,7 @@ describe("auto-discovered bunfig.toml [run] section", () => { expect(r.exitCode).toBe(0); }); - test.skipIf(isWindows)("--elide-lines on the CLI wins over [run] elide-lines", () => { + test("--elide-lines on the CLI wins over [run] elide-lines", () => { using dir = workspace("filter-bunfig-cli-elide", "[run]\nelide-lines = 15\n"); const r = run(String(dir), ["run", "--elide-lines", "0", "--filter", "dep0", "lines"], { FORCE_COLOR: "1", diff --git a/test/cli/run/no-orphans.test.ts b/test/cli/run/no-orphans.test.ts index 8f0606f8b32c..0e322243184d 100644 --- a/test/cli/run/no-orphans.test.ts +++ b/test/cli/run/no-orphans.test.ts @@ -266,7 +266,7 @@ describe.concurrent.each([ { via: "--no-orphans", argv: ["--no-orphans"], bunfig: false, env: {} }, { via: "bunfig [run] noOrphans = true", argv: [], bunfig: true, env: {} }, ])("clean exit reaps descendants", ({ via, argv, bunfig, env: extraEnv }) => { - test.concurrent.skipIf(!isPosix)(via, async () => { + test.concurrent(via, async () => { using dir = tempDir("no-orphans-clean-exit", { ...(bunfig && { "bunfig.toml": "[run]\nnoOrphans = true\n" }), "grandchild.js": `process.stdout.write("r"); setInterval(()=>{}, 1000);`, diff --git a/test/cli/run/run-autoinstall.test.ts b/test/cli/run/run-autoinstall.test.ts index 95ee83d0ac08..786477e3b4b8 100644 --- a/test/cli/run/run-autoinstall.test.ts +++ b/test/cli/run/run-autoinstall.test.ts @@ -2555,7 +2555,7 @@ describe.concurrent("auto-install is off when a security scanner is configured", expect(await run(files, { cwd: "elsewhere", args: ["../app/src/deep/index.js"] })).toEqual(refused(configured)); }); - test.serial.skipIf(isWindows).each([ + test.serial.each([ [".npmrc", "a folder of the project", { cwd: "app/src" }], [".npmrc", "outside the project", { cwd: "elsewhere", args: ["../app/src/index.js"] }], ["bunfig.toml", "outside the project", { cwd: "elsewhere", args: ["../app/src/index.js"] }], @@ -2578,7 +2578,7 @@ describe.concurrent("auto-install is off when a security scanner is configured", expect(await run(files)).toEqual(refused(unreadable)); }); - test.serial.skipIf(isWindows).each([ + test.serial.each([ ["the user's bunfig", "home/.bunfig.toml"], ["the user's .npmrc", "home/.npmrc"], ["the project's .npmrc", "app/.npmrc"], diff --git a/test/cli/run/run-crash-handler.test.ts b/test/cli/run/run-crash-handler.test.ts index 74764bc4bb5c..9828e826b46e 100644 --- a/test/cli/run/run-crash-handler.test.ts +++ b/test/cli/run/run-crash-handler.test.ts @@ -61,44 +61,40 @@ test.if(isDebug && isLinux && hasSymbolizer)( // JS listener's backing sigaction enqueues to the JS thread and returns; // without the reset the process would survive the raise and fall through to // the trap fallback, which on aarch64 (brk → SIGTRAP) used to spin forever. -test.if(isPosix)( - "panic terminates the process even when JS registered trap-signal listeners", - async () => { - await using proc = Bun.spawn({ - cmd: [ - bunExe(), - "-e", - `process.on("SIGTRAP", () => {}); +test("panic terminates the process even when JS registered trap-signal listeners", async () => { + await using proc = Bun.spawn({ + cmd: [ + bunExe(), + "-e", + `process.on("SIGTRAP", () => {}); process.on("SIGILL", () => {}); process.on("SIGABRT", () => {}); require("bun:internal-for-testing").crash_handler.panic();`, - // Make debug builds take the fast trace-string path instead of - // spawning llvm-symbolizer, which can take tens of seconds. - "--debug-crash-handler-use-trace-string", - ], - env: noReportEnv, - stdio: ["ignore", "pipe", "pipe"], - }); + // Make debug builds take the fast trace-string path instead of + // spawning llvm-symbolizer, which can take tens of seconds. + "--debug-crash-handler-use-trace-string", + ], + env: noReportEnv, + stdio: ["ignore", "pipe", "pipe"], + }); - // Without the fix the child never exits — it loops SIGTRAP delivery on the - // trap instruction. Bound the wait and fail explicitly rather than hanging - // the test runner and leaking a core-pinning process. - const exited = await Promise.race([proc.exited, Bun.sleep(8_000).then(() => "spinning" as const)]); - if (exited === "spinning") { - proc.kill("SIGKILL"); - } + // Without the fix the child never exits — it loops SIGTRAP delivery on the + // trap instruction. Bound the wait and fail explicitly rather than hanging + // the test runner and leaking a core-pinning process. + const exited = await Promise.race([proc.exited, Bun.sleep(8_000).then(() => "spinning" as const)]); + if (exited === "spinning") { + proc.kill("SIGKILL"); + } - const stderr = await proc.stderr.text(); - expect(exited, `process should have died from the trap, stderr:\n${stderr}`).not.toBe("spinning"); + const stderr = await proc.stderr.text(); + expect(exited, `process should have died from the trap, stderr:\n${stderr}`).not.toBe("spinning"); - // It went through the crash handler... - expect(stderr).toContain("invoked crashByPanic() handler"); - // ...and died from the trap's default action, not a clean exit, and not a - // JS-observed SIGTRAP (the JS listener must never swallow the crash). - expect(proc.signalCode === null ? proc.exitCode : proc.signalCode).not.toBe(0); - }, - 20_000, -); + // It went through the crash handler... + expect(stderr).toContain("invoked crashByPanic() handler"); + // ...and died from the trap's default action, not a clean exit, and not a + // JS-observed SIGTRAP (the JS listener must never swallow the crash). + expect(proc.signalCode === null ? proc.exitCode : proc.signalCode).not.toBe(0); +}, 20_000); // After printing the crash report the handler must terminate with a signal // that reflects the crash cause: panics abort (SIGABRT), a caught fault is diff --git a/test/cli/run/run-shell.test.ts b/test/cli/run/run-shell.test.ts index 4d2778abb7cd..4e5da1f2f955 100644 --- a/test/cli/run/run-shell.test.ts +++ b/test/cli/run/run-shell.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test"; import { chmodSync, mkdirSync } from "fs"; -import { bunEnv, bunExe, isWindows, tempDir, tmpdirSync } from "harness"; +import { bunEnv, bunExe, tempDir, tmpdirSync } from "harness"; import { join } from "path"; describe.concurrent("run-shell", () => { @@ -39,44 +39,41 @@ describe.concurrent("run-shell", () => { }); }); -test.skipIf(isWindows)( - "package script shell interpreter is resolved from the original PATH, not node_modules/.bin", - async () => { - // A dependency can place arbitrary executables named "bash"/"sh"/"zsh" into - // node_modules/.bin via its "bin" field. The interpreter that runs - // package.json scripts must never be picked up from there. - const fakeShell = "#!/bin/sh\necho FAKE_SHELL_USED\n"; - using dir = tempDir("run-shell-interpreter", { - "package.json": JSON.stringify({ - name: "shell-interpreter-fixture", - version: "1.0.0", - scripts: { - "say-hi": "echo real-shell-ran", - }, - }), - "node_modules/.bin/bash": fakeShell, - "node_modules/.bin/sh": fakeShell, - "node_modules/.bin/zsh": fakeShell, - }); - for (const name of ["bash", "sh", "zsh"]) { - chmodSync(join(String(dir), "node_modules", ".bin", name), 0o755); - } +test("package script shell interpreter is resolved from the original PATH, not node_modules/.bin", async () => { + // A dependency can place arbitrary executables named "bash"/"sh"/"zsh" into + // node_modules/.bin via its "bin" field. The interpreter that runs + // package.json scripts must never be picked up from there. + const fakeShell = "#!/bin/sh\necho FAKE_SHELL_USED\n"; + using dir = tempDir("run-shell-interpreter", { + "package.json": JSON.stringify({ + name: "shell-interpreter-fixture", + version: "1.0.0", + scripts: { + "say-hi": "echo real-shell-ran", + }, + }), + "node_modules/.bin/bash": fakeShell, + "node_modules/.bin/sh": fakeShell, + "node_modules/.bin/zsh": fakeShell, + }); + for (const name of ["bash", "sh", "zsh"]) { + chmodSync(join(String(dir), "node_modules", ".bin", name), 0o755); + } - await using proc = Bun.spawn({ - cmd: [bunExe(), "run", "say-hi"], - cwd: String(dir), - env: bunEnv, - stdout: "pipe", - stderr: "pipe", - }); + await using proc = Bun.spawn({ + cmd: [bunExe(), "run", "say-hi"], + cwd: String(dir), + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - // The script must run under a real system shell, not the executables a - // dependency dropped into node_modules/.bin. - expect(stdout).not.toContain("FAKE_SHELL_USED"); - expect(stderr).not.toContain("FAKE_SHELL_USED"); - expect(stdout).toContain("real-shell-ran"); - expect(exitCode).toBe(0); - }, -); + // The script must run under a real system shell, not the executables a + // dependency dropped into node_modules/.bin. + expect(stdout).not.toContain("FAKE_SHELL_USED"); + expect(stderr).not.toContain("FAKE_SHELL_USED"); + expect(stdout).toContain("real-shell-ran"); + expect(exitCode).toBe(0); +}); diff --git a/test/cli/test/bun-test.test.ts b/test/cli/test/bun-test.test.ts index 4d9e5e8f75b7..bdb6adc238af 100644 --- a/test/cli/test/bun-test.test.ts +++ b/test/cli/test/bun-test.test.ts @@ -2093,24 +2093,21 @@ describe.concurrent("test file discovery (scanner)", () => { ); } - test.skipIf(isWindows)( - "a path argument longer than MAX_PATH_BYTES is skipped like a missing path when other arguments match", - async () => { - using dir = tempDir("scanner-long-arg-mixed", { "exists.test.ts": existsTest }); - const longArg = "/" + Buffer.alloc(5000, "a").toString() + ".test.ts"; + test("a path argument longer than MAX_PATH_BYTES is skipped like a missing path when other arguments match", async () => { + using dir = tempDir("scanner-long-arg-mixed", { "exists.test.ts": existsTest }); + const longArg = "/" + Buffer.alloc(5000, "a").toString() + ".test.ts"; - await using proc = Bun.spawn({ - cmd: [bunExe(), "test", "./exists.test.ts", longArg], - env: bunEnv, - cwd: String(dir), - stderr: "pipe", - }); - const [stderr, exitCode] = await Promise.all([proc.stderr.text(), proc.exited]); + await using proc = Bun.spawn({ + cmd: [bunExe(), "test", "./exists.test.ts", longArg], + env: bunEnv, + cwd: String(dir), + stderr: "pipe", + }); + const [stderr, exitCode] = await Promise.all([proc.stderr.text(), proc.exited]); - expect(stderr).toContain("Ran 1 test across 1 file."); - expect(exitCode).toBe(0); - }, - ); + expect(stderr).toContain("Ran 1 test across 1 file."); + expect(exitCode).toBe(0); + }); // The directory walk joins parent + entry name for every directory it // descends into and every candidate test file. With pathIgnorePatterns diff --git a/test/cli/watch/watch.test.ts b/test/cli/watch/watch.test.ts index bc2982d84f66..6802f91386f6 100644 --- a/test/cli/watch/watch.test.ts +++ b/test/cli/watch/watch.test.ts @@ -1,7 +1,7 @@ import type { Subprocess } from "bun"; import { spawn } from "bun"; import { afterEach, expect, it } from "bun:test"; -import { bunEnv, bunExe, isBroken, isLinux, isMacOS, isWindows, tempDir, tmpdirSync } from "harness"; +import { bunEnv, bunExe, isLinux, isMacOS, isWindows, tempDir, tmpdirSync } from "harness"; import { copyFileSync, readdirSync, @@ -34,39 +34,35 @@ function stdoutWaiter(proc: Subprocess<"ignore", "pipe", any>) { } for (const dir of ["dir", "©️"]) { - it.todoIf(isBroken && isWindows)( - `should watch files${dir === "dir" ? "" : " (non-ascii path)"}`, - async () => { - const cwd = join(tmpdirSync(), dir); - const path = join(cwd, "watchee.js"); + it(`should watch files${dir === "dir" ? "" : " (non-ascii path)"}`, async () => { + const cwd = join(tmpdirSync(), dir); + const path = join(cwd, "watchee.js"); - const updateFile = async (i: number) => { - await Bun.write(path, `console.log(${i}, __dirname);`); - }; + const updateFile = async (i: number) => { + await Bun.write(path, `console.log(${i}, __dirname);`); + }; - let i = 0; - await updateFile(i); - await Bun.sleep(1000); - watchee = spawn({ - cwd, - cmd: [bunExe(), "--watch", "watchee.js"], - env: bunEnv, - stdout: "pipe", - stderr: "inherit", - stdin: "ignore", - }); + let i = 0; + await updateFile(i); + await Bun.sleep(1000); + watchee = spawn({ + cwd, + cmd: [bunExe(), "--watch", "watchee.js"], + env: bunEnv, + stdout: "pipe", + stderr: "inherit", + stdin: "ignore", + }); - for await (const line of watchee.stdout as ReadableStream) { - if (i == 10) break; - var str = new TextDecoder().decode(line); - expect(str).toContain(`${i} ${cwd}`); - i++; - await updateFile(i); - } - rmSync(path); - }, - 10000, - ); + for await (const line of watchee.stdout as ReadableStream) { + if (i == 10) break; + var str = new TextDecoder().decode(line); + expect(str).toContain(`${i} ${cwd}`); + i++; + await updateFile(i); + } + rmSync(path); + }, 10000); } afterEach(async () => { @@ -81,11 +77,9 @@ afterEach(async () => { } }); -it.skipIf(isWindows)( - "process.exit() in a watch kill-signal handler never returns to JS", - async () => { - using dir = tempDir("watch-exit-in-sigterm", { - "exiter.js": `process.on("SIGTERM", () => { +it("process.exit() in a watch kill-signal handler never returns to JS", async () => { + using dir = tempDir("watch-exit-in-sigterm", { + "exiter.js": `process.on("SIGTERM", () => { process.exit(0); require("fs").writeFileSync("should-not-write.txt", "hello"); }); @@ -95,48 +89,46 @@ process.on("SIGTERM", () => { console.log("started"); setInterval(() => {}, 1000); `, - }); - const cwd = String(dir); - const path = join(cwd, "exiter.js"); - watchee = spawn({ - cwd, - cmd: [bunExe(), "--watch", "exiter.js"], - env: bunEnv, - stdout: "pipe", - stderr: "inherit", - stdin: "ignore", - }); - let starts = 0; - let touched = false; - const decoder = new TextDecoder(); - await (async () => { - // Output written before this reader attaches waits in the kernel pipe - // buffer, so no start can be missed. Lines are reassembled across chunk - // boundaries before matching. - let buffered = ""; - for await (const chunk of watchee.stdout as ReadableStream) { - buffered += decoder.decode(chunk); - let newline; - while ((newline = buffered.indexOf("\n")) !== -1) { - const line = buffered.slice(0, newline); - buffered = buffered.slice(newline + 1); - if (line.includes("started") && ++starts === 2) return; - if (starts === 1 && !touched) { - touched = true; - // First boot seen: touch the file to trigger the kill-signal reload. - await Bun.write(path, (await Bun.file(path).text()) + "\n// touched"); - } + }); + const cwd = String(dir); + const path = join(cwd, "exiter.js"); + watchee = spawn({ + cwd, + cmd: [bunExe(), "--watch", "exiter.js"], + env: bunEnv, + stdout: "pipe", + stderr: "inherit", + stdin: "ignore", + }); + let starts = 0; + let touched = false; + const decoder = new TextDecoder(); + await (async () => { + // Output written before this reader attaches waits in the kernel pipe + // buffer, so no start can be missed. Lines are reassembled across chunk + // boundaries before matching. + let buffered = ""; + for await (const chunk of watchee.stdout as ReadableStream) { + buffered += decoder.decode(chunk); + let newline; + while ((newline = buffered.indexOf("\n")) !== -1) { + const line = buffered.slice(0, newline); + buffered = buffered.slice(newline + 1); + if (line.includes("started") && ++starts === 2) return; + if (starts === 1 && !touched) { + touched = true; + // First boot seen: touch the file to trigger the kill-signal reload. + await Bun.write(path, (await Bun.file(path).text()) + "\n// touched"); } } - // The child exiting before the reload start is a failure of the watch - // path itself; without this the absent-file expects below pass vacuously. - throw new Error(`watchee stdout ended after ${starts} start(s); expected 2`); - })(); - expect(await Bun.file(join(cwd, "should-not-write.txt")).exists()).toBe(false); - expect(await Bun.file(join(cwd, "second-listener-ran.txt")).exists()).toBe(false); - }, - 10000, -); + } + // The child exiting before the reload start is a failure of the watch + // path itself; without this the absent-file expects below pass vacuously. + throw new Error(`watchee stdout ended after ${starts} start(s); expected 2`); + })(); + expect(await Bun.file(join(cwd, "should-not-write.txt")).exists()).toBe(false); + expect(await Bun.file(join(cwd, "second-listener-ran.txt")).exists()).toBe(false); +}, 10000); // While one thread is inside execve(2), Linux fails every clone(CLONE_FS) in // the process with EAGAIN until the exec has killed the other threads diff --git a/test/integration/pprof/pprof.test.ts b/test/integration/pprof/pprof.test.ts index 64ce4ead58e9..32f689cf278d 100644 --- a/test/integration/pprof/pprof.test.ts +++ b/test/integration/pprof/pprof.test.ts @@ -1,14 +1,13 @@ import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, canBuildNodeAddons, isBroken, isMusl, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, canBuildNodeAddons, isBroken, isMusl, tempDir } from "harness"; import { join } from "node:path"; // pprof (google/pprof-nodejs) ships no prebuilds: its nan addon is compiled // from source with node-gyp, the same way test/v8 builds its module. Unlike // @datadog/pprof, it drives v8::CpuProfiler through the title-keyed // StartProfiling()/StopProfiling() overloads and CpuProfile::GetTitle() -// (#19678). The source build is skipped on Windows; test/v8 covers those -// overloads on every platform. -describe.skipIf(!canBuildNodeAddons() || isWindows).todoIf(isBroken && isMusl)("pprof", () => { +// (#19678). +describe.skipIf(!canBuildNodeAddons()).todoIf(isBroken && isMusl)("pprof", () => { test("time.profile() returns a profile with samples", async () => { using dir = tempDir("pprof", { "package.json": JSON.stringify({ diff --git a/test/internal/source-lints/windows-cross-config.test.ts b/test/internal/source-lints/windows-cross-config.test.ts index c11356e91550..3fe9bffa44d2 100644 --- a/test/internal/source-lints/windows-cross-config.test.ts +++ b/test/internal/source-lints/windows-cross-config.test.ts @@ -11,7 +11,6 @@ * native toolchain instead. */ import { describe, expect, test } from "bun:test"; -import { isWindows } from "harness"; import { resolveConfig, type Config, type PartialConfig, type Toolchain } from "../../../scripts/build/config.ts"; import { webkit } from "../../../scripts/build/deps/webkit.ts"; @@ -76,7 +75,7 @@ function resolveWindowsCross(partial: PartialConfig = {}, toolchain = mockToolch ); } -describe.skipIf(isWindows)("Windows cross-compile LTO config (non-windows host)", () => { +describe("Windows cross-compile LTO config (non-windows host)", () => { test("ci release x64 cross builds default to ThinLTO with cross-language LTO", () => { const cfg = resolveWindowsCross(); expect(cfg.windows).toBe(true); diff --git a/test/js/bun/console/console-write.test.ts b/test/js/bun/console/console-write.test.ts index 66d6f79fc3c6..f3ca11f2c554 100644 --- a/test/js/bun/console/console-write.test.ts +++ b/test/js/bun/console/console-write.test.ts @@ -126,7 +126,7 @@ console.error(JSON.stringify({ wrote })); // The Promise a backed-up console.write() returns is where its write error arrives: a script that // awaits it can handle a reader that hung up, instead of dying with an unhandled rejection. -test.skipIf(isWindows)("an awaited console.write rejects with EPIPE when the reader has hung up", async () => { +test("an awaited console.write rejects with EPIPE when the reader has hung up", async () => { await using proc = Bun.spawn({ cmd: [ bunExe(), diff --git a/test/js/bun/http/bun-listen-connect-args.test.ts b/test/js/bun/http/bun-listen-connect-args.test.ts index 7c582dc5f094..a89d041fca75 100644 --- a/test/js/bun/http/bun-listen-connect-args.test.ts +++ b/test/js/bun/http/bun-listen-connect-args.test.ts @@ -1,7 +1,7 @@ import { describe, test } from "bun:test"; -import { cwdScope, isWindows, tempDir } from "harness"; +import { cwdScope, tempDir } from "harness"; -describe.if(!isWindows)("unix socket", () => { +describe("unix socket", () => { test("valid", () => { using server = Bun.listen({ unix: Math.random().toString(32).slice(2, 15) + ".sock", diff --git a/test/js/bun/http/fetch-file-upload.test.ts b/test/js/bun/http/fetch-file-upload.test.ts index f077df18d360..451f74d51bd7 100644 --- a/test/js/bun/http/fetch-file-upload.test.ts +++ b/test/js/bun/http/fetch-file-upload.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { isBroken, isWindows, tempDir, withoutAggressiveGC } from "harness"; +import { tempDir, withoutAggressiveGC } from "harness"; import { tmpdir } from "os"; import { join } from "path"; @@ -126,38 +126,34 @@ test("formData uploads roundtrip, without a call to .body", async () => { expect(await (resData.get("file") as Blob).arrayBuffer()).toEqual(await file.arrayBuffer()); }); -test.todoIf(isBroken && isWindows)( - "uploads roundtrip with sendfile()", - async () => { - const hugeTxt = Buffer.allocUnsafe(1024 * 1024 * 32 * "huge".length); - hugeTxt.fill("huge"); - const hash = Bun.CryptoHasher.hash("sha256", hugeTxt, "hex"); +test("uploads roundtrip with sendfile()", async () => { + const hugeTxt = Buffer.allocUnsafe(1024 * 1024 * 32 * "huge".length); + hugeTxt.fill("huge"); + const hash = Bun.CryptoHasher.hash("sha256", hugeTxt, "hex"); - const path = join(tmpdir(), "huge.txt"); - require("fs").writeFileSync(path, hugeTxt); - using server = Bun.serve({ - port: 0, - development: false, - maxRequestBodySize: hugeTxt.byteLength * 2, - async fetch(req) { - const hasher = new Bun.CryptoHasher("sha256"); - for await (let chunk of req.body!) { - hasher.update(chunk); - } - return new Response(hasher.digest("hex")); - }, - }); + const path = join(tmpdir(), "huge.txt"); + require("fs").writeFileSync(path, hugeTxt); + using server = Bun.serve({ + port: 0, + development: false, + maxRequestBodySize: hugeTxt.byteLength * 2, + async fetch(req) { + const hasher = new Bun.CryptoHasher("sha256"); + for await (let chunk of req.body!) { + hasher.update(chunk); + } + return new Response(hasher.digest("hex")); + }, + }); - const resp = await fetch(server.url, { - body: Bun.file(path), - method: "PUT", - }); + const resp = await fetch(server.url, { + body: Bun.file(path), + method: "PUT", + }); - expect(resp.status).toBe(200); - expect(await resp.text()).toBe(hash); - }, - 10_000, -); + expect(resp.status).toBe(200); + expect(await resp.text()).toBe(hash); +}, 10_000); describe("Bun.file().slice() upload sends the slice's Content-Length", () => { // The sendfile fast path is entered when the backing file is >= 32 KiB. diff --git a/test/js/bun/http/http-server-chunking.test.ts b/test/js/bun/http/http-server-chunking.test.ts index 6f3dc38b1566..6c03a0e9631d 100644 --- a/test/js/bun/http/http-server-chunking.test.ts +++ b/test/js/bun/http/http-server-chunking.test.ts @@ -1,9 +1,9 @@ import type { Socket } from "bun"; import { setSocketOptions } from "bun:internal-for-testing"; import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isASAN, isPosix } from "harness"; +import { bunEnv, bunExe, isASAN } from "harness"; -describe.if(isPosix)("HTTP server handles chunked transfer encoding", () => { +describe("HTTP server handles chunked transfer encoding", () => { test.concurrentIf(!isASAN)("handles fragmented chunk terminators", async () => { const script = ` const server = Bun.serve({ @@ -108,7 +108,7 @@ describe.if(isPosix)("HTTP server handles chunked transfer encoding", () => { }); }); -describe.if(isPosix)("HTTP server handles split chunk-size CRLF", () => { +describe("HTTP server handles split chunk-size CRLF", () => { test.concurrentIf(!isASAN)("handles lone CR at end of chunk-size line across TCP segments", async () => { // Regression test: when a TCP segment boundary falls between the \r and \n // of a chunk-size line (e.g. "5\r" in one segment, "\n..." in the next), @@ -530,7 +530,7 @@ describe.if(isPosix)("HTTP server handles split chunk-size CRLF", () => { }); }); -describe.if(isPosix)("HTTP server handles fragmented requests", () => { +describe("HTTP server handles fragmented requests", () => { test.concurrentIf(!isASAN)("handles requests with tiny send buffer (regression test)", async () => { using server = Bun.serve({ hostname: "127.0.0.1", diff --git a/test/js/bun/http/serve.test.ts b/test/js/bun/http/serve.test.ts index 5c0da1622716..9b9fdcafe1c6 100644 --- a/test/js/bun/http/serve.test.ts +++ b/test/js/bun/http/serve.test.ts @@ -15,7 +15,6 @@ import { isIPv4, isIPv6, isLinux, - isPosix, isWindows, libcPathForDlopen, MAX_PATH_BYTES, @@ -2948,7 +2947,7 @@ describe("server.requestIP", () => { }); }); - it.if(isPosix)("server.requestIP (unix)", async () => { + it("server.requestIP (unix)", async () => { const unix = join(tmpdirSync(), "serve.sock"); using server = Bun.serve({ unix, @@ -3962,7 +3961,7 @@ it("only serves /bun:info to loopback clients in development mode", async () => expect(externalRes.status).toBe(200); }); -it.if(isPosix)("serves /bun:info over a unix socket in development mode", async () => { +it("serves /bun:info over a unix socket in development mode", async () => { using dir = tempDir("info", {}); const unix = join(String(dir), "bun-info.sock"); using server = Bun.serve({ diff --git a/test/js/bun/image/image.test.ts b/test/js/bun/image/image.test.ts index 805aa81a0f03..032160f8360e 100644 --- a/test/js/bun/image/image.test.ts +++ b/test/js/bun/image/image.test.ts @@ -1679,7 +1679,7 @@ describe("decode-only formats (BMP / TIFF / GIF)", () => { expect(Buffer.compare(got, ref)).toBe(0); }); - if (!isMacOS && !isWindows) { + if (!isMacOS) { test("TIFF on Linux throws UnsupportedOnPlatform", async () => { const tiff = Buffer.from("II*\x00\x08\x00\x00\x00", "binary"); await expect(new Bun.Image(tiff).png().bytes()).rejects.toMatchObject({ code: "ERR_IMAGE_FORMAT_UNSUPPORTED" }); diff --git a/test/js/bun/io/bun-write.test.js b/test/js/bun/io/bun-write.test.js index bcbd9deef142..fe0e2208cb70 100644 --- a/test/js/bun/io/bun-write.test.js +++ b/test/js/bun/io/bun-write.test.js @@ -544,7 +544,7 @@ const IS_UV_FS_COPYFILE_DISABLED = // must terminate on EOF, not on the stat-derived budget. // Bun.spawn({stdin:"pipe"}) hands the child a socketpair, not a FIFO, so // run the pipeline under sh to get real kernel pipes on fd 0/1. - it.skipIf(isWindows)("Bun.write(Bun.stdout, Bun.stdin) copies the whole pipe (> 4096 bytes)", async () => { + it("Bun.write(Bun.stdout, Bun.stdin) copies the whole pipe (> 4096 bytes)", async () => { const size = 1024 * 1024; const script = `process.stderr.write(String(await Bun.write(Bun.stdout, Bun.stdin)))`; diff --git a/test/js/bun/jsc/bun-jsc.test.ts b/test/js/bun/jsc/bun-jsc.test.ts index a8ec60ca32d4..e23197a3b4b9 100644 --- a/test/js/bun/jsc/bun-jsc.test.ts +++ b/test/js/bun/jsc/bun-jsc.test.ts @@ -26,7 +26,7 @@ import { totalCompileTime, } from "bun:jsc"; import { describe, expect, it } from "bun:test"; -import { bunEnv, bunExe, isBuildKite, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; import { createHash, type webcrypto } from "node:crypto"; import { existsSync, readdirSync, readFileSync } from "node:fs"; import { join } from "node:path"; @@ -245,7 +245,7 @@ describe("bun:jsc", () => { Bun.gc(true); }); - it.todoIf(isBuildKite && isWindows)("profile async", async () => { + it("profile async", async () => { const { promise, resolve } = Promise.withResolvers(); const result = await profile( async function hey(arg1: number) { @@ -259,7 +259,7 @@ describe("bun:jsc", () => { expect({ ...input }).toStrictEqual({ "0": 2 }); }); - it.todoIf(isBuildKite && isWindows)("profile can be called multiple times", () => { + it("profile can be called multiple times", () => { // Fibonacci generates deep stacks and is CPU-intensive function fib(n: number): number { if (n <= 1) return n; diff --git a/test/js/bun/module-graph/module-graph-isolation.test.ts b/test/js/bun/module-graph/module-graph-isolation.test.ts index 4e1ecf891b3e..624986b13776 100644 --- a/test/js/bun/module-graph/module-graph-isolation.test.ts +++ b/test/js/bun/module-graph/module-graph-isolation.test.ts @@ -3666,15 +3666,12 @@ describe.concurrent("ModuleGraph isolation: a disposed graph leaves nothing behi test("a global agent the host put in place of node's is what a graph's requests without an agent go through", async () => { expect(await runsFixture("host-replaced-the-global-agent.mjs")).toEqual({ stdout: `{"status":200}`, exitCode: 0 }); }); - test.skipIf(isWindows)( - "a child its leftover script spawns and writes to is not started: nothing fails, in it or in the host", - async () => { - expect(await runsFixture("spawns-after-it-was-disposed.mjs")).toEqual({ - stdout: `{"hostStillRuns":true}`, - exitCode: 0, - }); - }, - ); + test("a child its leftover script spawns and writes to is not started: nothing fails, in it or in the host", async () => { + expect(await runsFixture("spawns-after-it-was-disposed.mjs")).toEqual({ + stdout: `{"hostStillRuns":true}`, + exitCode: 0, + }); + }); test("a server it was disposed in the turn it listened is not announced to it: its listen callback cannot fail the host", async () => { expect(await runsFixture("disposed-in-the-turn-it-listens.mjs")).toEqual({ stdout: `{"heard":[]}`, exitCode: 0 }); }); diff --git a/test/js/bun/net/socket.test.ts b/test/js/bun/net/socket.test.ts index d58b87944327..f2df06296a68 100644 --- a/test/js/bun/net/socket.test.ts +++ b/test/js/bun/net/socket.test.ts @@ -504,7 +504,7 @@ describe.concurrent("socket", () => { expect(opened).toBe(true); }); - it.skipIf(isWindows)("should not leak file descriptors when connecting", async () => { + it("should not leak file descriptors when connecting", async () => { expect(await bunRun(fileURLToPath(new URL("./socket-leak-fixture.js", import.meta.url)))).toSpawn(); }); @@ -3795,54 +3795,48 @@ Reo= }; } - it.skipIf(isWindows)( - "verifies the server certificate against localhost over a unix socket when no serverName is given", - async () => { - using t = await connectOverUnix("uds-tls-identity-reject", { ca: CA1_CRT }); - expect(await t.handshake.promise).toEqual({ - authorizedArg: false, - authorizedGetter: false, - callbackError: AGENT1_LOCALHOST_MISMATCH, - callbackCode: "ERR_TLS_CERT_ALTNAME_INVALID", - getterError: AGENT1_LOCALHOST_MISMATCH, - getterCode: "ERR_TLS_CERT_ALTNAME_INVALID", - }); - await t.closed.promise; - expect(t.received).toEqual([]); - expect((t.client.getAuthorizationError() as any)?.code).toBe("ERR_TLS_CERT_ALTNAME_INVALID"); - - using ok = await connectOverUnix("uds-tls-identity-match", { ca: CA1_CRT, serverName: "agent1" }); - expect(await ok.handshake.promise).toEqual({ - authorizedArg: true, - authorizedGetter: true, - callbackError: null, - callbackCode: null, - getterError: null, - getterCode: null, - }); - ok.client.write("ping"); - await ok.echoed.promise; - expect(ok.received.join("")).toBe("hello-from-server\nping"); - }, - ); + it("verifies the server certificate against localhost over a unix socket when no serverName is given", async () => { + using t = await connectOverUnix("uds-tls-identity-reject", { ca: CA1_CRT }); + expect(await t.handshake.promise).toEqual({ + authorizedArg: false, + authorizedGetter: false, + callbackError: AGENT1_LOCALHOST_MISMATCH, + callbackCode: "ERR_TLS_CERT_ALTNAME_INVALID", + getterError: AGENT1_LOCALHOST_MISMATCH, + getterCode: "ERR_TLS_CERT_ALTNAME_INVALID", + }); + await t.closed.promise; + expect(t.received).toEqual([]); + expect((t.client.getAuthorizationError() as any)?.code).toBe("ERR_TLS_CERT_ALTNAME_INVALID"); - it.skipIf(isWindows)( - "reports authorized=false over a unix socket with rejectUnauthorized: false when the certificate does not name localhost", - async () => { - using t = await connectOverUnix("uds-tls-identity-keep", { ca: CA1_CRT, rejectUnauthorized: false }); - expect(await t.handshake.promise).toEqual({ - authorizedArg: false, - authorizedGetter: false, - callbackError: AGENT1_LOCALHOST_MISMATCH, - callbackCode: "ERR_TLS_CERT_ALTNAME_INVALID", - getterError: AGENT1_LOCALHOST_MISMATCH, - getterCode: "ERR_TLS_CERT_ALTNAME_INVALID", - }); - t.client.write("ping"); - await t.echoed.promise; - expect(t.received.join("")).toBe("hello-from-server\nping"); - }, - ); + using ok = await connectOverUnix("uds-tls-identity-match", { ca: CA1_CRT, serverName: "agent1" }); + expect(await ok.handshake.promise).toEqual({ + authorizedArg: true, + authorizedGetter: true, + callbackError: null, + callbackCode: null, + getterError: null, + getterCode: null, + }); + ok.client.write("ping"); + await ok.echoed.promise; + expect(ok.received.join("")).toBe("hello-from-server\nping"); + }); + + it("reports authorized=false over a unix socket with rejectUnauthorized: false when the certificate does not name localhost", async () => { + using t = await connectOverUnix("uds-tls-identity-keep", { ca: CA1_CRT, rejectUnauthorized: false }); + expect(await t.handshake.promise).toEqual({ + authorizedArg: false, + authorizedGetter: false, + callbackError: AGENT1_LOCALHOST_MISMATCH, + callbackCode: "ERR_TLS_CERT_ALTNAME_INVALID", + getterError: AGENT1_LOCALHOST_MISMATCH, + getterCode: "ERR_TLS_CERT_ALTNAME_INVALID", + }); + t.client.write("ping"); + await t.echoed.promise; + expect(t.received.join("")).toBe("hello-from-server\nping"); + }); }); // https://github.com/oven-sh/bun/issues/33754 diff --git a/test/js/bun/net/tcp-server.test.ts b/test/js/bun/net/tcp-server.test.ts index 8078f75e683b..4039091adf55 100644 --- a/test/js/bun/net/tcp-server.test.ts +++ b/test/js/bun/net/tcp-server.test.ts @@ -1,6 +1,6 @@ import { connect, listen, SocketHandler, TCPSocketListener } from "bun"; import { describe, expect, it } from "bun:test"; -import { bunEnv, bunExe, isWindows } from "harness"; +import { bunEnv, bunExe } from "harness"; import { join } from "node:path"; type Resolve = (value?: unknown) => void; @@ -296,7 +296,7 @@ describe("tcp socket binaryType", () => { } }); -it.skipIf(isWindows)("stop(true) between ticks closes every socket when a close handler ticks the loop", async () => { +it("stop(true) between ticks closes every socket when a close handler ticks the loop", async () => { await using proc = Bun.spawn({ cmd: [ bunExe(), diff --git a/test/js/bun/net/unix-socket-unlink.test.ts b/test/js/bun/net/unix-socket-unlink.test.ts index 85857483ea38..8ee6faf3c68c 100644 --- a/test/js/bun/net/unix-socket-unlink.test.ts +++ b/test/js/bun/net/unix-socket-unlink.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { isLinux, isWindows, tempDir } from "harness"; +import { isLinux, tempDir } from "harness"; import { once } from "node:events"; import { existsSync, writeFileSync } from "node:fs"; import { createServer } from "node:net"; @@ -10,7 +10,7 @@ import { join } from "node:path"; // - close() DOES unlink the socket file it created // Bun previously had this inverted (unlinked before bind, leaked on close). -describe.skipIf(isWindows)("unix domain socket unlink", () => { +describe("unix domain socket unlink", () => { test("Bun.listen removes the socket file on stop()", () => { using dir = tempDir("uds-unlink-listen", {}); const sock = join(String(dir), "a.sock"); diff --git a/test/js/bun/patch/patch.test.ts b/test/js/bun/patch/patch.test.ts index d2c7d7f0d701..07b491987d61 100644 --- a/test/js/bun/patch/patch.test.ts +++ b/test/js/bun/patch/patch.test.ts @@ -1122,7 +1122,7 @@ describe("apply", () => { ["a new file in a directory the patch creates", creation("newdir/new.txt"), "newdir/new.txt"], ]; - describe.if(process.platform !== "win32")("symlinks", () => { + describe("symlinks", () => { async function link(root: string) { await fs.symlink(join(root, "outside.txt"), join(root, "pkg", "link")); await fs.symlink(join(root, "outside"), join(root, "pkg", "evildir")); @@ -1334,7 +1334,7 @@ describe("makeDiff", () => { }); // The old folder is a git checkout in the cache, the new one a copy of it that left the links out. - test.skipIf(process.platform === "win32")("leaves out the symlinks that only the old folder has", async () => { + test("leaves out the symlinks that only the old folder has", async () => { await using tempdir = tempDir("patch-make-diff-links", { "a/a.txt": "one\n", "a/m.txt": "one\n", diff --git a/test/js/bun/resolve/dynamic-import-evaluation-error-gc.test.ts b/test/js/bun/resolve/dynamic-import-evaluation-error-gc.test.ts index 2249180322ca..cec432e42cae 100644 --- a/test/js/bun/resolve/dynamic-import-evaluation-error-gc.test.ts +++ b/test/js/bun/resolve/dynamic-import-evaluation-error-gc.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; // computeErrorInfoWrapperToString (src/jsc/bindings/FormatStackTraceForJS.cpp, // installed via vm.setOnComputeErrorInfo) runs from @@ -21,14 +21,8 @@ import { bunEnv, bunExe, isWindows, tempDir } from "harness"; // and GC cycles of a long loop; a single iteration does not fire, 100 fire on // about 1 run in 6, 300 on essentially every run of the unfixed assert build. // The fixture runs twice and a single crash fails the test. -// -// Skipped on Windows: two runs of a 300-iteration loop that writes 2100 files -// and evaluates 1200 module graphs are several times slower under -// Windows + ASAN in CI, and the fixed C++ path is not platform-specific. -test.skipIf(isWindows)( - "import() of a module whose evaluation throws survives a GC stack-trace finalizer", - async () => { - const fixture = ` +test("import() of a module whose evaluation throws survives a GC stack-trace finalizer", async () => { + const fixture = ` import { mkdirSync, writeFileSync } from "node:fs"; import { join } from "node:path"; @@ -52,21 +46,17 @@ test.skipIf(isWindows)( console.log("ok"); `; - using dir = tempDir("dynamic-import-eval-error-gc", { "fixture.mjs": fixture }); + using dir = tempDir("dynamic-import-eval-error-gc", { "fixture.mjs": fixture }); - for (let i = 0; i < 2; i++) { - await using proc = Bun.spawn({ - cmd: [bunExe(), "fixture.mjs"], - cwd: String(dir), - env: bunEnv, - stdio: ["ignore", "pipe", "pipe"], - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect({ stdout: stdout.trim(), exitCode }).toEqual({ stdout: "ok", exitCode: 0 }); - void stderr; - } - }, - // Two runs of a 300-iteration loop on a debug+ASAN build take well over the - // default 5s. - 120_000, -); + for (let i = 0; i < 2; i++) { + await using proc = Bun.spawn({ + cmd: [bunExe(), "fixture.mjs"], + cwd: String(dir), + env: bunEnv, + stdio: ["ignore", "pipe", "pipe"], + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect({ stdout: stdout.trim(), exitCode }).toEqual({ stdout: "ok", exitCode: 0 }); + void stderr; + } +}, 120_000); // Two runs of a 300-iteration loop on a debug+ASAN build take well over the default 5s. diff --git a/test/js/bun/resolve/resolve-error.test.ts b/test/js/bun/resolve/resolve-error.test.ts index 7eb5390e0440..72109ef214bb 100644 --- a/test/js/bun/resolve/resolve-error.test.ts +++ b/test/js/bun/resolve/resolve-error.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "bun:test"; -import { bunEnv, bunExe, isWindows, MAX_PATH_BYTES, normalizeBunSnapshot, tempDir } from "harness"; +import { bunEnv, bunExe, MAX_PATH_BYTES, normalizeBunSnapshot, tempDir } from "harness"; import path from "node:path"; describe("ResolveMessage", () => { @@ -499,14 +499,9 @@ describe.concurrent("Bun.resolve() rejections are tracked", () => { // collectContinuously keeps an end phase always imminent. The unfixed debug // build panics after 2500 to 8500 iterations. Not concurrent with the tests // above: a busy machine starves the collector and hides the race. -// -// Skipped on Windows: collectContinuously is several times slower under -// Windows + ASAN in CI, and the fixed C++ path is not platform-specific. -it.skipIf(isWindows)( - "Bun.resolve() rejection survives a GC stack-trace finalizer", - async () => { - using dir = tempDir("bun-resolve-rejection-gc", { - "fixture.mjs": ` +it("Bun.resolve() rejection survives a GC stack-trace finalizer", async () => { + using dir = tempDir("bun-resolve-rejection-gc", { + "fixture.mjs": ` const keep = []; let rejections = 0; for (let i = 0; i < 20000; i++) { @@ -519,20 +514,16 @@ it.skipIf(isWindows)( } console.log("ok rejections=" + rejections); `, - }); + }); - await using proc = Bun.spawn({ - cmd: [bunExe(), "fixture.mjs"], - env: { ...bunEnv, BUN_JSC_collectContinuously: "1" }, - cwd: String(dir), - stdout: "pipe", - stderr: "pipe", - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect({ stdout: stdout.trim(), exitCode }).toEqual({ stdout: "ok rejections=20000", exitCode: 0 }); - void stderr; - }, - // 20000 awaited rejections under collectContinuously on a debug+ASAN build - // take well over the default 5s. - 120_000, -); + await using proc = Bun.spawn({ + cmd: [bunExe(), "fixture.mjs"], + env: { ...bunEnv, BUN_JSC_collectContinuously: "1" }, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect({ stdout: stdout.trim(), exitCode }).toEqual({ stdout: "ok rejections=20000", exitCode: 0 }); + void stderr; +}, 120_000); // 20000 awaited rejections under collectContinuously on a debug+ASAN build take well over the default 5s. diff --git a/test/js/bun/resolve/resolve.test.ts b/test/js/bun/resolve/resolve.test.ts index bee8739d8625..eba7e5d44a80 100644 --- a/test/js/bun/resolve/resolve.test.ts +++ b/test/js/bun/resolve/resolve.test.ts @@ -299,7 +299,7 @@ it("import long string should not segfault", async () => { } catch {} }); -it.skipIf(isWindows)("resolves an absolute specifier that is longer than a path buffer until it is normalized", () => { +it("resolves an absolute specifier that is longer than a path buffer until it is normalized", () => { using dir = tempDir("resolve-long-specifier", { "index.js": "" }); expect(Bun.resolveSync(`${dir}${Buffer.alloc(5000, "/").toString()}index.js`, String(dir))).toBe( join(String(dir), "index.js"), @@ -469,7 +469,7 @@ describe("When CJS and ESM are mixed", () => { // The "browser" map resolver copied the normalized input path into a 512-byte // threadlocal buffer without a bounds check. Paths inside deep directory trees // can easily exceed 512 bytes while still being well under MAX_PATH_BYTES. -it.skipIf(isWindows)("browser map resolution handles relative paths longer than 512 bytes", async () => { +it("browser map resolution handles relative paths longer than 512 bytes", async () => { // Build a nested relative path longer than 512 bytes. Each component stays // well under NAME_MAX and the absolute path stays well under MAX_PATH_BYTES. const segments: string[] = []; @@ -2010,7 +2010,7 @@ it.skipIf(isWindows)("runs a script from a working directory nested 256 director expect(exitCode).toBe(0); }); -it.skipIf(isWindows)("reports a resolution error for an absolute specifier of the maximum path length", async () => { +it("reports a resolution error for an absolute specifier of the maximum path length", async () => { using dir = tempDir("resolver-max-length-specifier", { "package.json": JSON.stringify({ name: "host" }), }); diff --git a/test/js/bun/shell/bunshell.test.ts b/test/js/bun/shell/bunshell.test.ts index 4093aeb1c6f8..0a61cc143f9b 100644 --- a/test/js/bun/shell/bunshell.test.ts +++ b/test/js/bun/shell/bunshell.test.ts @@ -2052,7 +2052,7 @@ describe("deno_task", () => { }); // https://github.com/oven-sh/bun/issues/11305 - test.todoIf(isWindows)("stacktrace", async () => { + test("stacktrace", async () => { // const folder = TestBuilder.tmpdir(); const code = /* ts */ `import { $ } from 'bun' @@ -3392,60 +3392,57 @@ test("redirect target buffer stays attached while a builtin command is running", expect(stringifyBuffer(buffer)).toEqual("pin.txt\n"); }); -test.skipIf(isWindows)( - "output redirect buffer for an external command is detachable as soon as the command settles", - async () => { - // `> ${buf}` for an external command is pinned until the child's stdout - // reaches EOF. Here `sh` prints and exits at once while a background - // grandchild inherits the stdout pipe and holds it open until the gated - // fetch below is answered. The process exit and the stderr EOF are then - // processed long before the stdout EOF, so the stdout reader is the - // callback that settles the promise. The code after `await` must already - // see the buffer unpinned: a `transfer()` detaches it instead of copying. - const buffer = new Uint8Array(new ArrayBuffer(1 << 16)); - const gate = Promise.withResolvers(); - const grandchildStarted = Promise.withResolvers(); - await using server = Bun.serve({ - port: 0, - async fetch() { - grandchildStarted.resolve(); - await gate.promise; - return new Response("ok"); - }, - }); - const grandchildCode = `await fetch(${JSON.stringify(String(server.url))})`; - const promise = $`sh -c ${'"$0" -e "$1" 2>/dev/null & echo hi'} ${BUN} ${grandchildCode} > ${buffer}` - .env(bunEnv) - .nothrow(); - const running = promise.then(o => o); - // By the time the request arrives, `sh` has exited and the grandchild is - // the only holder of the stdout pipe. If the command settles first, the - // grandchild never held it: fail with the shell's output instead of - // waiting on the gate until the test times out. - await Promise.race([ - grandchildStarted.promise, - running.then(r => { - throw new Error(`command settled before the grandchild connected (exit ${r.exitCode}): ${r.stderr}`); - }), - ]); - - // The grandchild still holds stdout, so the buffer is pinned: a detach - // attempt copies instead (or throws). - try { - buffer.buffer.transfer(); - } catch {} - const detachedWhileOpen = buffer.buffer.detached; - gate.resolve(); - expect(detachedWhileOpen).toBe(false); +test("output redirect buffer for an external command is detachable as soon as the command settles", async () => { + // `> ${buf}` for an external command is pinned until the child's stdout + // reaches EOF. Here `sh` prints and exits at once while a background + // grandchild inherits the stdout pipe and holds it open until the gated + // fetch below is answered. The process exit and the stderr EOF are then + // processed long before the stdout EOF, so the stdout reader is the + // callback that settles the promise. The code after `await` must already + // see the buffer unpinned: a `transfer()` detaches it instead of copying. + const buffer = new Uint8Array(new ArrayBuffer(1 << 16)); + const gate = Promise.withResolvers(); + const grandchildStarted = Promise.withResolvers(); + await using server = Bun.serve({ + port: 0, + async fetch() { + grandchildStarted.resolve(); + await gate.promise; + return new Response("ok"); + }, + }); + const grandchildCode = `await fetch(${JSON.stringify(String(server.url))})`; + const promise = $`sh -c ${'"$0" -e "$1" 2>/dev/null & echo hi'} ${BUN} ${grandchildCode} > ${buffer}` + .env(bunEnv) + .nothrow(); + const running = promise.then(o => o); + // By the time the request arrives, `sh` has exited and the grandchild is + // the only holder of the stdout pipe. If the command settles first, the + // grandchild never held it: fail with the shell's output instead of + // waiting on the gate until the test times out. + await Promise.race([ + grandchildStarted.promise, + running.then(r => { + throw new Error(`command settled before the grandchild connected (exit ${r.exitCode}): ${r.stderr}`); + }), + ]); + + // The grandchild still holds stdout, so the buffer is pinned: a detach + // attempt copies instead (or throws). + try { + buffer.buffer.transfer(); + } catch {} + const detachedWhileOpen = buffer.buffer.detached; + gate.resolve(); + expect(detachedWhileOpen).toBe(false); - const result = await running; - expect(stringifyBuffer(buffer)).toEqual("hi\n"); + const result = await running; + expect(stringifyBuffer(buffer)).toEqual("hi\n"); - buffer.buffer.transfer(); - expect(buffer.buffer.detached).toBe(true); - expect(result.exitCode).toBe(0); - }, -); + buffer.buffer.transfer(); + expect(buffer.buffer.detached).toBe(true); + expect(result.exitCode).toBe(0); +}); test("a builtin stops writing to a redirect target that was resized to 0", async () => { const ab = new ArrayBuffer(1 << 20, { maxByteLength: 1 << 21 }); @@ -3465,7 +3462,7 @@ test("a builtin stops writing to a redirect target that was resized to 0", async }); describe.each(["> ", "2>&1 "])("an external command with a `%s${buf}` redirect", redirect => { - test.skipIf(isWindows)("drops the output that arrives after the target was resized to 0", async () => { + test("drops the output that arrives after the target was resized to 0", async () => { const ab = new ArrayBuffer(1 << 20, { maxByteLength: 1 << 21 }); const buffer = new Uint8Array(ab); const gate = Promise.withResolvers(); @@ -3592,7 +3589,7 @@ describe("stdin redirect from a zero-length buffer delivers EOF to the spawned c }); }); -describe.skipIf(isWindows)("stdin redirect whose pipe outlives the command's process", () => { +describe("stdin redirect whose pipe outlives the command's process", () => { // `< ${input}` is pumped into the child over a pipe. A command finishes once // its exit code and the stdin, stdout and stderr closes are all in, and the // four arrive as separate event-loop callbacks. Here the child hands its diff --git a/test/js/bun/shell/commands/cat.test.ts b/test/js/bun/shell/commands/cat.test.ts index 0b126eddd353..ea583a7162ce 100644 --- a/test/js/bun/shell/commands/cat.test.ts +++ b/test/js/bun/shell/commands/cat.test.ts @@ -64,7 +64,7 @@ describe.concurrent("cat (builtin) sharing one stdin reader", () => { describe.concurrent("cat (builtin) after a read error", () => { // A directory opens but cannot be read. Linux reports that while the read starts. - test.skipIf(isWindows).each([ + test.each([ ["cat . || echo failed", false, "failed\n"], ["true | cat . || echo failed", true, "failed\n"], ["cat . | cat . || echo failed", false, "failed\n"], diff --git a/test/js/bun/shell/commands/rm.test.ts b/test/js/bun/shell/commands/rm.test.ts index c8cb3747918b..7d54109aadca 100644 --- a/test/js/bun/shell/commands/rm.test.ts +++ b/test/js/bun/shell/commands/rm.test.ts @@ -245,50 +245,47 @@ function packagejson() { // swaps against the walker; the file behind the symlink must survive every // time. The legitimate case (real directories that are not swapped in time) // is exercised by the same loop: those entries are simply deleted. -test.skipIf(process.platform === "win32")( - "recursive rm does not follow a directory entry replaced by a symlink during deletion", - async () => { - const ENTRIES = 64; - const FILLER = 8; - const ITERATIONS = 10; - - for (let iter = 0; iter < ITERATIONS; iter++) { - const files: Record = { - "victim/keep.txt": "important", - "stash/.keep": "", - }; - for (let i = 0; i < ENTRIES; i++) { - for (let j = 0; j < FILLER; j++) { - files[`target/d${i}/f${j}.txt`] = ""; - } +test("recursive rm does not follow a directory entry replaced by a symlink during deletion", async () => { + const ENTRIES = 64; + const FILLER = 8; + const ITERATIONS = 10; + + for (let iter = 0; iter < ITERATIONS; iter++) { + const files: Record = { + "victim/keep.txt": "important", + "stash/.keep": "", + }; + for (let i = 0; i < ENTRIES; i++) { + for (let j = 0; j < FILLER; j++) { + files[`target/d${i}/f${j}.txt`] = ""; } - await using root = tempDir(`rm-swap-${iter}`, files); - const victimDir = path.join(root, "victim"); - const victimFile = path.join(victimDir, "keep.txt"); - const target = path.join(root, "target"); - - // Start the recursive delete on the worker pool, then immediately - // replace each subdirectory with a symlink pointing at the victim - // directory while the walk is in flight. - const running = ($`rm -rf ${target}`.nothrow().quiet() as any).run(); - for (let i = 0; i < ENTRIES; i++) { - const entry = path.join(target, `d${i}`); - try { - renameSync(entry, path.join(root, "stash", `d${i}`)); - symlinkSync(victimDir, entry); - } catch { - // The walker may have already deleted this entry; that's fine. - } + } + await using root = tempDir(`rm-swap-${iter}`, files); + const victimDir = path.join(root, "victim"); + const victimFile = path.join(victimDir, "keep.txt"); + const target = path.join(root, "target"); + + // Start the recursive delete on the worker pool, then immediately + // replace each subdirectory with a symlink pointing at the victim + // directory while the walk is in flight. + const running = ($`rm -rf ${target}`.nothrow().quiet() as any).run(); + for (let i = 0; i < ENTRIES; i++) { + const entry = path.join(target, `d${i}`); + try { + renameSync(entry, path.join(root, "stash", `d${i}`)); + symlinkSync(victimDir, entry); + } catch { + // The walker may have already deleted this entry; that's fine. } - await running; - - // The contents of the directory behind the symlink must never be - // deleted, no matter when the swap landed relative to the walk. - expect(existsSync(victimFile)).toBeTrue(); - expect(existsSync(victimDir)).toBeTrue(); } - }, -); + await running; + + // The contents of the directory behind the symlink must never be + // deleted, no matter when the swap landed relative to the walk. + expect(existsSync(victimFile)).toBeTrue(); + expect(existsSync(victimDir)).toBeTrue(); + } +}); // The recursive walk joined every entry onto its directory's path inside a // fixed-size path buffer on the worker thread, so a tree deeper than PATH_MAX @@ -383,45 +380,42 @@ test.skipIf(process.platform === "win32")( }, ); -test.skipIf(process.platform === "win32")( - "relative operands are resolved against the shell cwd, not the process cwd", - async () => { - using dir = tempDir("rm-shell-cwd", { - "work/file.txt": "content", - "work/sub/inner.txt": "content", - "keep.txt": "keep", - }); - const base = String(dir); - const shellCwd = path.join(base, "work"); +test("relative operands are resolved against the shell cwd, not the process cwd", async () => { + using dir = tempDir("rm-shell-cwd", { + "work/file.txt": "content", + "work/sub/inner.txt": "content", + "keep.txt": "keep", + }); + const base = String(dir); + const shellCwd = path.join(base, "work"); - const fixture = /* ts */ ` + const fixture = /* ts */ ` import { $ } from "bun"; const shellCwd = process.env.SHELL_CWD!; const { exitCode, stderr } = await $\`rm -rf .\`.cwd(shellCwd).quiet().nothrow(); console.log(JSON.stringify({ exitCode, stderr: stderr.toString() })); `; - await using proc = Bun.spawn({ - cmd: [bunExe(), "-e", fixture], - env: { ...bunEnv, SHELL_CWD: shellCwd }, - cwd: "/", - stdout: "pipe", - stderr: "pipe", - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - const result = JSON.parse(stdout.trim()); - expect(result.exitCode).toBe(1); - expect(result.stderr).toMatch(/^rm: \.: /); - if (process.platform === "linux") { - // Only Linux permits unlinking "." out from under itself; macOS returns - // before iterating, so its children survive there. - expect(existsSync(path.join(base, "work", "file.txt"))).toBeFalse(); - expect(existsSync(path.join(base, "work", "sub"))).toBeFalse(); - } - expect(existsSync(path.join(base, "work"))).toBeTrue(); - expect(existsSync(path.join(base, "keep.txt"))).toBeTrue(); - expect(exitCode).toBe(0); - }, -); + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", fixture], + env: { ...bunEnv, SHELL_CWD: shellCwd }, + cwd: "/", + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + const result = JSON.parse(stdout.trim()); + expect(result.exitCode).toBe(1); + expect(result.stderr).toMatch(/^rm: \.: /); + if (process.platform === "linux") { + // Only Linux permits unlinking "." out from under itself; macOS returns + // before iterating, so its children survive there. + expect(existsSync(path.join(base, "work", "file.txt"))).toBeFalse(); + expect(existsSync(path.join(base, "work", "sub"))).toBeFalse(); + } + expect(existsSync(path.join(base, "work"))).toBeTrue(); + expect(existsSync(path.join(base, "keep.txt"))).toBeTrue(); + expect(exitCode).toBe(0); +}); // The preserve-root check resolved every operand through the fixed-size // thread-local path buffers (1024 bytes for normalizing, 4096 for joining), diff --git a/test/js/bun/shell/commands/which.test.ts b/test/js/bun/shell/commands/which.test.ts index 4e04bb15f5de..8816d7d66472 100644 --- a/test/js/bun/shell/commands/which.test.ts +++ b/test/js/bun/shell/commands/which.test.ts @@ -55,7 +55,7 @@ test.concurrent("which searches $PATH for bare names and the shell cwd for ./ na expect(exitCode).toBe(0); }); -test.skipIf(isWindows)("which with an absolute path at the platform path length limit reports not found", async () => { +test("which with an absolute path at the platform path length limit reports not found", async () => { const fixture = /* ts */ ` import { $ } from "bun"; const max = process.platform === "linux" ? 4096 : 1024; diff --git a/test/js/bun/shell/epipe.test.ts b/test/js/bun/shell/epipe.test.ts index dcb545d8b37d..c7e9779f3c9d 100644 --- a/test/js/bun/shell/epipe.test.ts +++ b/test/js/bun/shell/epipe.test.ts @@ -1,10 +1,10 @@ import { $ } from "bun"; import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isPosix, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; import { createTestBuilder } from "./test_builder"; const TestBuilder = createTestBuilder(import.meta.path); -describe.if(isPosix)("IOWriter epipe", () => { +describe("IOWriter epipe", () => { TestBuilder.command`yes | head` .exitCode(0) .stdout("y\ny\ny\ny\ny\ny\ny\ny\ny\ny\n") @@ -40,7 +40,7 @@ describe.concurrent("a pipeline stage whose reader exits without reading", () => }); // These two finish at their first failed chunk, with more chunks queued behind it. - test.if(isPosix).each([ + test.each([ ["builtin cat", "await $`head -c 1048576 /dev/zero | cat | true`.quiet();"], [ "relayed subprocess output", diff --git a/test/js/bun/shell/shell-blocking-pipe.test.ts b/test/js/bun/shell/shell-blocking-pipe.test.ts index 3249b2caa3a6..53725f6b78a3 100644 --- a/test/js/bun/shell/shell-blocking-pipe.test.ts +++ b/test/js/bun/shell/shell-blocking-pipe.test.ts @@ -23,7 +23,7 @@ test.skipIf(isWindows)("writing > send buffer size doesn't block the main thread } }); -test.skipIf(isWindows)("writing > send buffer size (with a variable) doesn't block the main thread", async () => { +test("writing > send buffer size (with a variable) doesn't block the main thread", async () => { const expected = Buffer.alloc(1024 * 1024, "bun!").toString(); const result = await $`echo ${expected} | ${Bun.which("cat")}`.text(); diff --git a/test/js/bun/shell/shell-cmdsub-crash.test.ts b/test/js/bun/shell/shell-cmdsub-crash.test.ts index 43f95ac8f52c..d17856da0cb9 100644 --- a/test/js/bun/shell/shell-cmdsub-crash.test.ts +++ b/test/js/bun/shell/shell-cmdsub-crash.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; // Regression test for use-after-poison in builtin OutputTask callbacks // inside command substitution $(). @@ -14,7 +14,7 @@ import { bunEnv, bunExe, isWindows, tempDir } from "harness"; // alongside non-existent paths reliably triggers the ASAN // use-after-poison. -describe.skipIf(isWindows)("builtins in command substitution with errors should not crash", () => { +describe("builtins in command substitution with errors should not crash", () => { test("ls with errors in command substitution", async () => { // Create a temp directory with many files to produce output, // and include non-existent paths to produce errors. diff --git a/test/js/bun/shell/shelloutput.test.ts b/test/js/bun/shell/shelloutput.test.ts index 3ed8f0f697a8..28d923ff44f4 100644 --- a/test/js/bun/shell/shelloutput.test.ts +++ b/test/js/bun/shell/shelloutput.test.ts @@ -1,6 +1,6 @@ import { $ } from "bun"; import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isPosix } from "harness"; +import { bunEnv, bunExe } from "harness"; import { totalmem } from "os"; describe("ShellOutput + ShellError", () => { @@ -33,7 +33,7 @@ describe("ShellOutput + ShellError", () => { // callback) had never carried an error. This is the only way to exercise it. The // child holds 4 GiB of zeros (8 GiB while its buffer grows), so this runs in a // child process, with a long timeout, and only on machines with room. -describe.skipIf(!isPosix || totalmem() < 16 * 1024 ** 3)("stdout at the Buffer length limit", () => { +describe.skipIf(totalmem() < 16 * 1024 ** 3)("stdout at the Buffer length limit", () => { test("a capture of 2^32 + 1 bytes rejects with the RangeError that new ArrayBuffer(2 ** 32 + 1) throws", async () => { await using proc = Bun.spawn({ cmd: [ diff --git a/test/js/bun/spawn/bun-ipc-inherit.test.ts b/test/js/bun/spawn/bun-ipc-inherit.test.ts index 20ac5de46797..7c086a699ea7 100644 --- a/test/js/bun/spawn/bun-ipc-inherit.test.ts +++ b/test/js/bun/spawn/bun-ipc-inherit.test.ts @@ -29,7 +29,7 @@ it.todoIf(isWindows)("spawning a bun package script should inherit the ipc fd", expect(testMessage).toBe("hello"); }); -it.skipIf(isWindows)("bun exec doesn't pass NODE_CHANNEL_FD to a command that doesn't inherit the ipc fd", async () => { +it("bun exec doesn't pass NODE_CHANNEL_FD to a command that doesn't inherit the ipc fd", async () => { await using proc = spawn({ // Print the variables from sh, because bun and node delete them at startup. cmd: [bunExe(), "exec", `sh -c 'echo "[$NODE_CHANNEL_FD] [$NODE_CHANNEL_SERIALIZATION_MODE]"'`], diff --git a/test/js/bun/spawn/spawn-kill-signal.test.ts b/test/js/bun/spawn/spawn-kill-signal.test.ts index bda46aad3034..50c33cffc24e 100644 --- a/test/js/bun/spawn/spawn-kill-signal.test.ts +++ b/test/js/bun/spawn/spawn-kill-signal.test.ts @@ -101,7 +101,7 @@ const unsupportedSignals = (["SIGSTKFLT", "SIGPWR"] as const).filter(name => !(n const quiet: { stdio: ["ignore", "ignore", "ignore"] } = { stdio: ["ignore", "ignore", "ignore"] }; -describe.concurrent.skipIf(isWindows)("signal names map to the OS's own numbers", () => { +describe.concurrent("signal names map to the OS's own numbers", () => { describe.each(platformSignals)("%s", name => { const number: number = constants.signals[name]; diff --git a/test/js/bun/spawn/spawn-maxbuf.test.ts b/test/js/bun/spawn/spawn-maxbuf.test.ts index 12cd7a55a924..d901749d73bc 100644 --- a/test/js/bun/spawn/spawn-maxbuf.test.ts +++ b/test/js/bun/spawn/spawn-maxbuf.test.ts @@ -200,7 +200,7 @@ describe("timeout kills the process", () => { ["timeout", () => ({ timeout: 200 })], ["AbortSignal", () => ({ signal: AbortSignal.timeout(200) })], ] as const)("via %s", (_, opt) => { - test.skipIf(isWindows)("Bun.spawn stdout does not hang when a grandchild outlives the kill", async () => { + test("Bun.spawn stdout does not hang when a grandchild outlives the kill", async () => { // `sh` spawns `sleep` before the stdout marker so the assertion proves a // grandchild holds the pipe's write end when the kill signal reaches `sh`. await using proc = Bun.spawn({ diff --git a/test/js/bun/spawn/spawn-pipe-leak.test.ts b/test/js/bun/spawn/spawn-pipe-leak.test.ts index 86024690b3ff..f803225e8b7c 100644 --- a/test/js/bun/spawn/spawn-pipe-leak.test.ts +++ b/test/js/bun/spawn/spawn-pipe-leak.test.ts @@ -5,7 +5,7 @@ * and then exits. We only await the `process.exited` promise without reading * any of the output data to test for potential memory leaks. */ -import { bunExe, isASAN, isCI, isWindows, rss } from "harness"; +import { bunExe, isASAN, isCI, rss } from "harness"; describe.todoIf( /** @@ -141,11 +141,7 @@ describe.todoIf( await run(dontRead); }, 30_000); - test.todoIf(isWindows)( - "'pipe' stdout if read before exit should not leak memory", - async () => { - await run(readPipeBeforeExit); - }, - 30_000, - ); + test("'pipe' stdout if read before exit should not leak memory", async () => { + await run(readPipeBeforeExit); + }, 30_000); }); diff --git a/test/js/bun/spawn/spawn-renamed-cwd.test.ts b/test/js/bun/spawn/spawn-renamed-cwd.test.ts index 3c69e2db7f4f..ecdcd9ea211d 100644 --- a/test/js/bun/spawn/spawn-renamed-cwd.test.ts +++ b/test/js/bun/spawn/spawn-renamed-cwd.test.ts @@ -52,7 +52,7 @@ test.skipIf(isWindows)("spawn without explicit cwd works after the parent's cwd }); // An explicit `cwd` option must still chdir the child. -test.skipIf(isWindows)("spawn with explicit cwd still changes the child's directory", async () => { +test("spawn with explicit cwd still changes the child's directory", async () => { using dir = tempDir("spawn-explicit-cwd", { "sub/.keep": "" }); const sub = join(String(dir), "sub"); diff --git a/test/js/bun/spawn/spawn-stdin-pipe-fd-leak.test.ts b/test/js/bun/spawn/spawn-stdin-pipe-fd-leak.test.ts index e452319bf36f..ba8bc641b7e7 100644 --- a/test/js/bun/spawn/spawn-stdin-pipe-fd-leak.test.ts +++ b/test/js/bun/spawn/spawn-stdin-pipe-fd-leak.test.ts @@ -139,7 +139,7 @@ test.skipIf(!isASAN || isWindows)( // Reading `.stdin` after the child has already exited should still return // the FileSink (not `undefined`) — the fix must not regress this. -test.skipIf(!isPosix)("reading .stdin after child exit still returns a FileSink", async () => { +test("reading .stdin after child exit still returns a FileSink", async () => { const p = Bun.spawn({ cmd: ["true"], stdin: "pipe", stdout: "ignore", stderr: "ignore" }); await p.exited; const stdin = p.stdin; diff --git a/test/js/bun/spawn/spawn-stdin-readable-stream.test.ts b/test/js/bun/spawn/spawn-stdin-readable-stream.test.ts index 5e8249419967..b4d27e2a7b23 100644 --- a/test/js/bun/spawn/spawn-stdin-readable-stream.test.ts +++ b/test/js/bun/spawn/spawn-stdin-readable-stream.test.ts @@ -969,36 +969,33 @@ describe("spawn stdin ReadableStream", () => { // Bun.write() locks the process's stdin stream without starting its lazy source. A call that fails on that lock // must not start the source either. Nothing reads the child's stdout until the child reports, so its write is // still paused at that point. - test.skipIf(isWindows)( - "process.stdin throws when Bun.write() has locked the stdin stream, and doesn't consume any bytes", - async () => { - using dir = tempDir("spawn-stdin-lazy", { "in": Buffer.alloc(1 << 20, "a").toString() }); - await using proc = spawn({ - cmd: [ - bunExe(), - "-e", - ` + test("process.stdin throws when Bun.write() has locked the stdin stream, and doesn't consume any bytes", async () => { + using dir = tempDir("spawn-stdin-lazy", { "in": Buffer.alloc(1 << 20, "a").toString() }); + await using proc = spawn({ + cmd: [ + bunExe(), + "-e", + ` const written = Bun.write(Bun.stdout, Bun.stdin.stream()); let code; try { process.stdin.resume(); } catch (error) { code = error.code; } await Bun.write(Bun.stderr, String(code)); await written; `, - ], - env: bunEnv, - stdin: Bun.file(path.join(String(dir), "in")), - stdout: "pipe", - stderr: "pipe", - }); - const report = await proc.stderr.getReader().read(); - const [stdout, exitCode] = await Promise.all([proc.stdout.bytes(), proc.exited]); - expect({ report: new TextDecoder().decode(report.value), length: stdout.length, exitCode }).toEqual({ - report: "ERR_INVALID_STATE", - length: 1 << 20, - exitCode: 0, - }); - }, - ); + ], + env: bunEnv, + stdin: Bun.file(path.join(String(dir), "in")), + stdout: "pipe", + stderr: "pipe", + }); + const report = await proc.stderr.getReader().read(); + const [stdout, exitCode] = await Promise.all([proc.stdout.bytes(), proc.exited]); + expect({ report: new TextDecoder().decode(report.value), length: stdout.length, exitCode }).toEqual({ + report: "ERR_INVALID_STATE", + length: 1 << 20, + exitCode: 0, + }); + }); test("ReadableStream object type count", async () => { const iterations = isASAN diff --git a/test/js/bun/spawn/spawn-unread-stdout-gc.test.ts b/test/js/bun/spawn/spawn-unread-stdout-gc.test.ts index 348ce30afe99..aa9b100ce1c4 100644 --- a/test/js/bun/spawn/spawn-unread-stdout-gc.test.ts +++ b/test/js/bun/spawn/spawn-unread-stdout-gc.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows } from "harness"; +import { bunEnv, bunExe } from "harness"; // When a child process exits while its stdout/stderr pipe still has pending // activity, onProcessExit keeps the Subprocess JSRef Strong. The pipe later @@ -17,10 +17,8 @@ import { bunEnv, bunExe, isWindows } from "harness"; // loop — the path that leaked. // // Windows uses a different pipe/process model; the scenario is POSIX-shaped. -test.skipIf(isWindows)( - "Subprocess is collectable when stdout pipe drains asynchronously after process exit", - async () => { - const script = /* js */ ` +test("Subprocess is collectable when stdout pipe drains asynchronously after process exit", async () => { + const script = /* js */ ` let collected = 0; const registry = new FinalizationRegistry(() => { collected++; @@ -68,29 +66,27 @@ test.skipIf(isWindows)( console.log(JSON.stringify({ collected, iters: ITERS })); `; - await using proc = Bun.spawn({ - cmd: [bunExe(), "-e", script], - env: bunEnv, - stdout: "pipe", - stderr: "pipe", - }); + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", script], + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - const stderrLines = stderr - .split("\n") - .filter(l => l && !l.startsWith("WARNING: ASAN interferes")) - .join("\n"); - expect(stderrLines).toBe(""); - const { collected, iters } = JSON.parse(stdout.trim()); - // Without the fix, ZERO Subprocess wrappers are collected because their - // JSRef is never downgraded. With the fix they are all collectable. We - // accept any nonzero count as proof the mechanism works — under release- - // ASAN each grandchild's LSan-exit can outlast the poll window, so an - // exact-10 assertion is a wall-clock race, not a correctness check. - expect(collected).toBeGreaterThan(0); - expect(iters).toBe(10); - expect(exitCode).toBe(0); - }, - 120_000, -); + const stderrLines = stderr + .split("\n") + .filter(l => l && !l.startsWith("WARNING: ASAN interferes")) + .join("\n"); + expect(stderrLines).toBe(""); + const { collected, iters } = JSON.parse(stdout.trim()); + // Without the fix, ZERO Subprocess wrappers are collected because their + // JSRef is never downgraded. With the fix they are all collectable. We + // accept any nonzero count as proof the mechanism works — under release- + // ASAN each grandchild's LSan-exit can outlast the poll window, so an + // exact-10 assertion is a wall-clock race, not a correctness check. + expect(collected).toBeGreaterThan(0); + expect(iters).toBe(10); + expect(exitCode).toBe(0); +}, 120_000); diff --git a/test/js/bun/spawn/spawn.ipc.test.ts b/test/js/bun/spawn/spawn.ipc.test.ts index 1851c4adbe53..f3f67de7bc53 100644 --- a/test/js/bun/spawn/spawn.ipc.test.ts +++ b/test/js/bun/spawn/spawn.ipc.test.ts @@ -413,7 +413,7 @@ describe("ipc mode advanced", () => { }, ); - it.concurrent.skipIf(isWindows)( + it.concurrent( "closes the channel as soon as a frame header declares a length that's too long to deserialize", async () => { const header = `write(Buffer.from([0x02, 0x01, 0x00, 0x01, 0x00]));`; diff --git a/test/js/bun/spawn/spawn.test.ts b/test/js/bun/spawn/spawn.test.ts index 93ee74557333..9cb4c0e96854 100644 --- a/test/js/bun/spawn/spawn.test.ts +++ b/test/js/bun/spawn/spawn.test.ts @@ -814,7 +814,7 @@ describe("should not hang", () => { // poll is unregistered until the next pull, so it could not even observe the // child going away. Node: readStop() at the highWaterMark leaves the handle // inactive, and a pending read keeps it active. -describe.skipIf(isWindows)("stdout reader of an unref'd child and process lifetime", () => { +describe("stdout reader of an unref'd child and process lifetime", () => { async function run(script: string) { await using proc = spawn({ cmd: [bunExe(), "-e", script], @@ -1143,7 +1143,7 @@ describe("close handling", () => { describe("stdio[N>=3] blob-like inputs", () => { const readFd3 = `const fs = require("fs"); const b = Buffer.alloc(64); const n = fs.readSync(3, b); process.stdout.write(b.subarray(0, n));`; - it.skipIf(isWindows)("Bun.file(path) at index >= 3 is readable in the child", async () => { + it("Bun.file(path) at index >= 3 is readable in the child", async () => { const file = join(tmp, "stdio-extra-bunfile.txt"); writeFileSync(file, "from-bun-file"); await using proc = spawn({ @@ -1155,7 +1155,7 @@ describe("close handling", () => { expect({ stdout, stderr, exitCode }).toEqual({ stdout: "from-bun-file", stderr: "", exitCode: 0 }); }); - it.skipIf(isWindows)("Bun.file(fd) at index >= 3 is readable in the child", async () => { + it("Bun.file(fd) at index >= 3 is readable in the child", async () => { const file = join(tmp, "stdio-extra-bunfile-fd.txt"); writeFileSync(file, "from-bun-file-fd"); const fd = openSync(file, "r"); @@ -1172,7 +1172,7 @@ describe("close handling", () => { } }); - it.skipIf(isWindows)("empty Blob at index >= 3 is treated as ignore", async () => { + it("empty Blob at index >= 3 is treated as ignore", async () => { await using proc = spawn({ cmd: [bunExe(), "-e", "process.stdout.write('ok')"], env: bunEnv, @@ -1453,7 +1453,7 @@ it("throws when an ArrayBufferView is used for stdout or stderr", async () => { expect(exitCode).toBe(0); }); -it.skipIf(isWindows)("leaves a caller-supplied stdout fd open when stdin stream setup fails", async () => { +it("leaves a caller-supplied stdout fd open when stdin stream setup fails", async () => { const file = join(tmp, "stdin-setup-failure.txt"); const fixture = ` const { openSync, fstatSync, writeSync, closeSync } = require("node:fs"); @@ -1490,7 +1490,7 @@ it.skipIf(isWindows)("leaves a caller-supplied stdout fd open when stdin stream expect(exitCode).toBe(0); }); -it.skipIf(isWindows)("leaves a Bun.file(fd) stdout open when stdin stream setup fails", async () => { +it("leaves a Bun.file(fd) stdout open when stdin stream setup fails", async () => { // Bun.file(fd) as stdout is an fd-backed Blob; extract_blob lowers it to // Stdio::Fd before spawn, so the error-path cleanup must recognise it as // caller-owned via the Fd variant and leave it open. @@ -1713,7 +1713,7 @@ describe("onDisconnect", () => { expect(await proc.exited).toBe(0); }); - it.todoIf(isWindows)("onDisconnect callback is called when IPC disconnects", async () => { + it("onDisconnect callback is called when IPC disconnects", async () => { const disc = Promise.withResolvers(); let disconnectCalled = false; diff --git a/test/js/bun/spawn/spawnSync.test.ts b/test/js/bun/spawn/spawnSync.test.ts index 9ccf716fae0c..aff4aad07006 100644 --- a/test/js/bun/spawn/spawnSync.test.ts +++ b/test/js/bun/spawn/spawnSync.test.ts @@ -125,7 +125,7 @@ describe("spawnSync", () => { // same path. Each case makes the child hold 4 GiB of zeros (the read buffer // doubles to 8 GiB on the way), so the cases run one at a time, in a child // process, with a long timeout, and only on machines with room. -describe.skipIf(!isPosix || totalmem() < 16 * 1024 ** 3)("spawnSync output at the Buffer length limit", () => { +describe.skipIf(totalmem() < 16 * 1024 ** 3)("spawnSync output at the Buffer length limit", () => { async function captureZeros(size: number) { await using proc = Bun.spawn({ cmd: [ diff --git a/test/js/bun/sqlite/sqlite.test.js b/test/js/bun/sqlite/sqlite.test.js index 46997fa616c7..cd7a52b9294e 100644 --- a/test/js/bun/sqlite/sqlite.test.js +++ b/test/js/bun/sqlite/sqlite.test.js @@ -2533,10 +2533,9 @@ it("can continue to use existing statements after database has been GC'd", async Bun.gc(true); await Bun.sleep(1); Bun.gc(true); - if (!isWindows) { - // on Windows, FinalizationRegistry is more flaky than on POSIX. - expect(called).toBe(true); - } + + // on Windows, FinalizationRegistry is more flaky than on POSIX. + expect(called).toBe(true); }); it("statements should be disposable", () => { diff --git a/test/js/bun/terminal/terminal-platform-gaps.test.ts b/test/js/bun/terminal/terminal-platform-gaps.test.ts index ba26a7e927e5..3ccebe822079 100644 --- a/test/js/bun/terminal/terminal-platform-gaps.test.ts +++ b/test/js/bun/terminal/terminal-platform-gaps.test.ts @@ -103,7 +103,7 @@ describe("Bun.Terminal platform behaviour", () => { }, }); // POSIX: enable ECHO so the line discipline reflects writes back. - if (!isWindows) terminal.localFlags = terminal.localFlags | 0x8; + terminal.localFlags = terminal.localFlags | 0x8; terminal.write("ping\n"); await Promise.race([got.promise, Bun.sleep(200)]); diff --git a/test/js/bun/terminal/terminal.test.ts b/test/js/bun/terminal/terminal.test.ts index a83265912306..87d04f6ed1ef 100644 --- a/test/js/bun/terminal/terminal.test.ts +++ b/test/js/bun/terminal/terminal.test.ts @@ -335,15 +335,13 @@ describe("Bun.Terminal", () => { // write() on a full PTY holds that lock until the master's owner (the // very JS thread calling setRawMode) reads the master, freezing the // whole runtime. - test.skipIf(isWindows)( - "does not deadlock while the child is blocked writing to a full PTY", - async () => { - using dir = tempDir("terminal-setrawmode-deadlock", {}); - const pidFile = join(String(dir), "child.pid"); - // The child floods the PTY and blocks in write(). The sync spin keeps - // the event loop from reading the master, so the PTY buffer is still - // full (and the child still blocked) when setRawMode runs. - const script = ` + test("does not deadlock while the child is blocked writing to a full PTY", async () => { + using dir = tempDir("terminal-setrawmode-deadlock", {}); + const pidFile = join(String(dir), "child.pid"); + // The child floods the PTY and blocks in write(). The sync spin keeps + // the event loop from reading the master, so the PTY buffer is still + // full (and the child still blocked) when setRawMode runs. + const script = ` const fs = require("node:fs"); const proc = Bun.spawn(["head", "-c", "200000", "/dev/zero"], { terminal: { cols: 80, rows: 24, data() {} }, @@ -357,44 +355,42 @@ describe("Bun.Terminal", () => { await proc.exited; process.exit(0); `; - await using proc = Bun.spawn({ - cmd: [bunExe(), "-e", script], - env: { ...bunEnv, PID_FILE: pidFile }, - stdout: "pipe", - stderr: "pipe", - }); - - // Watchdog, not a wait-for-time: on a broken build the spawned bun - // never exits on its own, and SIGKILL alone cannot reap it (the - // kernel retries the blocked termios ioctl with the signal pending - // until the PTY child dies). Kill the child's process group first to - // release the ioctl so the process can actually die. - let setRawModeDeadlocked = false; - const watchdog = setTimeout(() => { - setRawModeDeadlocked = true; - try { - const childPid = parseInt(readFileSync(pidFile, "utf8").trim(), 10); - if (childPid > 0) { - // The PTY child is a session leader (setsid), so its pid is its - // process group id: kill the whole group. - process.kill(-childPid, "SIGKILL"); - } - } catch {} - proc.kill("SIGKILL"); - }, 10_000); - - const [stdout, stderr, exitCode] = await Promise.all([ - proc.stdout.text(), - proc.stderr.text(), - proc.exited, - ]).finally(() => clearTimeout(watchdog)); + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", script], + env: { ...bunEnv, PID_FILE: pidFile }, + stdout: "pipe", + stderr: "pipe", + }); - expect(setRawModeDeadlocked).toBe(false); - expect(stdout).toBe("RETURNED\n"); - expect(exitCode).toBe(0); - }, - 20_000, - ); + // Watchdog, not a wait-for-time: on a broken build the spawned bun + // never exits on its own, and SIGKILL alone cannot reap it (the + // kernel retries the blocked termios ioctl with the signal pending + // until the PTY child dies). Kill the child's process group first to + // release the ioctl so the process can actually die. + let setRawModeDeadlocked = false; + const watchdog = setTimeout(() => { + setRawModeDeadlocked = true; + try { + const childPid = parseInt(readFileSync(pidFile, "utf8").trim(), 10); + if (childPid > 0) { + // The PTY child is a session leader (setsid), so its pid is its + // process group id: kill the whole group. + process.kill(-childPid, "SIGKILL"); + } + } catch {} + proc.kill("SIGKILL"); + }, 10_000); + + const [stdout, stderr, exitCode] = await Promise.all([ + proc.stdout.text(), + proc.stderr.text(), + proc.exited, + ]).finally(() => clearTimeout(watchdog)); + + expect(setRawModeDeadlocked).toBe(false); + expect(stdout).toBe("RETURNED\n"); + expect(exitCode).toBe(0); + }, 20_000); }); describe("termios flags", () => { @@ -1654,7 +1650,7 @@ describe.concurrent("Bun.spawn with terminal option", () => { // If it got that far before the parent's kqueue watch (ESRCH), the parent used to block in wait4() on the // thread that reads the pty, forever. The worker and the garbage slow the parent down into that window. const limit = isDebug || isASAN ? 60_000 : 20_000; - test.skipIf(isWindows)( + test( "a pty child that exits before the parent watches it is still reaped", async () => { using dir = tempDir("pty-early-exit", { diff --git a/test/js/bun/test/jest-each-gc-root.test.ts b/test/js/bun/test/jest-each-gc-root.test.ts index a8858421f054..c081caa75879 100644 --- a/test/js/bun/test/jest-each-gc-root.test.ts +++ b/test/js/bun/test/jest-each-gc-root.test.ts @@ -9,7 +9,7 @@ // useZombieMode scribbles 0xbadbeef0 over swept cells so the dangling access // manifests as a hard crash / wrong-type error instead of a heisenbug. import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; const fixture = ` import { test, describe, expect } from "bun:test"; @@ -96,7 +96,7 @@ test("test.each/describe.each table array is a GC root", async () => { ...bunEnv, BUN_JSC_useZombieMode: "1", }; - if (!isWindows) gcEnv.BUN_JSC_collectContinuously = "1"; + gcEnv.BUN_JSC_collectContinuously = "1"; await using proc = Bun.spawn({ cmd: [bunExe(), "test", "each-gc.test.ts"], diff --git a/test/js/bun/transpiler/transpiler-error-gc-uaf.test.ts b/test/js/bun/transpiler/transpiler-error-gc-uaf.test.ts index 6f7973eba064..bf07e1bd9444 100644 --- a/test/js/bun/transpiler/transpiler-error-gc-uaf.test.ts +++ b/test/js/bun/transpiler/transpiler-error-gc-uaf.test.ts @@ -10,7 +10,7 @@ // manifests deterministically; collectContinuously races the collector against // the allocation loop so it reliably sweeps mid-loop. import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; const fixture = ` const src = Array.from({ length: 300 }, () => "a b").join("\\n"); @@ -41,7 +41,7 @@ test("Log.to_js roots BuildMessage cells across allocation", async () => { ...bunEnv, BUN_JSC_useZombieMode: "1", }; - if (!isWindows) gcEnv.BUN_JSC_collectContinuously = "1"; + gcEnv.BUN_JSC_collectContinuously = "1"; await using proc = Bun.spawn({ cmd: [bunExe(), "fixture.js"], diff --git a/test/js/bun/udp/dgram.test.ts b/test/js/bun/udp/dgram.test.ts index 26574b6eb8ab..5b799914e41f 100644 --- a/test/js/bun/udp/dgram.test.ts +++ b/test/js/bun/udp/dgram.test.ts @@ -440,7 +440,7 @@ test("setBroadcast()/setMulticastLoopback() before bind() throw EBADF", () => { // An oversized datagram fails send(2) with EMSGSIZE; on the connected path no // address/port is known, so the error must match Node's bare `send `. -test.skipIf(isWindows)("connected send() failure reports Node's error shape", async () => { +test("connected send() failure reports Node's error shape", async () => { const receiver = createSocket("udp4"); const { promise: receiverBound, resolve: onReceiverBound } = Promise.withResolvers(); receiver.bind(0, "127.0.0.1", onReceiverBound); @@ -500,7 +500,7 @@ test("connect() with a custom lookup that yields a name that cannot be a hostnam // Every send callback must fire with (null, byteLength) — never (null, 0) or // EAGAIN — even if the kernel refuses some writes (they queue and drain like // libuv's uv_udp_try_send → uv_udp_send fallback). -test.skipIf(isWindows)("send() reports (null, byteLength) for every callback under a burst", async () => { +test("send() reports (null, byteLength) for every callback under a burst", async () => { const receiver = createSocket("udp4"); await new Promise(resolve => receiver.bind(0, "127.0.0.1", resolve)); const port = receiver.address().port; diff --git a/test/js/bun/util/bun-file-fd-read.test.ts b/test/js/bun/util/bun-file-fd-read.test.ts index fe1e53191e31..cbefcfe44c23 100644 --- a/test/js/bun/util/bun-file-fd-read.test.ts +++ b/test/js/bun/util/bun-file-fd-read.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test"; import { closeSync, openSync } from "fs"; -import { isWindows, tempDir } from "harness"; +import { tempDir } from "harness"; import { join } from "path"; // Reading a Bun.file() backed by a file descriptor goes through @@ -10,7 +10,7 @@ import { join } from "path"; // so an abnormal fstat size could trip integerOutOfBounds. Triggering that // directly requires fstat to report > 4.5 PB which is not achievable here, // but these tests lock in the fd-backed ReadFile path that the fuzzer hit. -describe.skipIf(isWindows)("Bun.file(fd) read", () => { +describe("Bun.file(fd) read", () => { async function withFd(path: string, fn: (fd: number) => Promise): Promise { const fd = openSync(path, "r"); try { diff --git a/test/js/bun/util/bun-stdin-slice.test.ts b/test/js/bun/util/bun-stdin-slice.test.ts index 509ad92166c0..332d2525b9d2 100644 --- a/test/js/bun/util/bun-stdin-slice.test.ts +++ b/test/js/bun/util/bun-stdin-slice.test.ts @@ -1,11 +1,11 @@ import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows } from "harness"; +import { bunEnv, bunExe } from "harness"; // Reading a sliced non-regular file blob (like stdin from a pipe) with a size // close to Blob.max_size used to overflow when computing the initial read // buffer capacity. The overflow was only reachable on POSIX; on Windows the // ReadFileUV path already bailed on size > ULONG_MAX before the addition. -test.skipIf(isWindows)("Bun.stdin.slice(1).text() does not crash when stdin is a pipe", async () => { +test("Bun.stdin.slice(1).text() does not crash when stdin is a pipe", async () => { await using proc = Bun.spawn({ cmd: [bunExe(), "-e", `process.stdout.write(await Bun.stdin.slice(1).text());`], env: bunEnv, @@ -23,7 +23,7 @@ test.skipIf(isWindows)("Bun.stdin.slice(1).text() does not crash when stdin is a expect(exitCode).toBe(0); }); -test.skipIf(isWindows)("Bun.stdin.slice(0, N).text() caps reads at N bytes", async () => { +test("Bun.stdin.slice(0, N).text() caps reads at N bytes", async () => { await using proc = Bun.spawn({ cmd: [bunExe(), "-e", `process.stdout.write(await Bun.stdin.slice(0, 3).text());`], env: bunEnv, @@ -61,7 +61,7 @@ describe("Bun.stdin.slice(0, N).stream() over a pipe that stays open", () => { }); } - test.concurrent.skipIf(isWindows)("ends after N bytes of a larger write", async () => { + test.concurrent("ends after N bytes of a larger write", async () => { await using proc = spawnSliceEcho(3); proc.stdin.write("0123456789"); await proc.stdin.flush(); @@ -70,30 +70,27 @@ describe("Bun.stdin.slice(0, N).stream() over a pipe that stays open", () => { expect({ stdout, stderr, exitCode }).toEqual({ stdout: "012", stderr: "", exitCode: 0 }); }); - test.concurrent.skipIf(isWindows)( - "delivers a chunk that leaves the slice open, then ends on the one that fills it", - async () => { - await using proc = spawnSliceEcho(4); - const reader = proc.stdout.getReader(); - const decoder = new TextDecoder(); - let stdout = ""; - - proc.stdin.write("01"); - await proc.stdin.flush(); - // Wait for the first chunk to come back out so the second write is a separate read in the child. - for (let r; !stdout.includes("01") && !(r = await reader.read()).done;) { - stdout += decoder.decode(r.value, { stream: true }); - } - expect(stdout).toBe("01"); - - proc.stdin.write("23456"); - await proc.stdin.flush(); - for (let r; !(r = await reader.read()).done;) stdout += decoder.decode(r.value, { stream: true }); - - const [stderr, exitCode] = await Promise.all([proc.stderr.text(), proc.exited]); - expect({ stdout, stderr, exitCode }).toEqual({ stdout: "0123", stderr: "", exitCode: 0 }); - }, - ); + test.concurrent("delivers a chunk that leaves the slice open, then ends on the one that fills it", async () => { + await using proc = spawnSliceEcho(4); + const reader = proc.stdout.getReader(); + const decoder = new TextDecoder(); + let stdout = ""; + + proc.stdin.write("01"); + await proc.stdin.flush(); + // Wait for the first chunk to come back out so the second write is a separate read in the child. + for (let r; !stdout.includes("01") && !(r = await reader.read()).done;) { + stdout += decoder.decode(r.value, { stream: true }); + } + expect(stdout).toBe("01"); + + proc.stdin.write("23456"); + await proc.stdin.flush(); + for (let r; !(r = await reader.read()).done;) stdout += decoder.decode(r.value, { stream: true }); + + const [stderr, exitCode] = await Promise.all([proc.stderr.text(), proc.exited]); + expect({ stdout, stderr, exitCode }).toEqual({ stdout: "0123", stderr: "", exitCode: 0 }); + }); }); // The bytes after the window have to stay in the pipe for whoever reads stdin diff --git a/test/js/bun/util/which.test.ts b/test/js/bun/util/which.test.ts index 63b2de42ab2b..2f1ec068a281 100644 --- a/test/js/bun/util/which.test.ts +++ b/test/js/bun/util/which.test.ts @@ -350,9 +350,7 @@ test("Bun.which does not look in the current directory for bins", async () => { }); process.chdir(String(dir)); try { - if (!isWindows) { - await $`chmod +x ./some_program_name`; - } + await $`chmod +x ./some_program_name`; expect(which("some_program_name")).toBe(null); expect((await $`some_program_name`).exitCode).not.toBe(0); @@ -371,10 +369,8 @@ test("Bun.which does look in the current directory when given a path with a slas }); process.chdir(String(dir)); try { - if (!isWindows) { - await $`chmod +x ./some_program_name`; - await $`chmod +x ./folder/other_app`; - } + await $`chmod +x ./some_program_name`; + await $`chmod +x ./folder/other_app`; const suffix = isWindows ? ".cmd" : ""; @@ -398,9 +394,7 @@ test("Bun.which can find executables in a non-ascii directory", async () => { process.chdir(String(dir)); try { - if (!isWindows) { - await $`chmod +x ./some_program_name`; - } + await $`chmod +x ./some_program_name`; const suffix = isWindows ? ".cmd" : ""; expect(which("./some_program_name")).toBe(join(dir, "some_program_name" + suffix)); diff --git a/test/js/bun/wasm/wasi.test.js b/test/js/bun/wasm/wasi.test.js index 8d1bd1c02bab..3d53c5232f58 100644 --- a/test/js/bun/wasm/wasi.test.js +++ b/test/js/bun/wasm/wasi.test.js @@ -1,6 +1,6 @@ import { spawnSync } from "bun"; import { expect, it } from "bun:test"; -import { bunEnv, bunExe, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; import fs from "node:fs"; import path from "node:path"; import { WASI } from "node:wasi"; @@ -117,10 +117,9 @@ it("path_* syscalls cannot escape the preopened directory", () => { }); const root = String(dir); const sandbox = path.join(root, "sandbox"); - if (!isWindows) { - // a symlink that already exists inside the preopen and points outside of it - fs.symlinkSync(path.join("..", "secret.txt"), path.join(sandbox, "escape")); - } + + // a symlink that already exists inside the preopen and points outside of it + fs.symlinkSync(path.join("..", "secret.txt"), path.join(sandbox, "escape")); const wasi = new WASI({ preopens: { "/": sandbox } }); wasi.setMemory(new WebAssembly.Memory({ initial: 1 })); @@ -146,14 +145,13 @@ it("path_* syscalls cannot escape the preopened directory", () => { expect(fs.existsSync(path.join(root, "secret.txt"))).toBe(true); // (3) a pre-placed symlink inside the preopen that points outside of it - if (!isWindows) { - len = writePath("escape"); - expect(wasi.wasiImport.path_filestat_get(preopenFd, 1, pathPtr, len, statBufPtr)).toBe(WASI_ENOTCAPABLE); - expect(wasi.wasiImport.path_open(preopenFd, 0, pathPtr, len, 0, WASI_RIGHT_FD_READ, BigInt(0), 0, fdPtr)).toBe( - WASI_ENOTCAPABLE, - ); - expect(wasi.FD_MAP.has(4)).toBe(false); - } + + len = writePath("escape"); + expect(wasi.wasiImport.path_filestat_get(preopenFd, 1, pathPtr, len, statBufPtr)).toBe(WASI_ENOTCAPABLE); + expect(wasi.wasiImport.path_open(preopenFd, 0, pathPtr, len, 0, WASI_RIGHT_FD_READ, BigInt(0), 0, fdPtr)).toBe( + WASI_ENOTCAPABLE, + ); + expect(wasi.FD_MAP.has(4)).toBe(false); // a path that stays inside the preopen still works len = writePath("inside.txt"); diff --git a/test/js/bun/webview/webview-chrome.test.ts b/test/js/bun/webview/webview-chrome.test.ts index e6a88be909b0..7956301e0230 100644 --- a/test/js/bun/webview/webview-chrome.test.ts +++ b/test/js/bun/webview/webview-chrome.test.ts @@ -271,7 +271,7 @@ it("chrome: screenshot encoding options", async () => { // shm internally, we create one after decoding). Name uses the // bun-chrome- prefix to disambiguate from WebKit's child-created // segments. - if (process.platform !== "win32") { + { const shm = await view.screenshot({ encoding: "shmem" }); expect(typeof shm.name).toBe("string"); expect(shm.name.startsWith("/bun-chrome-")).toBe(true); diff --git a/test/js/first_party/ws/ws-client-compat-response.test.ts b/test/js/first_party/ws/ws-client-compat-response.test.ts index d1977ee31831..d2e9831264f1 100644 --- a/test/js/first_party/ws/ws-client-compat-response.test.ts +++ b/test/js/first_party/ws/ws-client-compat-response.test.ts @@ -1,7 +1,7 @@ // Tests how a `ws` client reads the body of a non-101 response, and how the server's response to a `perMessageDeflate` // offer with parameters affects the frames the client sends. See ws-raw-peer.ts. import { describe, expect, test } from "bun:test"; -import { tls as harnessTls, isWindows } from "harness"; +import { tls as harnessTls } from "harness"; import crypto from "node:crypto"; import type { IncomingMessage } from "node:http"; import zlib from "node:zlib"; @@ -114,7 +114,7 @@ describe.each(implementations)("ws client handshake response ($name)", row => { if (sent !== lastSent) idle = 0; lastSent = sent; } - if (!isWindows) expect(sent).toBeLessThan(limit); + expect(sent).toBeLessThan(limit); // At most the high water mark plus one socket read. expect(response.readableLength).toBeLessThan(2 * 1024 * 1024); diff --git a/test/js/first_party/ws/ws-server-compat-close.test.ts b/test/js/first_party/ws/ws-server-compat-close.test.ts index c72106b39b8e..4ea0f3011e96 100644 --- a/test/js/first_party/ws/ws-server-compat-close.test.ts +++ b/test/js/first_party/ws/ws-server-compat-close.test.ts @@ -1,7 +1,7 @@ // Tests close(), terminate(), the closing handshake, wss.close() and wss.clients on a `ws` server, using a raw TCP // client. See ws-raw-peer.ts. import { describe, expect, test } from "bun:test"; -import { tls as certificate, isWindows } from "harness"; +import { tls as certificate } from "harness"; import http from "node:http"; import https from "node:https"; import type { TLSSocket } from "node:tls"; @@ -417,7 +417,7 @@ describe.each(implementations)("ws server close ($name)", ({ ws: implementation, }); // Winsock buffers any amount of loopback data, so the process never sees backpressure. - describe.skipIf(isWindows)("under backpressure", () => { + describe("under backpressure", () => { const size = 64 * 1024; // Sends until three messages in a row have left something unwritten. The client runs on this thread, so it can't diff --git a/test/js/first_party/ws/ws-server-compat-transports.test.ts b/test/js/first_party/ws/ws-server-compat-transports.test.ts index a42acbb4763a..164aa3488b5d 100644 --- a/test/js/first_party/ws/ws-server-compat-transports.test.ts +++ b/test/js/first_party/ws/ws-server-compat-transports.test.ts @@ -3,7 +3,7 @@ import fastifyWebsocket from "@fastify/websocket"; import { describe, expect, test } from "bun:test"; import Fastify from "fastify"; -import { tls as certificate, isWindows, tempDir } from "harness"; +import { tls as certificate, tempDir } from "harness"; import { once } from "node:events"; import http from "node:http"; import http2 from "node:http2"; @@ -73,7 +73,7 @@ describe.each(implementations)("ws server transports ($name)", ({ ws: implementa expect(secure).toEqual([true]); }); - test.skipIf(isWindows)("http.Server on a unix socket: address() returns the path", async () => { + test("http.Server on a unix socket: address() returns the path", async () => { using dir = tempDir("ws-server-unix", {}); const socketPath = path.join(String(dir), "ws.sock"); const server = http.createServer(); diff --git a/test/js/node/child_process/child_process.test.ts b/test/js/node/child_process/child_process.test.ts index 7845b6d9f361..c80dc782d85f 100644 --- a/test/js/node/child_process/child_process.test.ts +++ b/test/js/node/child_process/child_process.test.ts @@ -191,7 +191,7 @@ describe("fork() IPC", () => { // libuv maps an inherited stdio slot the parent has closed to /dev/null. Registering a plain // inherit instead captured whichever fd was created next: the ipc socketpair's parent end, which the // child then held open as its stdin, so the parent's disconnect() never reached it. - it.skipIf(isWindows)("inherits a closed stdin as /dev/null, so disconnect() still reaches the child", async () => { + it("inherits a closed stdin as /dev/null, so disconnect() still reaches the child", async () => { const dir = tmpdirSync(); await write( path.join(dir, "parent.js"), @@ -1014,7 +1014,7 @@ it.if(!isWindows)("spawnSync correctly reports signal codes", () => { // OS's name for the number the child died from, as in node. const platformSignals = (["SIGUSR1", "SIGUSR2", "SIGSTKFLT"] as const).filter(name => name in os.constants.signals); -describe.skipIf(!isPosix)("exit signals are named with the OS's own numbering", () => { +describe("exit signals are named with the OS's own numbering", () => { it.concurrent.each(platformSignals)("child.kill(%s) exits with that signal", async name => { const child = spawn("sleep", ["1000"], { stdio: "ignore" }); try { @@ -1086,7 +1086,7 @@ it("spawnSync(does-not-exist)", () => { // run that produced no output. The spawn must fail with EBADF, matching // node. On other POSIX platforms the child-side dup2 of an fd that is not // open fails with EBADF, so the observable behavior is the same. -it.if(!isWindows)("spawn with an fd number at Darwin OPEN_MAX in stdio reports EBADF", () => { +it("spawn with an fd number at Darwin OPEN_MAX in stdio reports EBADF", () => { // Precondition: fd 10240 is not open in this process, so the fd is // invalid on every platform (on Darwin it is invalid by number alone). expect(() => fs.fstatSync(10240)).toThrow(); @@ -1180,7 +1180,7 @@ it.if(isLinux)("spawn still works with more than 10240 fds open", async () => { // GC'd, finalize_streams does not close the fd a second time (EBADF, or // worse, closing a reused fd number). Before the fix, Fd::close()'s // debug_assert!(err.is_none()) panicked under debug_assertions builds. -it.skipIf(isWindows)("extra stdio pipes are not double-closed on GC", async () => { +it("extra stdio pipes are not double-closed on GC", async () => { // Run in a subprocess so GC/finalize timing is isolated from the test // runner's own state, and so the assert abort surfaces as a non-zero // exit rather than taking the whole test runner down. @@ -1426,7 +1426,7 @@ console.log(JSON.stringify({ uid: process.getuid(), threwCode: thrown?.code, thr // 'exit' fired the autoResume path discarded the entire buffered output, so a // late reader received 0 bytes. With kernel backpressure the child blocks at // the pipe buffer until JS starts reading, matching Node. -describe.skipIf(!isPosix)("stdout pipe backpressure", () => { +describe("stdout pipe backpressure", () => { it("blocks the child until a reader attaches and delivers every byte", async () => { const SIZE = 1024 * 1024; const c = spawn("sh", ["-c", `head -c ${SIZE} /dev/zero`], { diff --git a/test/js/node/cluster.test.ts b/test/js/node/cluster.test.ts index 034a68b99502..b750a7b7247e 100644 --- a/test/js/node/cluster.test.ts +++ b/test/js/node/cluster.test.ts @@ -305,7 +305,7 @@ if (cluster.isPrimary) { expect(stdout).toContain("worker exit: 0"); }); -test.skipIf(isWindows)("round-robin accepted sockets honor allowHalfOpen after the client's FIN", async () => { +test("round-robin accepted sockets honor allowHalfOpen after the client's FIN", async () => { const dir = tempDirWithFiles("bun-test", { "main.ts": ` const cluster = require("node:cluster"); @@ -684,7 +684,7 @@ test.each(["net", "http"])("cluster 'listening' reports the address a %s server const dir = tempDirWithFiles("cluster-listening", { "fixture.js": listeningPayloadFixture }); const targets: ({ host: string | null } | { path: string })[] = [{ host: "127.0.0.1" }, { host: null }]; if (isIPv6()) targets.push({ host: "::1" }); - if (!isWindows) targets.push({ path: joinP(dir, `${moduleName}.sock`) }); + targets.push({ path: joinP(dir, `${moduleName}.sock`) }); const { stdout } = await bunRun(joinP(dir, "fixture.js"), { MODULE: moduleName, TARGETS: JSON.stringify(targets) }); const payloads = JSON.parse(stdout); @@ -935,11 +935,9 @@ if (cluster.isPrimary) { expect(stdout).toContain("TLS and non-TLS cluster workers cannot share"); }, 30_000); -test.skipIf(isWindows)( - "SCHED_NONE listen({fd:2}) fails EINVAL like node and does not close the primary's stderr", - async () => { - const dir = tempDirWithFiles("bun-test", { - "main.ts": ` +test("SCHED_NONE listen({fd:2}) fails EINVAL like node and does not close the primary's stderr", async () => { + const dir = tempDirWithFiles("bun-test", { + "main.ts": ` const cluster = require("node:cluster"); const net = require("node:net"); const fs = require("node:fs"); @@ -969,12 +967,11 @@ if (cluster.isPrimary) { server.listen({ fd: 2 }); } `, - }); - const { stdout } = await bunRun(joinP(dir, "main.ts"), bunEnv); - expect(stdout).toContain("worker error code: EINVAL"); - expect(stdout).toContain("stderr open: true"); - }, -); + }); + const { stdout } = await bunRun(joinP(dir, "main.ts"), bunEnv); + expect(stdout).toContain("worker error code: EINVAL"); + expect(stdout).toContain("stderr open: true"); +}); test.skipIf(isWindows)("dgram bind({ fd }) on a stream socket doesn't close the primary's server", async () => { using dir = tempDir("cluster-dgram-stream-fd", { @@ -1126,11 +1123,9 @@ if (cluster.isPrimary) { expect(exitCode).toBe(0); }); -test.skipIf(isWindows)( - "round-robin: RST-while-queued handle is dropped, not shipped stale", - async () => { - using dir = tempDir("cluster-rst-queued", { - "main.ts": ` +test("round-robin: RST-while-queued handle is dropped, not shipped stale", async () => { + using dir = tempDir("cluster-rst-queued", { + "main.ts": ` const cluster = require("node:cluster"); const net = require("node:net"); if (cluster.isPrimary) { @@ -1165,20 +1160,18 @@ if (cluster.isPrimary) { server.listen(0, "127.0.0.1"); } `, - }); - await using proc = Bun.spawn({ - cmd: [bunExe(), "main.ts"], - env: bunEnv, - cwd: String(dir), - stdout: "pipe", - stderr: "pipe", - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect({ stdout: stdout.trim(), stderr }).toEqual({ stdout: "worker got: REAL", stderr: expect.any(String) }); - expect(exitCode).toBe(0); - }, - 30_000, -); + }); + await using proc = Bun.spawn({ + cmd: [bunExe(), "main.ts"], + env: bunEnv, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect({ stdout: stdout.trim(), stderr }).toEqual({ stdout: "worker got: REAL", stderr: expect.any(String) }); + expect(exitCode).toBe(0); +}, 30_000); test("round-robin worker closes a server.blockList peer silently, like node", async () => { using dir = tempDir("cluster-blocklist", { diff --git a/test/js/node/crypto/crypto.test.ts b/test/js/node/crypto/crypto.test.ts index b164a672cd39..7de1ac0c20c4 100644 --- a/test/js/node/crypto/crypto.test.ts +++ b/test/js/node/crypto/crypto.test.ts @@ -1,7 +1,7 @@ import { CryptoHasher, MD4, MD5, SHA1, SHA224, SHA256, SHA384, SHA512, SHA512_256, gc } from "bun"; import { describe, expect, it } from "bun:test"; import crypto from "crypto"; -import { bunEnv, bunExe, isWindows, tmpdirSync } from "harness"; +import { bunEnv, bunExe, tmpdirSync } from "harness"; import path from "path"; import { hashesFixture } from "./fixtures/sign.fixture.ts"; const HashClasses = [MD5, MD4, SHA1, SHA224, SHA256, SHA384, SHA512, SHA512_256]; @@ -166,7 +166,7 @@ describe("createHash outputLength", () => { ).toBe("5881092d"); }); - it.skipIf(isWindows)("shake128 outputLength >= 2**31 does not abort the process", async () => { + it("shake128 outputLength >= 2**31 does not abort the process", async () => { // options.outputLength is validated against the full uint32 range, so 2**31 // must either produce a digest or throw a catchable error, never terminate // the process. diff --git a/test/js/node/dns/dns-tcp-bidirectional-poll.test.ts b/test/js/node/dns/dns-tcp-bidirectional-poll.test.ts index cfce1c3a8934..3be17ad5fc7a 100644 --- a/test/js/node/dns/dns-tcp-bidirectional-poll.test.ts +++ b/test/js/node/dns/dns-tcp-bidirectional-poll.test.ts @@ -10,10 +10,10 @@ // the whole process, and the busy-loop on EPOLLOUT (pre-fix) would otherwise // starve the test runner. import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows } from "harness"; +import { bunEnv, bunExe } from "harness"; import { join } from "node:path"; -test.skipIf(isWindows)("c-ares TCP DNS fd registers readable+writable on one FilePoll without asserting", async () => { +test("c-ares TCP DNS fd registers readable+writable on one FilePoll without asserting", async () => { await using proc = Bun.spawn({ cmd: [bunExe(), join(import.meta.dir, "dns-tcp-bidirectional-poll-fixture.ts")], env: bunEnv, diff --git a/test/js/node/dns/node-dns.test.js b/test/js/node/dns/node-dns.test.js index 34ffd9486887..6eb1f2a71d66 100644 --- a/test/js/node/dns/node-dns.test.js +++ b/test/js/node/dns/node-dns.test.js @@ -144,7 +144,7 @@ async function withCompressedAnswerServer(type, rdata, fn) { } } -test.skipIf(isWindows)("dns.resolveSrv accepts compressed target in RDATA", async () => { +test("dns.resolveSrv accepts compressed target in RDATA", async () => { // priority=10, weight=50, port=80, target = "srv" + pointer to QNAME. // After decompression the target is srv.. const rdata = ptr => Buffer.concat([Buffer.from([0x00, 0x0a, 0x00, 0x32, 0x00, 0x50, 0x03, 0x73, 0x72, 0x76]), ptr]); @@ -267,7 +267,7 @@ test("dns.resolveCaa (caa.socketify.dev)", () => { return promise; }); -test.skipIf(isWindows)("dns.Resolver#resolveCaa keeps a numeric CAA tag reachable by index", async () => { +test("dns.Resolver#resolveCaa keeps a numeric CAA tag reachable by index", async () => { const socket = dgram.createSocket("udp4"); try { socket.on("message", (query, rinfo) => { diff --git a/test/js/node/fs/cp.test.ts b/test/js/node/fs/cp.test.ts index 036ff3e26658..8be192c5f7d4 100644 --- a/test/js/node/fs/cp.test.ts +++ b/test/js/node/fs/cp.test.ts @@ -1,6 +1,6 @@ import { describe, expect, jest, test } from "bun:test"; import fs from "fs"; -import { bunEnv, bunExe, type DirectoryTree, isLinux, isPosix, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, type DirectoryTree, isLinux, isWindows, tempDir } from "harness"; import { mkfifo } from "mkfifo"; import { isAbsolute, join } from "path"; @@ -600,27 +600,25 @@ describe.skipIf(isWindows).each(["cp", "cpSync"] as const)( // freed the parent while other subtasks were still dereferencing it // (heap-use-after-free under ASAN). // -// POSIX-only: uses a pre-existing directory at the destination path of one +// Uses a pre-existing directory at the destination path of one // file so that its SingleTask fails with EISDIR. This works even when running // as root. On macOS the pre-existing dst/ makes clonefile() fail with EEXIST // and fall through to the per-file SingleTask path being tested. -test.skipIf(!isPosix)( - "fs.promises.cp recursive does not free parent task while subtasks are in flight after an error", - async () => { - const files: DirectoryTree = {}; - // Enough siblings so several SingleTasks are running on the thread pool - // when the failing one errors. - for (let i = 0; i < 32; i++) files[`src/f${i}.txt`] = "x"; - files["src/000-bad.txt"] = "x"; - // The destination for 000-bad.txt is a directory → copying into it fails. - files["dst/000-bad.txt"] = { ".keep": "" }; - using dir = tempDir("cp-uaf", files); - const base = String(dir); - - // Run the copy in a subprocess: before the fix this is a - // heap-use-after-free that ASAN aborts on. The subprocess loops to make - // the race reliable. It must reject with EISDIR each iteration and exit 0. - const script = ` +test("fs.promises.cp recursive does not free parent task while subtasks are in flight after an error", async () => { + const files: DirectoryTree = {}; + // Enough siblings so several SingleTasks are running on the thread pool + // when the failing one errors. + for (let i = 0; i < 32; i++) files[`src/f${i}.txt`] = "x"; + files["src/000-bad.txt"] = "x"; + // The destination for 000-bad.txt is a directory → copying into it fails. + files["dst/000-bad.txt"] = { ".keep": "" }; + using dir = tempDir("cp-uaf", files); + const base = String(dir); + + // Run the copy in a subprocess: before the fix this is a + // heap-use-after-free that ASAN aborts on. The subprocess loops to make + // the race reliable. It must reject with EISDIR each iteration and exit 0. + const script = ` const fs = require("fs"); const path = require("path"); const base = ${JSON.stringify(base)}; @@ -642,18 +640,17 @@ test.skipIf(!isPosix)( console.log("ok"); })(); `; - await using proc = Bun.spawn({ - cmd: [bunExe(), "-e", script], - env: bunEnv, - stdout: "pipe", - stderr: "pipe", - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect(stderr).toBe(""); - expect(stdout.trim()).toBe("ok"); - expect(exitCode).toBe(0); - }, -); + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", script], + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stderr).toBe(""); + expect(stdout.trim()).toBe("ok"); + expect(exitCode).toBe(0); +}); test.skipIf(!isLinux)("fs.cp and fs.copyFile create the destination with the source file's mode", async () => { using dir = tempDir("cp-dest-mode", {}); diff --git a/test/js/node/fs/fs.test.ts b/test/js/node/fs/fs.test.ts index 03ad9f49b4a8..36c6aaff1b3e 100644 --- a/test/js/node/fs/fs.test.ts +++ b/test/js/node/fs/fs.test.ts @@ -4,7 +4,6 @@ import { bunExe, gc, getMaxFD, - isBroken, isDebug, isGlibc, isIntelMacOS, @@ -3499,7 +3498,7 @@ describe("readlink encoding option", () => { // stack PathBuffer in that case. We can't create a >= PATH_MAX target on a // normal filesystem, but we can exercise the longest-possible target to make // sure the bounds check in sys.readlink doesn't fire early. -it.skipIf(isWindows)("readlink with PATH_MAX-1 target", () => { +it("readlink with PATH_MAX-1 target", () => { const dir = tmpdirSync(); // Find the longest target the local filesystem will accept for symlink(2). // On Linux this is 4095, on macOS 1023. Bun's own path validation silently @@ -4483,7 +4482,7 @@ describe("fs.ReadStream", () => { }); describe("createWriteStream", () => { - it.todoIf(isBroken && isWindows)("simple write stream finishes", async () => { + it("simple write stream finishes", async () => { const streamPath = join(tmpdirSync(), "create-write-stream.txt"); const { promise: done, resolve, reject } = Promise.withResolvers(); @@ -5366,8 +5365,7 @@ describe("utimesSync", () => { expect(finalStats.atime).toEqual(prevAccessTime); }); - // TODO: make this work on Windows - it.skipIf(isWindows)("works after 2038", () => { + it("works after 2038", () => { const tmp = join(tmpdir(), "utimesSync-test-file-" + Math.random().toString(36).slice(2)); writeFileSync(tmp, "test"); const prevStats = fs.statSync(tmp); @@ -5933,7 +5931,7 @@ it.skipIf(isWindows)("BigIntStats *Ns fields are negative for pre-epoch timestam expect(st2.atimeNs / 1_000_000n).toBe(st2.atimeMs); }); -it.skipIf(isWindows)("BigIntStats *Ns does not clamp for post-2262 timestamps", () => { +it("BigIntStats *Ns does not clamp for post-2262 timestamps", () => { using dir = tempDir("bigintstats-far-future", { "f.txt": "x" }); const f = join(String(dir), "f.txt"); @@ -6296,39 +6294,35 @@ it("fs.statfsSync should work", () => { // back as 0 and the remaining fields were shifted. Any real filesystem // has a positive block size and at least one block — asserting that here // catches the misaligned-struct case without depending on absolute values. - if (isPosix) { - expect(stats.bsize).toBeGreaterThan(0); - expect(stats.blocks).toBeGreaterThan(0); - } + + expect(stats.bsize).toBeGreaterThan(0); + expect(stats.blocks).toBeGreaterThan(0); const bigIntStats = statfsSync(import.meta.path, { bigint: true }); ["type", "bsize", "blocks", "bfree", "bavail", "files", "ffree"].forEach(k => { expect(bigIntStats).toHaveProperty(k); expect(bigIntStats[k]).toBeTypeOf("bigint"); }); - if (isPosix) { - expect(bigIntStats.bsize > 0n).toBe(true); - expect(bigIntStats.blocks > 0n).toBe(true); - } + + expect(bigIntStats.bsize > 0n).toBe(true); + expect(bigIntStats.blocks > 0n).toBe(true); }); it("fs.promises.statfs should work", async () => { const stats = await fs.promises.statfs(import.meta.path); expect(stats).toBeDefined(); // See "fs.statfsSync should work" above — same regression gate for #31133. - if (isPosix) { - expect(stats.bsize).toBeGreaterThan(0); - expect(stats.blocks).toBeGreaterThan(0); - } + + expect(stats.bsize).toBeGreaterThan(0); + expect(stats.blocks).toBeGreaterThan(0); }); it("fs.promises.statfs should work with bigint", async () => { const stats = await fs.promises.statfs(import.meta.path, { bigint: true }); expect(stats).toBeDefined(); - if (isPosix) { - expect(stats.bsize > 0n).toBe(true); - expect(stats.blocks > 0n).toBe(true); - } + + expect(stats.bsize > 0n).toBe(true); + expect(stats.blocks > 0n).toBe(true); }); it("fs.statfs (callback) should work with bigint", async () => { @@ -6344,10 +6338,9 @@ it("fs.statfs (callback) should work with bigint", async () => { expect(stats[k]).toBeTypeOf("bigint"); } // See "fs.statfsSync should work" above — same regression gate for #31133. - if (isPosix) { - expect(stats.bsize > 0n).toBe(true); - expect(stats.blocks > 0n).toBe(true); - } + + expect(stats.bsize > 0n).toBe(true); + expect(stats.blocks > 0n).toBe(true); }); // Regression for oven-sh/bun#31510: the non-bigint statfs path stored block @@ -7665,7 +7658,7 @@ describe("a process.nextTick queued by an fs callback runs before a microtask it it.each(cases)("%s", async (_name, start) => { using dir = tempDir("fs-callback-order", { "file.txt": "hello", "empty": {} }); - if (!isWindows) symlinkSync(join(String(dir), "file.txt"), join(String(dir), "symlink")); + symlinkSync(join(String(dir), "file.txt"), join(String(dir), "symlink")); await tickQueueExists(); const { promise, resolve } = Promise.withResolvers(); diff --git a/test/js/node/http/node-http-connect.test.ts b/test/js/node/http/node-http-connect.test.ts index aba4e11854e7..0d3ba2eb754e 100644 --- a/test/js/node/http/node-http-connect.test.ts +++ b/test/js/node/http/node-http-connect.test.ts @@ -1231,26 +1231,23 @@ describe("HTTP server CONNECT", () => { }, ); - test.skipIf(isWindows)( - "AF_UNIX CONNECT sockets whose peer closes first do not spin the loop on EPOLLHUP", - async () => { - // An AF_UNIX peer close() on a half-open (CONNECT hand-off) socket is EPOLLHUP, which is level-triggered: - // the loop must stay idle while the server still holds its side, and each socket ends and closes once. - using dir = tempDir("connect-unix-hangup", {}); - const result = await bunRun(join(import.meta.dir, "node-http-connect-unix-hangup-fixture.js"), { - SOCK: join(String(dir), "proxy.sock"), - }); - const perTarget = Object.fromEntries( - Array.from({ length: 8 }, (_, i) => [`peer-${i}:443`, { ends: 1, closes: 1 }]), - ); - expect(result).toEqual({ - stdout: JSON.stringify(perTarget) + "\nidle", - stderr: "", - exitCode: 0, - signalCode: null, - }); - }, - ); + test("AF_UNIX CONNECT sockets whose peer closes first do not spin the loop on EPOLLHUP", async () => { + // An AF_UNIX peer close() on a half-open (CONNECT hand-off) socket is EPOLLHUP, which is level-triggered: + // the loop must stay idle while the server still holds its side, and each socket ends and closes once. + using dir = tempDir("connect-unix-hangup", {}); + const result = await bunRun(join(import.meta.dir, "node-http-connect-unix-hangup-fixture.js"), { + SOCK: join(String(dir), "proxy.sock"), + }); + const perTarget = Object.fromEntries( + Array.from({ length: 8 }, (_, i) => [`peer-${i}:443`, { ends: 1, closes: 1 }]), + ); + expect(result).toEqual({ + stdout: JSON.stringify(perTarget) + "\nidle", + stderr: "", + exitCode: 0, + signalCode: null, + }); + }); }); // A CONNECT that the server parses while an earlier response on the connection is still pending. diff --git a/test/js/node/http/node-http-pinned-write.test.ts b/test/js/node/http/node-http-pinned-write.test.ts index 49efd38b538a..2e632c5c43ae 100644 --- a/test/js/node/http/node-http-pinned-write.test.ts +++ b/test/js/node/http/node-http-pinned-write.test.ts @@ -113,7 +113,7 @@ describe("node:http large Buffer writes are sent zero-copy", () => { // bytes with the GC a second time). transfer() mid-write therefore detaches, // as in Node, but it moves the storage rather than freeing it, so the bytes // still to be written reach the client intact. - test.skipIf(isWindows)("a plain Buffer transferred while its write is pending still arrives intact", async () => { + test("a plain Buffer transferred while its write is pending still arrives intact", async () => { const payload = makePayload(CHUNK_SIZE); const expectedHash = sha1(payload); let detachedWhilePending: boolean | undefined; @@ -358,7 +358,7 @@ describe("node:http large Buffer writes are sent zero-copy", () => { server.close(); `; - test.skipIf(isWindows)("a resizable ArrayBuffer is copied into backpressure, not held by reference", async () => { + test("a resizable ArrayBuffer is copied into backpressure, not held by reference", async () => { // resize() mprotect()s trimmed pages PROT_NONE and pin() doesn't block // it, so a retained raw slice faults on spill / EFAULT-spins on drain. // Run in a child so the crash is observed as exit != 0. @@ -378,7 +378,7 @@ describe("node:http large Buffer writes are sent zero-copy", () => { expect(exitCode).toBe(0); }); - test.skipIf(isWindows)("resizing a resizable ArrayBuffer after write() does not wedge drain", async () => { + test("resizing a resizable ArrayBuffer after write() does not wedge drain", async () => { // Drain path: send() on PROT_NONE pages returns EFAULT -> 0 consumed -> // onWritable re-arms forever. `await using` kills the child on timeout. await using proc = Bun.spawn({ @@ -458,28 +458,23 @@ describe("node:http large Buffer writes are sent zero-copy", () => { server.close(); `; - test.skipIf(isWindows)( - "write() copies a WebAssembly.Memory's bytes, so growing the memory afterwards doesn't corrupt the body", - async () => { - await using proc = Bun.spawn({ - cmd: [bunExe(), "-e", wasmMemoryChild], - // Without fast memory, `grow()` moves the block on every platform. With system malloc, the freed block - // is unmapped. - env: { ...bunEnv, BUN_JSC_useWasmFastMemory: "0", Malloc: "1" }, - stdout: "pipe", - stderr: "pipe", - }); + test("write() copies a WebAssembly.Memory's bytes, so growing the memory afterwards doesn't corrupt the body", async () => { + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", wasmMemoryChild], + // Without fast memory, `grow()` moves the block on every platform. With system malloc, the freed block + // is unmapped. + env: { ...bunEnv, BUN_JSC_useWasmFastMemory: "0", Malloc: "1" }, + stdout: "pipe", + stderr: "pipe", + }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect({ stdout: stdout.trim(), stderr }).toEqual({ - stdout: JSON.stringify({ detachedAfterGrow: true, bodyLength: WASM_PAGES * 64 * 1024, bodyMatches: true }), - stderr: expect.any(String), - }); - expect(exitCode).toBe(0); - }, - // Sending 32 MB on a debug build can exceed the default 5 s timeout. - 30_000, - ); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect({ stdout: stdout.trim(), stderr }).toEqual({ + stdout: JSON.stringify({ detachedAfterGrow: true, bodyLength: WASM_PAGES * 64 * 1024, bodyMatches: true }), + stderr: expect.any(String), + }); + expect(exitCode).toBe(0); + }, 30_000); // Sending 32 MB on a debug build can exceed the default 5 s timeout. test("a second write before drain releases the pin on the first buffer", async () => { let detachedAfterSecondWrite: boolean | undefined; diff --git a/test/js/node/module/esm-registry-concurrent-gc.test.ts b/test/js/node/module/esm-registry-concurrent-gc.test.ts index 3ef1b18de2d6..d43af6763da5 100644 --- a/test/js/node/module/esm-registry-concurrent-gc.test.ts +++ b/test/js/node/module/esm-registry-concurrent-gc.test.ts @@ -15,17 +15,12 @@ // module loader, then asserts the program ran to completion. import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; -// collectContinuously is very slow on Windows in CI and the code path is -// identical across platforms; skip there (same rationale as -// module-children-concurrent-gc.test.ts). -test.skipIf(isWindows)( - "ESM registry removeEntry/clearAll under concurrent GC", - async () => { - const files: Record = { - "dep.mjs": `export const value = 1;\n`, - "entry.cjs": ` +test("ESM registry removeEntry/clearAll under concurrent GC", async () => { + const files: Record = { + "dep.mjs": `export const value = 1;\n`, + "entry.cjs": ` // Pad the registry so the marker spends longer iterating it. const pads = []; for (let i = 0; i < 8; i++) pads.push(import("data:text/javascript,export default " + i)); @@ -44,30 +39,28 @@ test.skipIf(isWindows)( // continuous collector is still marking. }); `, - }; + }; - using dir = tempDir("esm-registry-concurrent-gc", files); + using dir = tempDir("esm-registry-concurrent-gc", files); - await using proc = Bun.spawn({ - cmd: [bunExe(), "entry.cjs"], - env: { - ...bunEnv, - BUN_JSC_collectContinuously: "1", - }, - cwd: String(dir), - stderr: "pipe", - stdout: "pipe", - }); + await using proc = Bun.spawn({ + cmd: [bunExe(), "entry.cjs"], + env: { + ...bunEnv, + BUN_JSC_collectContinuously: "1", + }, + cwd: String(dir), + stderr: "pipe", + stdout: "pipe", + }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - // Assert output before exit code so a failure shows the actual crash - // text. Debug/ASAN builds print a harmless "WARNING: ASAN interferes - // with JSC signal handlers" banner on stderr, so only surface stderr - // when the process failed. - expect(stdout.trim()).toBe("ok 1"); - if (exitCode !== 0) expect(stderr).toBe(""); - expect(exitCode).toBe(0); - }, - 90_000, -); + // Assert output before exit code so a failure shows the actual crash + // text. Debug/ASAN builds print a harmless "WARNING: ASAN interferes + // with JSC signal handlers" banner on stderr, so only surface stderr + // when the process failed. + expect(stdout.trim()).toBe("ok 1"); + if (exitCode !== 0) expect(stderr).toBe(""); + expect(exitCode).toBe(0); +}, 90_000); diff --git a/test/js/node/module/module-children-concurrent-gc.test.ts b/test/js/node/module/module-children-concurrent-gc.test.ts index 868688ab78be..b0835c9a3f0a 100644 --- a/test/js/node/module/module-children-concurrent-gc.test.ts +++ b/test/js/node/module/module-children-concurrent-gc.test.ts @@ -20,23 +20,19 @@ // the program still runs to completion with correct output. import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; -// collectContinuously is very slow on Windows in CI and the code path is -// identical across platforms; skip there (same rationale as issue 29519). -test.skipIf(isWindows)( - "JSCommonJSModule m_children mutation under concurrent GC", - async () => { - const files: Record = { - "c.cjs": `module.exports = 1;\n`, - "p.cjs": ` +test("JSCommonJSModule m_children mutation under concurrent GC", async () => { + const files: Record = { + "c.cjs": `module.exports = 1;\n`, + "p.cjs": ` // ~12 reallocations of this module's m_children backing buffer. for (let i = 0; i < 4000; i++) require("./c.cjs"); // setterChildren: locks, clears the native Vector, stores the JS value. module.children = module.children; module.exports = module.children.length; `, - "entry.cjs": ` + "entry.cjs": ` require("./c.cjs"); const pPath = require.resolve("./p.cjs"); let n = 0; @@ -49,30 +45,28 @@ test.skipIf(isWindows)( if (module.children.length < 2) throw new Error("children missing"); console.log("ok " + n); `, - }; + }; - using dir = tempDir("cjs-children-concurrent-gc", files); + using dir = tempDir("cjs-children-concurrent-gc", files); - await using proc = Bun.spawn({ - cmd: [bunExe(), "entry.cjs"], - env: { - ...bunEnv, - BUN_JSC_collectContinuously: "1", - }, - cwd: String(dir), - stderr: "pipe", - stdout: "pipe", - }); + await using proc = Bun.spawn({ + cmd: [bunExe(), "entry.cjs"], + env: { + ...bunEnv, + BUN_JSC_collectContinuously: "1", + }, + cwd: String(dir), + stderr: "pipe", + stdout: "pipe", + }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - // Assert output before exit code so a failure shows the actual crash - // text. Debug/ASAN builds print a harmless "WARNING: ASAN interferes - // with JSC signal handlers" banner on stderr, so only surface stderr - // when the process failed. - expect(stdout.trim()).toBe("ok 1"); - if (exitCode !== 0) expect(stderr).toBe(""); - expect(exitCode).toBe(0); - }, - 60_000, -); + // Assert output before exit code so a failure shows the actual crash + // text. Debug/ASAN builds print a harmless "WARNING: ASAN interferes + // with JSC signal handlers" banner on stderr, so only surface stderr + // when the process failed. + expect(stdout.trim()).toBe("ok 1"); + if (exitCode !== 0) expect(stderr).toBe(""); + expect(exitCode).toBe(0); +}, 60_000); diff --git a/test/js/node/net/node-net-allowHalfOpen.test.js b/test/js/node/net/node-net-allowHalfOpen.test.js index fed467fcda61..a1529fc95099 100644 --- a/test/js/node/net/node-net-allowHalfOpen.test.js +++ b/test/js/node/net/node-net-allowHalfOpen.test.js @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { bunRun, isWindows, nodeExe, tempDir, tempDirWithFiles } from "harness"; +import { bunRun, nodeExe, tempDir, tempDirWithFiles } from "harness"; import net from "node:net"; import { join } from "node:path"; @@ -117,7 +117,7 @@ test("allowHalfOpen: false should work on client-side", async () => { // A paused allowHalfOpen socket whose AF_UNIX peer writes a tail and closes (EPOLLHUP while paused) must keep // the tail for resume() and then deliver data, end and close once each; the paused window must not spin the loop. -test.skipIf(isWindows)("allowHalfOpen: paused socket whose unix peer closed delivers its tail on resume", async () => { +test("allowHalfOpen: paused socket whose unix peer closed delivers its tail on resume", async () => { using dir = tempDir("net-paused-unix-hangup", {}); const result = await bunRun(join(import.meta.dir, "node-net-paused-unix-hangup-fixture.js"), { SOCK: join(String(dir), "p.sock"), diff --git a/test/js/node/net/node-net.test.ts b/test/js/node/net/node-net.test.ts index 3efd0530162a..8fd77169bd76 100644 --- a/test/js/node/net/node-net.test.ts +++ b/test/js/node/net/node-net.test.ts @@ -1235,19 +1235,17 @@ it.if(isWindows)("should not leak when connect({path}) fails asynchronously whil // On Windows, unix paths route through the named-pipe codepath which reports // failure asynchronously; this test targets the synchronous-failure branch in // Listener.connectInner. -it.skipIf(isWindows)( - "should not leak when connect({path}) fails synchronously on a reused handle", - async () => { - // node:net creates a detached native socket (`_handle`) and passes it as - // `prev` to connectInner. connectInner unconditionally `socket.ref()`s - // before `doConnect`. A nonexistent unix path makes `doConnect` throw - // synchronously while the socket is still `.detached`, so - // `handleConnectError`'s own deref (gated on `!isDetached()`) does not - // fire — the ref taken here must be released by the caller for reused - // sockets too, not only freshly-allocated ones. Without that, every - // failed reconnect leaks one native TCPSocket struct + its connection - // string. - const script = ` +it("should not leak when connect({path}) fails synchronously on a reused handle", async () => { + // node:net creates a detached native socket (`_handle`) and passes it as + // `prev` to connectInner. connectInner unconditionally `socket.ref()`s + // before `doConnect`. A nonexistent unix path makes `doConnect` throw + // synchronously while the socket is still `.detached`, so + // `handleConnectError`'s own deref (gated on `!isDetached()`) does not + // fire — the ref taken here must be released by the caller for reused + // sockets too, not only freshly-allocated ones. Without that, every + // failed reconnect leaks one native TCPSocket struct + its connection + // string. + const script = ` const net = require("node:net"); const { heapStats } = require("bun:jsc"); const path = "/tmp/bun-test-nonexistent-" + process.pid + ".sock"; @@ -1289,23 +1287,21 @@ it.skipIf(isWindows)( const after = pageCount(); console.log(JSON.stringify({ before, after, delta: after - before })); `; - await using proc = Bun.spawn({ - cmd: [bunExe(), "-e", script], - env: { ...bunEnv, BUN_GARBAGE_COLLECTOR_LEVEL: "0" }, - stdout: "pipe", - stderr: "pipe", - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect(stderr).toBe(""); - const { before, after, delta } = JSON.parse(stdout.trim().split("\n").pop()!); - // Without the balancing deref: +25 pages (release) / +163 (debug+ASAN). - // With it the socket delta is 0, but since #34009 JSC shares mimalloc and - // adds up to +14 of heap noise on aarch64/darwin release (build 75589). - expect(delta, `mimalloc page count: ${before} -> ${after}`).toBeLessThan(20); - expect(exitCode).toBe(0); - }, - 60_000, -); + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", script], + env: { ...bunEnv, BUN_GARBAGE_COLLECTOR_LEVEL: "0" }, + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stderr).toBe(""); + const { before, after, delta } = JSON.parse(stdout.trim().split("\n").pop()!); + // Without the balancing deref: +25 pages (release) / +163 (debug+ASAN). + // With it the socket delta is 0, but since #34009 JSC shares mimalloc and + // adds up to +14 of heap noise on aarch64/darwin release (build 75589). + expect(delta, `mimalloc page count: ${before} -> ${after}`).toBeLessThan(20); + expect(exitCode).toBe(0); +}, 60_000); // node only reaches readStart() from _read(), and read(0) never calls _read once // the readable side has ended, so a `readable: false` client leaves the peer's @@ -2907,9 +2903,7 @@ it("onread: a swallowed throw does not drop the rest of the current native read" expect(exitCode).toBe(0); }); -// On Windows the native layer does not report fatal send errors yet (the WSA -// error translation is a follow-up), so the write error never surfaces there. -it.skipIf(isWindows)("a write after the peer reset the connection fails with a write error", async () => { +it("a write after the peer reset the connection fails with a write error", async () => { const { promise, resolve, reject } = Promise.withResolvers(); // resetAndDestroy() sends an RST (not a FIN); allowHalfOpen keeps the client's // writable side open like Node, so the failure must surface from the write @@ -3067,9 +3061,8 @@ describe("a write that fails at once", () => { // libuv's uv__tcp_bind always sets SO_REUSEADDR on Unix, so Node can bind a // client localPort that still has earlier connections in TIME_WAIT. Bun used // to call bind() bare here and fail with EADDRINUSE, which made -// sequential/test-net-localport.js order-dependent in CI. Windows is skipped -// because libuv intentionally does not set SO_REUSEADDR there. -it.skipIf(isWindows)("connect({ localPort }) succeeds when the local port has TIME_WAIT sockets", async () => { +// sequential/test-net-localport.js order-dependent in CI. +it("connect({ localPort }) succeeds when the local port has TIME_WAIT sockets", async () => { // Reserve a port and release it so nothing else is listening on it. const probe = createServer(); await new Promise((resolve, reject) => { @@ -3400,7 +3393,7 @@ describe.skipIf(!isWindows)("connect() error codes on Windows", () => { describe("net.Server.listen({ fd })", () => { // node's createServerHandle only accepts TCP / pipe descriptors and reports anything else as EINVAL; // the raw listen(2) failure for a datagram socket is EOPNOTSUPP. - it.skipIf(isWindows)("reports a datagram descriptor as EINVAL, like node", async () => { + it("reports a datagram descriptor as EINVAL, like node", async () => { await using proc = Bun.spawn({ cmd: [ bunExe(), diff --git a/test/js/node/path/browserify.test.js b/test/js/node/path/browserify.test.js index 9b53caa26cb5..f2c2fa8636f5 100644 --- a/test/js/node/path/browserify.test.js +++ b/test/js/node/path/browserify.test.js @@ -33,9 +33,8 @@ describe("browserify path tests", () => { ]; for (const [input, expected] of fixtures) { expect(path.posix.dirname(input)).toBe(expected); - if (!isWindows) { - expect(path.dirname(input)).toBe(expected); - } + + expect(path.dirname(input)).toBe(expected); } }); it("path.posix.dirname", () => { diff --git a/test/js/node/path/resolve.test.js b/test/js/node/path/resolve.test.js index 289d0c304c3c..be0d5f694c22 100644 --- a/test/js/node/path/resolve.test.js +++ b/test/js/node/path/resolve.test.js @@ -147,26 +147,23 @@ describe("path.resolve", () => { }); }); - test.skipIf(isWindows)( - "win32 drive-relative input resolves when the working directory is the filesystem root", - async () => { - const script = `const p = require("path"); + test("win32 drive-relative input resolves when the working directory is the filesystem root", async () => { + const script = `const p = require("path"); console.log(JSON.stringify([ p.win32.resolve("C:foo"), p.win32.resolve("C:"), ]));`; - await using proc = Bun.spawn({ - cmd: [bunExe(), "-e", script], - env: bunEnv, - cwd: "/", - stdout: "pipe", - stderr: "pipe", - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect(stdout.trim()).toBe(JSON.stringify(["C:\\foo", "C:\\"])); - expect(exitCode).toBe(0); - }, - ); + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", script], + env: bunEnv, + cwd: "/", + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stdout.trim()).toBe(JSON.stringify(["C:\\foo", "C:\\"])); + expect(exitCode).toBe(0); + }); test.skipIf(!isWindows)("win32 drive-relative input on another drive resolves against that drive's root", () => { const drive = process.cwd()[0].toUpperCase() === "Z" ? "Y:" : "Z:"; diff --git a/test/js/node/process/process-execve.test.ts b/test/js/node/process/process-execve.test.ts index d88157896d8b..0b34f88773a4 100644 --- a/test/js/node/process/process-execve.test.ts +++ b/test/js/node/process/process-execve.test.ts @@ -42,7 +42,7 @@ describe.concurrent("process.execve", () => { expect(exitCode).toBe(0); }); - test.skipIf(isWindows)("throws ERR_WORKER_UNSUPPORTED_OPERATION in a worker", async () => { + test("throws ERR_WORKER_UNSUPPORTED_OPERATION in a worker", async () => { using dir = tempDir("process-execve-worker", { "index.js": ` const { Worker, isMainThread, parentPort } = require("worker_threads"); diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js index 781c8c37606b..e5a1fd8bb6cc 100644 --- a/test/js/node/process/process.test.js +++ b/test/js/node/process/process.test.js @@ -40,15 +40,12 @@ it("process", () => { // process.title defaults to argv[0] as invoked, matching Node // (uv_get_process_title semantics), so it is the executable path here. - if (!isNode && process.platform !== "win32" && typeof process.title !== "string") - throw new Error("process.title is not a string"); - if (!isNode && process.platform !== "win32" && process.title.length === 0) throw new Error("process.title is empty"); + if (!isNode && typeof process.title !== "string") throw new Error("process.title is not a string"); + if (!isNode && process.title.length === 0) throw new Error("process.title is empty"); - if (process.platform !== "win32" && typeof process.env.USER !== "string") - throw new Error("process.env is not an object"); + if (typeof process.env.USER !== "string") throw new Error("process.env is not an object"); - if (process.platform !== "win32" && process.env.USER.length === 0) - throw new Error("process.env is missing a USER property"); + if (process.env.USER.length === 0) throw new Error("process.env is missing a USER property"); if (process.platform !== "darwin" && process.platform !== "linux" && process.platform !== "win32") throw new Error("process.platform is invalid"); @@ -1473,8 +1470,7 @@ describe.concurrent(() => { ).toThrow("The property 'prevValue.system' is invalid. Received -1"); }); - // Skipped on Windows because it seems UV returns { user: 15000, system: 0 } constantly - it.skipIf(process.platform === "win32")("works with diff", () => { + it("works with diff", () => { const init = process.cpuUsage(); init.system = 0; init.user = 0; @@ -1483,7 +1479,7 @@ describe.concurrent(() => { expect(delta.system).toBeGreaterThanOrEqual(0); }); - it.skipIf(process.platform === "win32")("works with diff of different structure", () => { + it("works with diff of different structure", () => { const init = { system: 0, user: 0, @@ -2744,7 +2740,7 @@ describe("process.exitCode", () => { ); }); - it.todoIf(isWindows)("zeroExitWithUncaughtHandler", async () => { + it("zeroExitWithUncaughtHandler", async () => { await runInlineFixture( ` process.on('exit', (code) => { @@ -2765,7 +2761,7 @@ describe("process.exitCode", () => { ); }); - it.todoIf(isWindows)("changeCodeInUncaughtHandler", async () => { + it("changeCodeInUncaughtHandler", async () => { await runInlineFixture( ` process.on('exit', (code) => { diff --git a/test/js/node/test/parallel/test-process-exit-code.js b/test/js/node/test/parallel/test-process-exit-code.js index fdf6574c5ca5..63f6322d077a 100644 --- a/test/js/node/test/parallel/test-process-exit-code.js +++ b/test/js/node/test/parallel/test-process-exit-code.js @@ -24,13 +24,6 @@ const common = require('../common'); const assert = require('assert'); const debug = require('util').debuglog('test'); -if (common.isWindows) { - // Bun: at least one of the exit-from-uncaught-exception cases (throw inside - // an uncaughtException handler, or clearing process._fatalException) hangs - // on Windows instead of exiting with the expected code. - common.skip('exit codes from the uncaught-exception path hang on Windows in Bun'); -} - const { getTestCases } = require('../common/process-exit-code-cases'); const testCases = getTestCases(false); diff --git a/test/js/node/test/sequential/test-cluster-inspect-brk.js b/test/js/node/test/sequential/test-cluster-inspect-brk.js index efeb91e59f02..c512a3b0e958 100644 --- a/test/js/node/test/sequential/test-cluster-inspect-brk.js +++ b/test/js/node/test/sequential/test-cluster-inspect-brk.js @@ -1,7 +1,5 @@ 'use strict'; const common = require('../common'); -if (common.isWindows) - common.skip('extra stdio pipes (stdio beyond the IPC channel) are not supported on Windows'); common.skipIfInspectorDisabled(); // A test to ensure that cluster properly interoperates with the diff --git a/test/js/node/timers/node-timers.test.ts b/test/js/node/timers/node-timers.test.ts index e81839dffd63..e7fef4bcdefc 100644 --- a/test/js/node/timers/node-timers.test.ts +++ b/test/js/node/timers/node-timers.test.ts @@ -1,6 +1,6 @@ import jsc from "bun:jsc"; import { describe, expect, it, mock, test } from "bun:test"; -import { bunEnv, bunExe, bunRun, isWindows } from "harness"; +import { bunEnv, bunExe, bunRun } from "harness"; import path from "node:path"; import { clearInterval, clearTimeout, promises, setImmediate, setInterval, setTimeout } from "node:timers"; import { promisify } from "util"; @@ -390,25 +390,20 @@ describe("symbol-keyed methods", () => { }); describe.each(["with", "without"])("setImmediate %s timers running", mode => { - // TODO(@190n) #17901 did not fix this for Windows - it.todoIf(isWindows && mode == "with")( - "has reasonable performance when nested", - async () => { - const process = Bun.spawn({ - cmd: [bunExe(), path.join(__dirname, "setImmediate-fixture.ts"), mode + "-interval"], - stdout: "pipe", - env: bunEnv, - }); + it("has reasonable performance when nested", async () => { + const process = Bun.spawn({ + cmd: [bunExe(), path.join(__dirname, "setImmediate-fixture.ts"), mode + "-interval"], + stdout: "pipe", + env: bunEnv, + }); - await process.exited; - const out = await process.stdout.text(); - expect(process.exitCode).toBe(0); - // if this fails, there will be a nicer error than printing out the entire string - expect((out.match(/\n/g) ?? []).length).toBe(5000); - expect(out).toBe("callback\n".repeat(5000)); - }, - 5000, - ); + await process.exited; + const out = await process.stdout.text(); + expect(process.exitCode).toBe(0); + // if this fails, there will be a nicer error than printing out the entire string + expect((out.match(/\n/g) ?? []).length).toBe(5000); + expect(out).toBe("callback\n".repeat(5000)); + }, 5000); }); it("should defer microtasks when an exception is thrown in an immediate", async () => { diff --git a/test/js/node/tls/node-tls-connect.test.ts b/test/js/node/tls/node-tls-connect.test.ts index 06a4714c615d..7dee3ae6b439 100644 --- a/test/js/node/tls/node-tls-connect.test.ts +++ b/test/js/node/tls/node-tls-connect.test.ts @@ -5556,7 +5556,7 @@ describe("tls.connect() attaches onConnectEnd to 'end' exactly once", () => { }); }); - it.skipIf(process.platform === "win32")("tls.connect({ path })", async () => { + it("tls.connect({ path })", async () => { using dir = tempDir("tls-connect-path", {}); const path = join(String(dir), "tls.sock"); await withTLSServer({ path }, async () => { diff --git a/test/js/node/tls/test-use-system-ca.test.ts b/test/js/node/tls/test-use-system-ca.test.ts index 0ecee7a3dd7d..4df742830a56 100644 --- a/test/js/node/tls/test-use-system-ca.test.ts +++ b/test/js/node/tls/test-use-system-ca.test.ts @@ -318,7 +318,7 @@ describe.concurrent.skipIf(!isLinux)("SSL_CERT_FILE that can be read only once", // The default store also trusts what X509_STORE_set_default_paths would: $SSL_CERT_FILE (else /etc/ssl/cert.pem) and // the $SSL_CERT_DIR (else /etc/ssl/certs) hash directory. The file is read lazily and deduplicated against the bundled // roots, so these pin that the same certificates are still trusted. -describe.skipIf(isWindows)("default store and OpenSSL's default paths", () => { +describe("default store and OpenSSL's default paths", () => { const keys = join(import.meta.dir, "../test/fixtures/keys"); const ca1 = readFileSync(join(keys, "ca1-cert.pem"), "utf8"); diff --git a/test/js/node/tty.test.ts b/test/js/node/tty.test.ts index 74b28d2e09f2..98b7c36bc956 100644 --- a/test/js/node/tty.test.ts +++ b/test/js/node/tty.test.ts @@ -324,7 +324,7 @@ describe("WriteStream.prototype.getColorDepth", () => { }); // Bun recognizes these truecolor terminals on top of Node's list. - it.skipIf(isWindows)("reports 24-bit color for ghostty and WezTerm", () => { + it("reports 24-bit color for ghostty and WezTerm", () => { expect(getColorDepth({ TERM_PROGRAM: "ghostty" })).toBe(24); expect(getColorDepth({ TERM_PROGRAM: "WezTerm" })).toBe(24); }); diff --git a/test/js/node/url/url-pathtofileurl.test.js b/test/js/node/url/url-pathtofileurl.test.js index 561cb3e3b8b9..da12319251c8 100644 --- a/test/js/node/url/url-pathtofileurl.test.js +++ b/test/js/node/url/url-pathtofileurl.test.js @@ -4,8 +4,7 @@ import assert from "node:assert"; import url from "node:url"; describe("url.pathToFileURL", () => { - // TODO: Fix these asserts on Windows. - test.skipIf(isWindows)("dangling slashes and percent sign", () => { + test("dangling slashes and percent sign", () => { { const fileURL = url.pathToFileURL("test/").href; assert.ok(fileURL.startsWith("file:///")); diff --git a/test/js/node/vm/sourcetextmodule-link-gc.test.ts b/test/js/node/vm/sourcetextmodule-link-gc.test.ts index f10bcbfaf512..5eb1ba12d5f8 100644 --- a/test/js/node/vm/sourcetextmodule-link-gc.test.ts +++ b/test/js/node/vm/sourcetextmodule-link-gc.test.ts @@ -12,14 +12,10 @@ // ~9× per link() call. import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; -// collectContinuously is very slow under Windows + ASAN in CI; the code path -// is identical on Linux/macOS, so skip Windows to keep duration reasonable. -test.skipIf(isWindows)( - "vm.SourceTextModule link() m_resolveCache survives concurrent GC", - async () => { - const fixture = ` +test("vm.SourceTextModule link() m_resolveCache survives concurrent GC", async () => { + const fixture = ` const vm = require("node:vm"); const N = 500; @@ -49,27 +45,25 @@ test.skipIf(isWindows)( }); `; - await using proc = Bun.spawn({ - cmd: [bunExe(), "-e", fixture], - env: { - ...bunEnv, - BUN_JSC_collectContinuously: "1", - BUN_JSC_useGenerationalGC: "0", - }, - stderr: "pipe", - stdout: "pipe", - }); + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", fixture], + env: { + ...bunEnv, + BUN_JSC_collectContinuously: "1", + BUN_JSC_useGenerationalGC: "0", + }, + stderr: "pipe", + stdout: "pipe", + }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - // Debug/ASAN builds print a "WARNING: ASAN interferes with JSC signal - // handlers…" banner to stderr at startup; stdout === "ok" + exit 0 is the - // real signal — a mid-rehash visit aborts before either. - expect({ stdout: stdout.trim(), exitCode }).toEqual({ stdout: "ok", exitCode: 0 }); - void stderr; - }, - 120_000, -); + // Debug/ASAN builds print a "WARNING: ASAN interferes with JSC signal + // handlers…" banner to stderr at startup; stdout === "ok" + exit 0 is the + // real signal — a mid-rehash visit aborts before either. + expect({ stdout: stdout.trim(), exitCode }).toEqual({ stdout: "ok", exitCode: 0 }); + void stderr; +}, 120_000); // computeErrorInfoWrapperToString (src/jsc/bindings/FormatStackTraceForJS.cpp, // installed via vm.setOnComputeErrorInfo) runs from ErrorInstance:: @@ -139,31 +133,27 @@ function evaluationErrorFixture(iterations: number) { `; } -test.skipIf(isWindows)( - "vm.SourceTextModule evaluation error survives a concurrent-GC stack-trace finalizer", - async () => { - using dir = tempDir("vm-module-eval-error-gc", { "fixture.mjs": evaluationErrorFixture(60) }); +test("vm.SourceTextModule evaluation error survives a concurrent-GC stack-trace finalizer", async () => { + using dir = tempDir("vm-module-eval-error-gc", { "fixture.mjs": evaluationErrorFixture(60) }); - async function run() { - await using proc = Bun.spawn({ - cmd: [bunExe(), "--smol", "--experimental-vm-modules", "fixture.mjs"], - env: bunEnv, - cwd: String(dir), - stderr: "pipe", - stdout: "pipe", - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - return { stdout: stdout.trim(), stderr, exitCode }; - } + async function run() { + await using proc = Bun.spawn({ + cmd: [bunExe(), "--smol", "--experimental-vm-modules", "fixture.mjs"], + env: bunEnv, + cwd: String(dir), + stderr: "pipe", + stdout: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + return { stdout: stdout.trim(), stderr, exitCode }; + } - for (let i = 0; i < 4; i++) { - const { stdout, stderr, exitCode } = await run(); - expect({ stdout, exitCode }).toEqual({ stdout: "ok", exitCode: 0 }); - void stderr; - } - }, - 120_000, -); + for (let i = 0; i < 4; i++) { + const { stdout, stderr, exitCode } = await run(); + expect({ stdout, exitCode }).toEqual({ stdout: "ok", exitCode: 0 }); + void stderr; + } +}, 120_000); // The finalizer now sets the mutator's pending exception aside with a // SuspendExceptionScope. That scope restores the slot blindly on exit, so a @@ -177,13 +167,11 @@ test.skipIf(isWindows)( // Run the GC-churn fixture inside a Worker and terminate it mid-run, 20 times. // Every run of the build with the suspend scope but without the deferral // asserted; this guards that pairing rather than the original crash (it passes -// on a build with neither). Windows: same reason as above. -test.skipIf(isWindows)( - "terminate() while the stack-trace finalizer runs keeps the exception and trap state in sync", - async () => { - using dir = tempDir("vm-module-eval-error-gc-terminate", { - "fixture.mjs": evaluationErrorFixture(600), - "stress.mjs": ` +// on a build with neither). +test("terminate() while the stack-trace finalizer runs keeps the exception and trap state in sync", async () => { + using dir = tempDir("vm-module-eval-error-gc-terminate", { + "fixture.mjs": evaluationErrorFixture(600), + "stress.mjs": ` import { readFileSync } from "node:fs"; const src = readFileSync(new URL("./fixture.mjs", import.meta.url), "utf8"); const url = URL.createObjectURL(new Blob([src], { type: "text/javascript" })); @@ -199,18 +187,16 @@ test.skipIf(isWindows)( } console.log("ok"); `, - }); + }); - await using proc = Bun.spawn({ - cmd: [bunExe(), "--smol", "--experimental-vm-modules", "stress.mjs"], - env: bunEnv, - cwd: String(dir), - stderr: "pipe", - stdout: "pipe", - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect({ stdout: stdout.trim(), exitCode }).toEqual({ stdout: "ok", exitCode: 0 }); - void stderr; - }, - 120_000, -); + await using proc = Bun.spawn({ + cmd: [bunExe(), "--smol", "--experimental-vm-modules", "stress.mjs"], + env: bunEnv, + cwd: String(dir), + stderr: "pipe", + stdout: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect({ stdout: stdout.trim(), exitCode }).toEqual({ stdout: "ok", exitCode: 0 }); + void stderr; +}, 120_000); diff --git a/test/js/node/watch/fs.watch.rewrite.test.ts b/test/js/node/watch/fs.watch.rewrite.test.ts index 798babbd7c8c..058c6d3f9b33 100644 --- a/test/js/node/watch/fs.watch.rewrite.test.ts +++ b/test/js/node/watch/fs.watch.rewrite.test.ts @@ -7,7 +7,7 @@ // and dedupes by (realpath, recursive). These tests pin behaviour the old design // couldn't provide. import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isFreeBSD, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, isFreeBSD, tempDir } from "harness"; import fs from "node:fs"; import path from "node:path"; @@ -172,7 +172,7 @@ test.skipIf(isFreeBSD)("closing an inner watch does not break an overlapping rec // must work even on a completely cold VM that never touched the transpiler. Run a // child process that does nothing but fs.watch to prove there's no hidden ordering // dependency on the module-graph watcher. -test.skipIf(isWindows)("fs.watch works without any module-graph watcher state", async () => { +test("fs.watch works without any module-graph watcher state", async () => { using dir = tempDir("fs-watch-cold-vm", { "watched.txt": "init", "main.js": ` diff --git a/test/js/node/worker_threads/worker_heap_snapshot_gc.test.ts b/test/js/node/worker_threads/worker_heap_snapshot_gc.test.ts index de939901550d..f7ee4a89d69e 100644 --- a/test/js/node/worker_threads/worker_heap_snapshot_gc.test.ts +++ b/test/js/node/worker_threads/worker_heap_snapshot_gc.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isASAN, isDebug, isIntelMacOS, isWindows } from "harness"; +import { bunEnv, bunExe, isASAN, isDebug, isIntelMacOS } from "harness"; import { join } from "node:path"; // The getHeapSnapshot() round-trip must never let the worker thread touch @@ -15,11 +15,10 @@ import { join } from "node:path"; // builds are several times slower per heap snapshot, so they get a reduced // workload as a functional check — plain release CI is where this guards // against regressions. -// Skipped on Windows and Intel (x64) macOS: this branch's always-on per-worker -// stdio path adds per-spawn overhead that a 15x300-snapshot stress exceeds on -// those builders. The race it guards is platform-agnostic and still covered on -// Linux and Apple-Silicon macOS. -test.skipIf(isWindows || isIntelMacOS)( +// Skipped on Intel (x64) macOS: this branch's always-on per-worker stdio path +// adds per-spawn overhead that a 15x300-snapshot stress exceeds on those +// builders. The race it guards is platform-agnostic and still covered elsewhere. +test.skipIf(isIntelMacOS)( "worker.getHeapSnapshot() does not race the parent VM's Strong Handles list under GC", async () => { const slow = isDebug || isASAN; diff --git a/test/js/sql/adapter-env-var-precedence.test.ts b/test/js/sql/adapter-env-var-precedence.test.ts index adf1fd4561f6..4aea768462b2 100644 --- a/test/js/sql/adapter-env-var-precedence.test.ts +++ b/test/js/sql/adapter-env-var-precedence.test.ts @@ -1544,7 +1544,7 @@ describe("SQL adapter environment variable precedence", () => { }, ); - test.skipIf(isWindows)("postgres: a directory gets the name of the socket file, with the resolved port", () => { + test("postgres: a directory gets the name of the socket file, with the resolved port", () => { using dir = tempDir("sql-pg-socket-dir", { "custom.sock": "" }); expect(new SQL({ path: String(dir) }).options.path).toBe(`${dir}/.s.PGSQL.5432`); expect(new SQL({ path: String(dir), port: 5433 }).options.path).toBe(`${dir}/.s.PGSQL.5433`); diff --git a/test/js/sql/postgres-auth.test.ts b/test/js/sql/postgres-auth.test.ts index 003451b16b99..abd14ecd7651 100644 --- a/test/js/sql/postgres-auth.test.ts +++ b/test/js/sql/postgres-auth.test.ts @@ -5,7 +5,7 @@ import { SQL } from "bun"; import { describe, expect, test } from "bun:test"; -import { describeWithContainer, tls as harnessTls, isWindows, tempDir } from "harness"; +import { describeWithContainer, tls as harnessTls, tempDir } from "harness"; import { once } from "node:events"; import fs from "node:fs"; import net from "node:net"; @@ -600,7 +600,7 @@ describe.concurrent("scripted server", () => { }); }); - test.skipIf(isWindows)("require, a unix socket: refused, and nothing is sent", async () => { + test("require, a unix socket: refused, and nothing is sent", async () => { using dir = tempDir("postgres-auth", {}); const socketPath = path.join(String(dir), "s"); const answer = Promise.withResolvers(); diff --git a/test/js/sql/postgres-connection.test.ts b/test/js/sql/postgres-connection.test.ts index 684d4d14cc9c..7480ff6b32b2 100644 --- a/test/js/sql/postgres-connection.test.ts +++ b/test/js/sql/postgres-connection.test.ts @@ -10,7 +10,6 @@ import { expiredTls, isDebug, isLinux, - isWindows, tls as localhostTls, tempDir, } from "harness"; @@ -234,7 +233,7 @@ test("a FATAL error for the query that runs closes the connection at once", asyn } }); -test.skipIf(isWindows)("a unix socket that is not there rejects with the code of a refused connection", async () => { +test("a unix socket that is not there rejects with the code of a refused connection", async () => { using dir = tempDir("postgres-no-socket", {}); const socket = path.join(String(dir), "postgres.sock"); await using sql = new SQL({ adapter: "postgres", path: socket, username: "u", database: "db", max: 1 }); @@ -692,7 +691,7 @@ describe.concurrent("cancel()", () => { const stop = (server: Server) => server.reply(pgErrorResponse(canceled), pgReadyForQuery()); const finish = (server: Server) => server.reply(...described(0), ...bound("done")); - test.skipIf(isWindows)("reaches a server on a unix socket", async () => { + test("reaches a server on a unix socket", async () => { using dir = tempDir("postgres-cancel", {}); const socket = path.join(String(dir), "postgres.sock"); await using server = await cancelServer({ socket }); diff --git a/test/js/sql/sql-mysql-connection.test.ts b/test/js/sql/sql-mysql-connection.test.ts index b28f26b87ceb..e344efe614ed 100644 --- a/test/js/sql/sql-mysql-connection.test.ts +++ b/test/js/sql/sql-mysql-connection.test.ts @@ -1,6 +1,6 @@ import { randomUUIDv7, SQL } from "bun"; import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, describeWithContainer, isLinux, isWindows } from "harness"; +import { bunEnv, bunExe, describeWithContainer, isLinux } from "harness"; import { once } from "node:events"; import net from "node:net"; import { @@ -1088,7 +1088,7 @@ describe("scripted server", () => { } }); - test.skipIf(isWindows)("a unix socket that is not there rejects with its path", async () => { + test("a unix socket that is not there rejects with its path", async () => { const path = "/nonexistent/dir/mysql.sock"; await using sql = new SQL({ adapter: "mysql", path, username: "root", database: "db", max: 1 }); const err: any = await sql`SELECT 1`.catch(err => err); diff --git a/test/js/sql/sql-mysql.auth.test.ts b/test/js/sql/sql-mysql.auth.test.ts index 703ad3267193..2a5531cb3453 100644 --- a/test/js/sql/sql-mysql.auth.test.ts +++ b/test/js/sql/sql-mysql.auth.test.ts @@ -1,6 +1,6 @@ import { randomUUIDv7, SQL } from "bun"; import { expect, test } from "bun:test"; -import { describeWithContainer, isWindows, tempDir } from "harness"; +import { describeWithContainer, tempDir } from "harness"; import { constants, createHash, generateKeyPairSync, privateDecrypt } from "node:crypto"; import { once } from "node:events"; import net from "node:net"; @@ -353,7 +353,7 @@ function authScript(name: keyof typeof scripts, packets: string[]) { } const longPassword = "a-password-of-more-than-20-bytes"; -test.skipIf(isWindows).each([ +test.each([ // [plugin, allowPublicKeyRetrieval, outcome, auth packets] ["caching_sha2_password", false, "ok", [`clear ${JSON.stringify(longPassword + "\0")}`]], // The server's sha256_password takes nothing but TLS for a secure transport. diff --git a/test/js/sql/sql-mysql.test.ts b/test/js/sql/sql-mysql.test.ts index ce94c7d7cdd6..faf1d9e17d1d 100644 --- a/test/js/sql/sql-mysql.test.ts +++ b/test/js/sql/sql-mysql.test.ts @@ -1,6 +1,6 @@ import { SQL, randomUUIDv7 } from "bun"; import { beforeAll, describe, expect, mock, test } from "bun:test"; -import { bunEnv, bunExe, bunRun, describeWithContainer, isWindows, tempDirWithFiles } from "harness"; +import { bunEnv, bunExe, bunRun, describeWithContainer, tempDirWithFiles } from "harness"; import path from "path"; import { UnixDomainSocketProxy } from "../../unix-domain-socket-proxy.ts"; import { @@ -275,7 +275,7 @@ console.log(JSON.stringify({ outcome, closed }));`, }, ); - test.skipIf(isWindows || image.image === "mysql_tls")( + test.skipIf(image.image === "mysql_tls")( "connects through the unix socket that `path`, `socket` or the URL names", async () => { await container.ready; diff --git a/test/js/sql/sql-options.test.ts b/test/js/sql/sql-options.test.ts index 25b06beeb08d..5371471e678c 100644 --- a/test/js/sql/sql-options.test.ts +++ b/test/js/sql/sql-options.test.ts @@ -833,7 +833,7 @@ console.log(JSON.stringify(results));`, expect(exitCode).toBe(0); }); -test.skipIf(isWindows).concurrent.each(["postgres", "mysql"] as const)( +test.concurrent.each(["postgres", "mysql"] as const)( "%s: a unix socket that is not there fails the connection, and TCP is not dialed", async adapter => { let tcpConnections = 0; diff --git a/test/js/third_party/next-auth/next-auth.test.ts b/test/js/third_party/next-auth/next-auth.test.ts index 7c589ef1c426..bc20afac6669 100644 --- a/test/js/third_party/next-auth/next-auth.test.ts +++ b/test/js/third_party/next-auth/next-auth.test.ts @@ -1,44 +1,39 @@ import { describe, expect, it } from "bun:test"; import { cpSync } from "fs"; -import { bunEnv, bunRun, isCI, isWindows, runBunInstall, tmpdirSync } from "harness"; +import { bunEnv, bunRun, runBunInstall, tmpdirSync } from "harness"; import { join } from "path"; describe("next-auth", () => { - // This test OOMs on Windows. - it.todoIf(isCI && isWindows)( - "should be able to call server action multiple times using auth middleware #18977", - async () => { - const testDir = tmpdirSync("next-auth-" + Date.now()); + it("should be able to call server action multiple times using auth middleware #18977", async () => { + const testDir = tmpdirSync("next-auth-" + Date.now()); - cpSync(join(import.meta.dir, "fixture"), testDir, { - recursive: true, - force: true, - filter: src => { - if (src.includes("node_modules")) { - return false; - } - if (src.startsWith(".next")) { - return false; - } - return true; - }, - }); + cpSync(join(import.meta.dir, "fixture"), testDir, { + recursive: true, + force: true, + filter: src => { + if (src.includes("node_modules")) { + return false; + } + if (src.startsWith(".next")) { + return false; + } + return true; + }, + }); - console.log("running bun install"); - await runBunInstall(bunEnv, testDir, { savesLockfile: false }); + console.log("running bun install"); + await runBunInstall(bunEnv, testDir, { savesLockfile: false }); - console.log("starting server"); - const result = await bunRun(join(testDir, "server.js"), { - AUTH_SECRET: "I7Jiq12TSMlPlAzyVAT+HxYX7OQb/TTqIbfTTpr1rg8=", - }); + console.log("starting server"); + const result = await bunRun(join(testDir, "server.js"), { + AUTH_SECRET: "I7Jiq12TSMlPlAzyVAT+HxYX7OQb/TTqIbfTTpr1rg8=", + }); - console.log(result.stdout); - console.log(result.stderr); - expect(result.stderr).toBe(""); - expect(result.stdout).toBeDefined(); - const lines = result.stdout?.split("\n") ?? []; - expect(lines[lines.length - 1]).toMatch(/request sent/); - expect(result.exitCode).toBe(0); - }, - 90_000, - ); + console.log(result.stdout); + console.log(result.stderr); + expect(result.stderr).toBe(""); + expect(result.stdout).toBeDefined(); + const lines = result.stdout?.split("\n") ?? []; + expect(lines[lines.length - 1]).toMatch(/request sent/); + expect(result.exitCode).toBe(0); + }, 90_000); }); diff --git a/test/js/web/abort/abort-controller-gc-reason.test.ts b/test/js/web/abort/abort-controller-gc-reason.test.ts index 144ce6a2abe4..85c4858bbe37 100644 --- a/test/js/web/abort/abort-controller-gc-reason.test.ts +++ b/test/js/web/abort/abort-controller-gc-reason.test.ts @@ -1,23 +1,19 @@ import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows } from "harness"; +import { bunEnv, bunExe } from "harness"; // https://bugs.webkit.org/show_bug.cgi?id=293319 // AbortController.signal.reason is lost after garbage collection describe("AbortController GC", () => { // https://bugs.webkit.org/show_bug.cgi?id=236353 - // verifyGC + collectContinuously is prohibitively slow under Windows CI and - // the code path under test is platform-independent. - test.skipIf(isWindows)( - "reason is marked across concurrent GC (write barrier / output constraint)", - async () => { - // verifyGC asserts on reachable cells the concurrent collector missed. - // Before the fix, abort() after the controller was scanned stored the - // reason with no write barrier / output constraint, leaving it unmarked. - await using proc = Bun.spawn({ - cmd: [ - bunExe(), - "-e", - ` + test("reason is marked across concurrent GC (write barrier / output constraint)", async () => { + // verifyGC asserts on reachable cells the concurrent collector missed. + // Before the fix, abort() after the controller was scanned stored the + // reason with no write barrier / output constraint, leaving it unmarked. + await using proc = Bun.spawn({ + cmd: [ + bunExe(), + "-e", + ` const controllers = []; for (let iter = 0; iter < 500; iter++) { const c = new AbortController(); @@ -32,32 +28,30 @@ describe("AbortController GC", () => { } console.log("PASS lost=" + lost); `, - ], - env: { - ...bunEnv, - BUN_JSC_verifyGC: "1", - BUN_JSC_collectContinuously: "1", - // `bun -e` sets numberOfGCMarkers=1 for one-shot startup, which - // serializes marking and hides the race. Restore parallel marking. - BUN_JSC_numberOfGCMarkers: "8", - }, - stdout: "pipe", - stderr: "pipe", - }); + ], + env: { + ...bunEnv, + BUN_JSC_verifyGC: "1", + BUN_JSC_collectContinuously: "1", + // `bun -e` sets numberOfGCMarkers=1 for one-shot startup, which + // serializes marking and hides the race. Restore parallel marking. + BUN_JSC_numberOfGCMarkers: "8", + }, + stdout: "pipe", + stderr: "pipe", + }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - // On failure verifyGC prints "GC Verifier: ERROR cell ... was not marked" - // to stderr and aborts before stdout is reached. stderr is included for - // diagnostics only; debug/ASAN builds may emit benign warnings there. - expect({ stdout: stdout.trim(), exitCode, stderr }).toEqual({ - stdout: "PASS lost=0", - exitCode: 0, - stderr: expect.not.stringContaining("was not marked"), - }); - }, - 120_000, - ); + // On failure verifyGC prints "GC Verifier: ERROR cell ... was not marked" + // to stderr and aborts before stdout is reached. stderr is included for + // diagnostics only; debug/ASAN builds may emit benign warnings there. + expect({ stdout: stdout.trim(), exitCode, stderr }).toEqual({ + stdout: "PASS lost=0", + exitCode: 0, + stderr: expect.not.stringContaining("was not marked"), + }); + }, 120_000); test("signal.reason survives GC when only controller is retained", async () => { await using proc = Bun.spawn({ @@ -152,20 +146,16 @@ describe("AbortController GC", () => { }); }); - // collectContinuously is very slow on Windows CI; the code under test is - // identical across platforms. - test.skipIf(isWindows)( - "AbortSignal.abort()'s default reason survives GC for fetch / throwIfAborted / any", - async () => { - // The default DOMException for AbortSignal.abort() was stored in - // m_reason (a JSC::Weak) before the JSAbortSignal wrapper existed, so a - // GC during toJSNewlyCreated()'s allocation could collect it and fetch - // would reject with `null`. collectContinuously makes that GC likely. - await using proc = Bun.spawn({ - cmd: [ - bunExe(), - "-e", - ` + test("AbortSignal.abort()'s default reason survives GC for fetch / throwIfAborted / any", async () => { + // The default DOMException for AbortSignal.abort() was stored in + // m_reason (a JSC::Weak) before the JSAbortSignal wrapper existed, so a + // GC during toJSNewlyCreated()'s allocation could collect it and fetch + // would reject with `null`. collectContinuously makes that GC likely. + await using proc = Bun.spawn({ + cmd: [ + bunExe(), + "-e", + ` const bad = []; for (let i = 0; i < 10000; i++) { const sig = AbortSignal.abort(); @@ -187,17 +177,15 @@ describe("AbortController GC", () => { } console.log(bad.length ? "bad=" + bad.length + " first=" + bad[0] : "bad=0"); `, - ], - env: { ...bunEnv, BUN_JSC_collectContinuously: "1" }, - stderr: "pipe", - stdout: "pipe", - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect({ stdout: stdout.trim(), stderr }).toEqual({ stdout: "bad=0", stderr: "" }); - expect(exitCode).toBe(0); - }, - 60_000, - ); + ], + env: { ...bunEnv, BUN_JSC_collectContinuously: "1" }, + stderr: "pipe", + stdout: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect({ stdout: stdout.trim(), stderr }).toEqual({ stdout: "bad=0", stderr: "" }); + expect(exitCode).toBe(0); + }, 60_000); test("signal.reason survives GC with many controllers", async () => { await using proc = Bun.spawn({ diff --git a/test/js/web/broadcastchannel/message-event-init-gc.test.ts b/test/js/web/broadcastchannel/message-event-init-gc.test.ts index 827484289d59..4d8e82068ef4 100644 --- a/test/js/web/broadcastchannel/message-event-init-gc.test.ts +++ b/test/js/web/broadcastchannel/message-event-init-gc.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isASAN, isDebug, isWindows } from "harness"; +import { bunEnv, bunExe, isASAN, isDebug } from "harness"; // Regression test for a data race in MessageEvent between the mutator thread // (initMessageEvent() reassigning m_data) and the GC marker thread @@ -17,10 +17,7 @@ import { bunEnv, bunExe, isASAN, isDebug, isWindows } from "harness"; // accumulate every received event in `all` so each GC cycle has progressively // more memoryCost() visits, and each round supplies a fresh batch whose // initMessageEvent() call performs the racy variant reassignment. -// -// collectContinuously is prohibitively slow on Windows CI; the m_data race is -// platform-agnostic C++ so POSIX coverage is sufficient. -test.skipIf(isWindows)( +test( "MessageEvent.initMessageEvent does not race GC visitor on m_data", async () => { const script = /* js */ ` diff --git a/test/js/web/fetch/body-clone.test.ts b/test/js/web/fetch/body-clone.test.ts index d76197199c15..b7a42451f360 100644 --- a/test/js/web/fetch/body-clone.test.ts +++ b/test/js/web/fetch/body-clone.test.ts @@ -1336,7 +1336,7 @@ describe("clone() of a body over an unread native stream keeps the Blob behind i }).toEqual({ fresh: ["EISDIR", "EISDIR"], sizeRead: ["EISDIR", "EISDIR"] }); }); - test.skipIf(isWindows)("a directory by file descriptor", async () => { + test("a directory by file descriptor", async () => { const fd = openSync(directory(), "r"); try { expect({ @@ -1348,7 +1348,7 @@ describe("clone() of a body over an unread native stream keeps the Blob behind i } }); - test.skipIf(isWindows)("a file descriptor that is not open", async () => { + test("a file descriptor that is not open", async () => { expect(await codes(Bun.file(1_000_000))).toEqual(["EBADF", "EBADF"]); }); }); diff --git a/test/js/web/fetch/fetch-backpressure.test.ts b/test/js/web/fetch/fetch-backpressure.test.ts index 023a29e10c30..769a142b4e96 100644 --- a/test/js/web/fetch/fetch-backpressure.test.ts +++ b/test/js/web/fetch/fetch-backpressure.test.ts @@ -2,7 +2,7 @@ // HTTP thread from buffering the entire response in memory. import { S3Client } from "bun"; import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, forEachLine, isASAN, isDebug, isWindows, tempDir, tls } from "harness"; +import { bunEnv, bunExe, forEachLine, isASAN, isDebug, tempDir, tls } from "harness"; import { once } from "node:events"; import { statSync } from "node:fs"; import { stat } from "node:fs/promises"; @@ -354,7 +354,7 @@ const STALL_NO_CONSUMER = /* js */ ` for (const kind of ["h1", "h1-chunked", "h1-gzip", "h1-tls", "h2", "h3"] as Kind[]) { describe.concurrent(`fetch() ${kind} receive backpressure`, () => { - const skip = kind === "h3" && isWindows; + const skip = false; const scripts = kind === "h1-gzip" diff --git a/test/js/web/fetch/fetch-compress.test.ts b/test/js/web/fetch/fetch-compress.test.ts index 2fcb6132d80c..fd1129ca3836 100644 --- a/test/js/web/fetch/fetch-compress.test.ts +++ b/test/js/web/fetch/fetch-compress.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { isWindows, tempDir } from "harness"; +import { tempDir } from "harness"; import { join } from "node:path"; import { brotliDecompressSync, gunzipSync, inflateSync, zstdDecompressSync } from "node:zlib"; @@ -271,7 +271,7 @@ describe("fetch compress option", () => { // A Bun.file() body large enough to qualify for the sendfile fast path // (≥32 KiB, plain http, no proxy, non-Windows) must still be compressed // when compress is explicitly set — the sendfile heuristic must not win. - test.skipIf(isWindows)("Bun.file() body large enough for sendfile is still compressed", async () => { + test("Bun.file() body large enough for sendfile is still compressed", async () => { using server = makeServer(); const big = Buffer.alloc(64 * 1024, "abcdefghij").toString(); using dir = tempDir("fetch-compress-sendfile", { "body.txt": big }); diff --git a/test/js/web/fetch/fetch-keepalive.test.ts b/test/js/web/fetch/fetch-keepalive.test.ts index 78574c1237b8..e18f50cded3b 100644 --- a/test/js/web/fetch/fetch-keepalive.test.ts +++ b/test/js/web/fetch/fetch-keepalive.test.ts @@ -836,7 +836,7 @@ test.concurrent.each( expect({ result, exitCode }).toEqual({ result: { misattributed: [] }, exitCode: 0 }); }); -test.skipIf(isWindows)("a full keep-alive pool evicts the longest-idle connection", async () => { +test("a full keep-alive pool evicts the longest-idle connection", async () => { function makeServer() { let connections = 0; const srv = createServer(sock => { diff --git a/test/js/web/fetch/fetch-response-finalizer-sweep.test.ts b/test/js/web/fetch/fetch-response-finalizer-sweep.test.ts index 6ba1c1eb3ce9..6cd728a29945 100644 --- a/test/js/web/fetch/fetch-response-finalizer-sweep.test.ts +++ b/test/js/web/fetch/fetch-response-finalizer-sweep.test.ts @@ -13,7 +13,7 @@ // Response unconsumed, force a synchronous sweep. import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows } from "harness"; +import { bunEnv, bunExe } from "harness"; const fixture = /* js */ ` const net = require("node:net"); @@ -83,9 +83,7 @@ const fixture = /* js */ ` console.log("ok"); `; -// collectContinuously is slow under Windows CI and the code path is identical -// across platforms; the assertion this guards is platform-independent. -describe.skipIf(isWindows)("fetch Response Weak finalizer during GC sweep", () => { +describe("fetch Response Weak finalizer during GC sweep", () => { test("dropping an unconsumed streaming-upload Response does not touch JSCells in the finalizer", async () => { await using proc = Bun.spawn({ cmd: [bunExe(), "-e", fixture], diff --git a/test/js/web/fetch/fetch-session.test.ts b/test/js/web/fetch/fetch-session.test.ts index a547a192e5a0..f97eaf10935b 100644 --- a/test/js/web/fetch/fetch-session.test.ts +++ b/test/js/web/fetch/fetch-session.test.ts @@ -291,7 +291,7 @@ describe("Bun.FetchSession", () => { expect(await outcome({ session: trusting, tls: { serverName: "localhost" } })).toBe("DEPTH_ZERO_SELF_SIGNED_CERT"); }); - test.skipIf(isWindows)("unix comes from the session", async () => { + test("unix comes from the session", async () => { using dir = tempDir("fetch-session-unix", {}); const path = join(String(dir), "session.sock"); using server = Bun.serve({ unix: path, fetch: req => new Response(new URL(req.url).pathname) }); @@ -299,7 +299,7 @@ describe("Bun.FetchSession", () => { expect(await (await fetch("http://anything.invalid/over-unix", { session })).text()).toBe("/over-unix"); }); - test.skipIf(isWindows)("a request's unix or proxy replaces the other one named by the session", async () => { + test("a request's unix or proxy replaces the other one named by the session", async () => { using dir = tempDir("fetch-session-unix-proxy", {}); const path = join(String(dir), "session.sock"); using overUnix = Bun.serve({ unix: path, fetch: req => new Response("unix " + req.url) }); diff --git a/test/js/web/fetch/fetch.unix.test.ts b/test/js/web/fetch/fetch.unix.test.ts index 65d61963d37a..1b92b0d5bab4 100644 --- a/test/js/web/fetch/fetch.unix.test.ts +++ b/test/js/web/fetch/fetch.unix.test.ts @@ -219,7 +219,7 @@ it("works with node:http", async () => { await Promise.all(promises); }); -it.skipIf(isWindows)("reuses the connection (keep-alive)", async () => { +it("reuses the connection (keep-alive)", async () => { function makeServer() { let connections = 0; const heads: string[] = []; @@ -279,7 +279,7 @@ it.skipIf(isWindows)("reuses the connection (keep-alive)", async () => { } }); -it.skipIf(isWindows)("a unix request never consults the proxy environment", async () => { +it("a unix request never consults the proxy environment", async () => { using dir = tempDir("fetch-unix-proxy-env", {}); const sockPath = join(String(dir), "proxy-env.sock"); const heads: string[] = []; @@ -323,7 +323,7 @@ it.skipIf(isWindows)("a unix request never consults the proxy environment", asyn } }); -it.skipIf(isWindows)("keep-alive pool is keyed by socket path", async () => { +it("keep-alive pool is keyed by socket path", async () => { function makeServer(name: string) { let connections = 0; const srv = createServer(sock => { @@ -414,7 +414,7 @@ it.skipIf(isWindows)("a relative socket path is resolved against the cwd at fetc expect(exitCode).toBe(0); }); -it.skipIf(isWindows)("TLS over a unix socket is only reused for the hostname the handshake verified", async () => { +it("TLS over a unix socket is only reused for the hostname the handshake verified", async () => { using dir = tempDir("fetch-unix-tls", { "ca.pem": tls.cert }); // The harness cert is valid for "localhost". A pooled connection verified // for it must not serve a request to a different hostname over the same @@ -475,7 +475,7 @@ it.skipIf(isWindows)("TLS over a unix socket is only reused for the hostname the expect(exitCode).toBe(0); }); -it.skipIf(isWindows)("TLS over a unix socket honors tls.ca", async () => { +it("TLS over a unix socket honors tls.ca", async () => { using dir = tempDir("fetch-unix-tls-ca", {}); const unix = join(String(dir), "tls.sock"); let connections = 0; @@ -505,7 +505,7 @@ it.skipIf(isWindows)("TLS over a unix socket honors tls.ca", async () => { } }); -it.skipIf(isWindows)("unix keep-alive entries are not evicted by TCP pool pressure", async () => { +it("unix keep-alive entries are not evicted by TCP pool pressure", async () => { function makeServer() { let connections = 0; const srv = createServer(sock => { diff --git a/test/js/web/streams/streams-leak.test.ts b/test/js/web/streams/streams-leak.test.ts index 2f9f2d81391f..09b6fa9c54bf 100644 --- a/test/js/web/streams/streams-leak.test.ts +++ b/test/js/web/streams/streams-leak.test.ts @@ -114,61 +114,58 @@ test.skipIf(isWindows)("abandoned Bun.file().stream() reader does not leak its f const BYTES_TO_WRITE = 500_000; // https://github.com/oven-sh/bun/issues/12198 -test.skipIf(isWindows)( - "Absolute memory usage remains relatively constant when reading and writing to a pipe", - async () => { - async function write(bytes: number) { - const buf = Buffer.alloc(bytes, "foo"); - await cat.stdin.write(buf); - } - async function read(bytes: number) { - let i = 0; - while (true) { - const { value } = await r.read(); - i += value?.length ?? 0; - if (i >= bytes) { - return; - } +test("Absolute memory usage remains relatively constant when reading and writing to a pipe", async () => { + async function write(bytes: number) { + const buf = Buffer.alloc(bytes, "foo"); + await cat.stdin.write(buf); + } + async function read(bytes: number) { + let i = 0; + while (true) { + const { value } = await r.read(); + i += value?.length ?? 0; + if (i >= bytes) { + return; } } + } - async function readAndWrite(bytes = BYTES_TO_WRITE) { - await Promise.all([write(bytes), read(bytes)]); - } + async function readAndWrite(bytes = BYTES_TO_WRITE) { + await Promise.all([write(bytes), read(bytes)]); + } - await using cat = Bun.spawn(["cat"], { - stdin: "pipe", - stdout: "pipe", - stderr: "inherit", - }); - const r = cat.stdout.getReader() as any; + await using cat = Bun.spawn(["cat"], { + stdin: "pipe", + stdout: "pipe", + stderr: "inherit", + }); + const r = cat.stdout.getReader() as any; - const rounds = 5000; + const rounds = 5000; - for (let i = 0; i < rounds; i++) { - await readAndWrite(BYTES_TO_WRITE); - } - Bun.gc(true); - const before = rss(); + for (let i = 0; i < rounds; i++) { + await readAndWrite(BYTES_TO_WRITE); + } + Bun.gc(true); + const before = rss(); - for (let i = 0; i < rounds; i++) { - await readAndWrite(); - } - Bun.gc(true); - const after = rss(); + for (let i = 0; i < rounds; i++) { + await readAndWrite(); + } + Bun.gc(true); + const after = rss(); - for (let i = 0; i < rounds; i++) { - await readAndWrite(); - } - Bun.gc(true); - const after2 = rss(); - console.log({ after, after2 }); - console.log(require("bun:jsc").heapStats()); - console.log("RSS delta", ((after - before) | 0) / 1024 / 1024); - console.log("RSS total", (after / 1024 / 1024) | 0, "MB"); - // ASAN's quarantine + shadow memory raise the absolute RSS floor and slow - // recycling of freed allocations; widen both bounds under bun-asan. - expect(after).toBeLessThan((isASAN ? 700 : 250) * 1024 * 1024); - expect(after).toBeLessThan(before * (isASAN ? 3 : 1.5)); - }, -); + for (let i = 0; i < rounds; i++) { + await readAndWrite(); + } + Bun.gc(true); + const after2 = rss(); + console.log({ after, after2 }); + console.log(require("bun:jsc").heapStats()); + console.log("RSS delta", ((after - before) | 0) / 1024 / 1024); + console.log("RSS total", (after / 1024 / 1024) | 0, "MB"); + // ASAN's quarantine + shadow memory raise the absolute RSS floor and slow + // recycling of freed allocations; widen both bounds under bun-asan. + expect(after).toBeLessThan((isASAN ? 700 : 250) * 1024 * 1024); + expect(after).toBeLessThan(before * (isASAN ? 3 : 1.5)); +}); diff --git a/test/js/web/websocket/websocket-proxy-close-reentrancy.test.ts b/test/js/web/websocket/websocket-proxy-close-reentrancy.test.ts index 8b0a99f57a30..db4188eb2b72 100644 --- a/test/js/web/websocket/websocket-proxy-close-reentrancy.test.ts +++ b/test/js/web/websocket/websocket-proxy-close-reentrancy.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows } from "harness"; +import { bunEnv, bunExe } from "harness"; import path from "node:path"; // Calling ws.close() on a wss:// WebSocket that is connecting through an HTTP @@ -11,7 +11,7 @@ import path from "node:path"; // // The re-entrancy requires synchronous us_socket_close → on_close dispatch, which // only happens on POSIX; libuv defers the close callback on Windows. -test.skipIf(isWindows)("ws.close() during proxy TLS handshake does not double-free", async () => { +test("ws.close() during proxy TLS handshake does not double-free", async () => { await using proc = Bun.spawn({ cmd: [bunExe(), path.join(import.meta.dir, "websocket-proxy-close-reentrancy-fixture.ts")], env: bunEnv, diff --git a/test/js/web/workers/worker-late-completion.test.ts b/test/js/web/workers/worker-late-completion.test.ts index c7b032a1c095..5c95c4e82a58 100644 --- a/test/js/web/workers/worker-late-completion.test.ts +++ b/test/js/web/workers/worker-late-completion.test.ts @@ -371,7 +371,7 @@ describe.skipIf(isWindows)("terminate() cancels a read parked on the io loop", ( // a FIFO nobody has written to yet (node:fs reads that way), so its duration is // entirely the test's to decide — no timing thresholds. Debug builds also name // what the wait is waiting for. -describe.skipIf(isWindows)("terminate() waits for work that cannot be cancelled", () => { +describe("terminate() waits for work that cannot be cancelled", () => { test("a pool thread parked in read() holds the worker's teardown until it returns", async () => { using dir = tempDir("worker-terminate-waits", {}); const fifo = path.join(String(dir), "fifo"); diff --git a/test/js/workerd/html-rewriter.test.js b/test/js/workerd/html-rewriter.test.js index 9260f59efc23..b0c6414b008f 100644 --- a/test/js/workerd/html-rewriter.test.js +++ b/test/js/workerd/html-rewriter.test.js @@ -2704,7 +2704,7 @@ describe("streamed input pacing", () => { // Regular-file reads are synchronous on POSIX, so reading ahead of the // consumer would show up as `transform()` itself reading (and buffering the // rewrite of) the entire file. Sparse files keep these cheap. - describe.skipIf(isWindows)("large sparse file", () => { + describe("large sparse file", () => { // 5 GiB: a body size that does not fit lol_html's `u32::MAX` memory limit. it.concurrent.each([256 * 1024 * 1024, 5 * 1024 * 1024 * 1024])( "of %d bytes: transform() does not read ahead", diff --git a/test/napi/napi.test.ts b/test/napi/napi.test.ts index 9db7e54a0289..8fcc19841af5 100644 --- a/test/napi/napi.test.ts +++ b/test/napi/napi.test.ts @@ -2022,7 +2022,7 @@ describe.concurrent.skipIf(!canBuildNodeAddons())("napi", () => { // BUN_JSC_slowPathAllocsBetweenGCs collects every few slow-path allocations. maxBuffer is the last option // Bun.spawnSync reads, so the collection that follows the drop runs once it is past them. Bun.gc() leaves every free // list empty, so what it allocates from there on takes the slow path, whatever ran before. - it.skipIf(isWindows)("async work queued by a finalizer during Bun.spawnSync keeps the event loop alive", async () => { + it("async work queued by a finalizer during Bun.spawnSync keeps the event loop alive", async () => { const code = ` const addon = require(${JSON.stringify(join(__dirname, "napi-app/build/Debug/test_queue_async_work_in_finalizer_experimental.node"))}); // In a frame of its own: an object left in a register of this one would outlive the drop. @@ -2063,10 +2063,8 @@ describe.concurrent.skipIf(!canBuildNodeAddons())("napi", () => { // The other way around: the ref a finalizer gave up was taken off the loop being waited on, so the thread's loop // kept it and the process never exited. - it.skipIf(isWindows)( - "a threadsafe function unref'd by a finalizer during Bun.spawnSync lets go of the event loop", - async () => { - const code = ` + it("a threadsafe function unref'd by a finalizer during Bun.spawnSync lets go of the event loop", async () => { + const code = ` const addon = require(${JSON.stringify(join(__dirname, "napi-app/build/Debug/test_queue_async_work_in_finalizer_experimental.node"))}); (() => { globalThis.held = Array.from({ length: 3 }, () => addon.makeThreadsafeFunction()); })(); let finalizedBeforeDrop; @@ -2082,20 +2080,19 @@ describe.concurrent.skipIf(!canBuildNodeAddons())("napi", () => { }); console.log(JSON.stringify({ finalizedBeforeDrop, finalizedDuringCall: addon.finalized() })); `; - await using proc = spawn({ - cmd: [bunExe(), "-e", code], - env: { ...bunEnv, BUN_JSC_slowPathAllocsBetweenGCs: "3" }, - stdout: "pipe", - stderr: "pipe", - }); - const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); - expect({ stdout: stdout.trim(), stderr, exitCode }).toEqual({ - stdout: JSON.stringify({ finalizedBeforeDrop: 0, finalizedDuringCall: 3 }), - stderr: "", - exitCode: 0, - }); - }, - ); + await using proc = spawn({ + cmd: [bunExe(), "-e", code], + env: { ...bunEnv, BUN_JSC_slowPathAllocsBetweenGCs: "3" }, + stdout: "pipe", + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect({ stdout: stdout.trim(), stderr, exitCode }).toEqual({ + stdout: JSON.stringify({ finalizedBeforeDrop: 0, finalizedDuringCall: 3 }), + stderr: "", + exitCode: 0, + }); + }); it("napi_reference_unref can be called from finalizers in regular modules", async () => { // This test ensures that napi_reference_unref can be called during GC diff --git a/test/regression/issue/05828.test.ts b/test/regression/issue/05828.test.ts index 5b3b3e752ec9..0545af6e1c72 100644 --- a/test/regression/issue/05828.test.ts +++ b/test/regression/issue/05828.test.ts @@ -1,9 +1,9 @@ import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isPosix } from "harness"; +import { bunEnv, bunExe } from "harness"; import { join } from "path"; // https://github.com/oven-sh/bun/issues/5828 -test.if(isPosix)("bun bun.lockb handles BrokenPipe gracefully", async () => { +test("bun bun.lockb handles BrokenPipe gracefully", async () => { // Use an existing lockfile that has enough content to trigger the BrokenPipe // The sharp integration test has a lockfile with many dependencies const lockfilePath = join(import.meta.dir, "../../integration/sharp/bun.lockb"); diff --git a/test/regression/issue/18239/18239.test.ts b/test/regression/issue/18239/18239.test.ts index 7c427a83f464..72196faef026 100644 --- a/test/regression/issue/18239/18239.test.ts +++ b/test/regression/issue/18239/18239.test.ts @@ -1,10 +1,10 @@ import { spawnSync } from "bun"; import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows } from "harness"; +import { bunEnv, bunExe } from "harness"; import { join } from "path"; // https://github.com/oven-sh/bun/issues/18239 -test.skipIf(isWindows)("TTY stdin buffering should work correctly", async () => { +test("TTY stdin buffering should work correctly", async () => { const dataGeneratorPath = join(import.meta.dir, "data-generator.sh"); const fixturePath = join(import.meta.dir, "18239.fixture.ts"); diff --git a/test/regression/issue/24329.test.ts b/test/regression/issue/24329.test.ts index 1c88174ff128..146df11fa234 100644 --- a/test/regression/issue/24329.test.ts +++ b/test/regression/issue/24329.test.ts @@ -6,11 +6,6 @@ import { bunEnv, isWindows, tempDir } from "harness"; // See: https://github.com/oven-sh/bun/issues/24329 test("bun npm placeholder script should exit with error if postinstall hasn't run", async () => { - // Skip on Windows as the placeholder is a shell script - if (isWindows) { - return; - } - // This is the placeholder script content that gets written to bin/bun.exe // during npm package build (see packages/bun-release/scripts/upload-npm.ts) // Note: no shebang — a #!/bin/sh shebang breaks Windows because npm's cmd-shim diff --git a/test/regression/issue/26286.test.ts b/test/regression/issue/26286.test.ts index 40d2c7b505b6..ef9c0dbf1909 100644 --- a/test/regression/issue/26286.test.ts +++ b/test/regression/issue/26286.test.ts @@ -36,7 +36,7 @@ test.skipIf(isWindows)("Bun.Terminal data callback works inside AsyncLocalStorag expect(new TextDecoder().decode(result.data!)).toContain("Hello"); }); -test.skipIf(isWindows)("Bun.Terminal preserves async context inside callbacks", async () => { +test("Bun.Terminal preserves async context inside callbacks", async () => { const storage = new AsyncLocalStorage<{ id: number }>(); async function terminalTest() { diff --git a/test/regression/issue/29519.test.ts b/test/regression/issue/29519.test.ts index 15a965c8a519..c80f5f2ea78a 100644 --- a/test/regression/issue/29519.test.ts +++ b/test/regression/issue/29519.test.ts @@ -7,13 +7,11 @@ // crashing visitChildren walks) in each new global. import { describe, expect, test } from "bun:test"; -import { bunEnv, bunExe, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, tempDir } from "harness"; -// collectContinuously is very slow under Windows + ASAN in CI; the code path -// is identical on Linux/macOS, so skip Windows to keep duration reasonable. // Both tests spawn independent subprocesses with no shared state, so run them // concurrently to halve wall-clock. -describe.skipIf(isWindows).concurrent("Zig::GlobalObject creation on a warm VM under concurrent GC", () => { +describe.concurrent("Zig::GlobalObject creation on a warm VM under concurrent GC", () => { test("bun test --isolate survives concurrent GC while swapping globals", async () => { const files: Record = {}; // Six files is enough to recycle the Zig::GlobalObject IsoSubspace slot diff --git a/test/regression/issue/31503.test.ts b/test/regression/issue/31503.test.ts index 58fa438d10b7..72684df2c157 100644 --- a/test/regression/issue/31503.test.ts +++ b/test/regression/issue/31503.test.ts @@ -1,5 +1,5 @@ import { expect, test } from "bun:test"; -import { bunEnv, bunExe, isMacOS, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, isMacOS, tempDir } from "harness"; import { join } from "node:path"; // https://github.com/oven-sh/bun/issues/31503 @@ -35,11 +35,10 @@ for (let s = 0; s < spellings; s++) { console.log("resolved-ok"); `; -// Skipped on Windows (dir symlinks need privileges) and macOS (resolving through -// ~500 symlinks exhausts its low default fd limit, so `Bun.resolveSync` throws). -// The overflowing code path is in the libc/arch-independent allocator, so Linux -// (glibc + musl, x64 + aarch64) coverage is sufficient. -test.skipIf(isWindows || isMacOS)( +// Skipped on macOS: resolving through ~500 symlinks exhausts its low default fd +// limit, so `Bun.resolveSync` throws. The overflowing code path is in the +// libc/arch-independent allocator, so coverage elsewhere is sufficient. +test.skipIf(isMacOS)( "resolver filename store survives interning >270k long names (#31503)", async () => { using dir = tempDir("issue-31503", { "driver.js": DRIVER }); diff --git a/test/regression/issue/40111.test.ts b/test/regression/issue/40111.test.ts index 038bcc842d62..6bb561b80b6e 100644 --- a/test/regression/issue/40111.test.ts +++ b/test/regression/issue/40111.test.ts @@ -1,6 +1,6 @@ import { expect, test } from "bun:test"; import { readdirSync, statSync } from "fs"; -import { bunEnv, bunExe, isMacOS, isWindows, tempDir } from "harness"; +import { bunEnv, bunExe, isMacOS, tempDir } from "harness"; import { join } from "path"; // https://github.com/oven-sh/bun/issues/40111 @@ -10,7 +10,7 @@ import { join } from "path"; // EACCES, failing the build. The temp file must carry owner read/write // permission from creation. Skipped on macOS: the clonefile() fast path // copies the executable's 0755 mode and never reaches the open() under test. -test.skipIf(isWindows || isMacOS)("compile temp file is not created with mode 000", async () => { +test.skipIf(isMacOS)("compile temp file is not created with mode 000", async () => { using dir = tempDir("issue-40111", { "index.ts": `console.log("hello");`, }); diff --git a/test/regression/issue/atomics-waitasync-wtftimer-uaf.test.ts b/test/regression/issue/atomics-waitasync-wtftimer-uaf.test.ts index 6fb973ff1db3..9ceb95d7a733 100644 --- a/test/regression/issue/atomics-waitasync-wtftimer-uaf.test.ts +++ b/test/regression/issue/atomics-waitasync-wtftimer-uaf.test.ts @@ -3,9 +3,8 @@ // The UAF occurs when the timer fires and continues to reference `this` after it's been freed. import { expect, test } from "bun:test"; -import { isWindows } from "harness"; -test.todoIf(isWindows)("Atomics.waitAsync with setTimeout does not crash (UAF bug)", async () => { +test("Atomics.waitAsync with setTimeout does not crash (UAF bug)", async () => { // Run 2 times to trigger the UAF with ASAN for (let i = 0; i < 2; i++) { const buffer = new SharedArrayBuffer(16); @@ -31,7 +30,7 @@ test.todoIf(isWindows)("Atomics.waitAsync with setTimeout does not crash (UAF bu } }); -test.todoIf(isWindows)("Multiple Atomics.waitAsync calls do not crash", async () => { +test("Multiple Atomics.waitAsync calls do not crash", async () => { const buffer = new SharedArrayBuffer(16); const view = new Int32Array(buffer); diff --git a/test/regression/issue/isArray-proxy-crash.test.ts b/test/regression/issue/isArray-proxy-crash.test.ts index 0fc51d067c9e..043209a9bb77 100644 --- a/test/regression/issue/isArray-proxy-crash.test.ts +++ b/test/regression/issue/isArray-proxy-crash.test.ts @@ -47,7 +47,7 @@ describe("isArray + Proxy crash fixes", () => { expect(accesses).toContain("1"); }); - test.skipIf(process.platform === "win32")("process.setgroups throws TypeError for Proxy (no crash)", () => { + test("process.setgroups throws TypeError for Proxy (no crash)", () => { // Node.js also rejects Proxy here (with a native assertion). We throw a TypeError. expect(() => process.setgroups!(new Proxy([], {}))).toThrow(TypeError); });