mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 02:48:50 +00:00
Co-authored-by: Jarred-Sumner <709451+Jarred-Sumner@users.noreply.github.com> Co-authored-by: Alistair Smith <hi@alistair.sh> Co-authored-by: Claude Bot <claude-bot@bun.sh> Co-authored-by: Claude <noreply@anthropic.com>
190 lines
6.2 KiB
TypeScript
190 lines
6.2 KiB
TypeScript
import { Subprocess } from "bun";
|
|
import { beforeEach, describe, expect, test } from "bun:test";
|
|
import { chmodSync, existsSync, mkdirSync, readdirSync, realpathSync, rmSync, writeFileSync } from "fs";
|
|
import { bunEnv, bunExe, bunRun, tmpdirSync } from "harness";
|
|
import { join } from "path";
|
|
|
|
function dummyFile(size: number, cache_bust: string, value: string | { code: string }) {
|
|
const data = Buffer.alloc(size);
|
|
data.write("/*" + cache_bust);
|
|
const end = `*/\nconsole.log(${(value as any).code ?? JSON.stringify(value)});`;
|
|
data.fill("*", 2 + cache_bust.length, size - end.length, "utf-8");
|
|
data.write(end, size - end.length, "utf-8");
|
|
return data;
|
|
}
|
|
|
|
let temp_dir: string = "";
|
|
let cache_dir = "";
|
|
|
|
const env = {
|
|
...bunEnv,
|
|
BUN_RUNTIME_TRANSPILER_CACHE_PATH: cache_dir,
|
|
BUN_DEBUG_ENABLE_RESTORE_FROM_TRANSPILER_CACHE: "1",
|
|
};
|
|
|
|
let prev_cache_count = 0;
|
|
function newCacheCount() {
|
|
let new_count = readdirSync(cache_dir).length;
|
|
let delta = new_count - prev_cache_count;
|
|
prev_cache_count = new_count;
|
|
return delta;
|
|
}
|
|
|
|
function removeCache() {
|
|
prev_cache_count = 0;
|
|
try {
|
|
rmSync(cache_dir, { recursive: true, force: true });
|
|
} catch (error) {
|
|
chmodSync(cache_dir, 0o777);
|
|
readdirSync(cache_dir).forEach(item => {
|
|
chmodSync(join(cache_dir, item), 0o777);
|
|
});
|
|
rmSync(cache_dir, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
beforeEach(() => {
|
|
if (cache_dir) {
|
|
rmSync(temp_dir, { recursive: true, force: true });
|
|
removeCache();
|
|
}
|
|
|
|
temp_dir = tmpdirSync();
|
|
mkdirSync(temp_dir, { recursive: true });
|
|
temp_dir = realpathSync(temp_dir);
|
|
cache_dir = join(temp_dir, ".cache");
|
|
env.BUN_RUNTIME_TRANSPILER_CACHE_PATH = cache_dir;
|
|
});
|
|
|
|
describe("transpiler cache", () => {
|
|
test("works", async () => {
|
|
writeFileSync(join(temp_dir, "a.js"), dummyFile((50 * 1024 * 1.5) | 0, "1", "a"));
|
|
const a = bunRun(join(temp_dir, "a.js"), env);
|
|
expect(a.stdout == "a");
|
|
expect(existsSync(cache_dir)).toBeTrue();
|
|
expect(newCacheCount()).toBe(1);
|
|
const b = bunRun(join(temp_dir, "a.js"), env);
|
|
expect(b.stdout == "a");
|
|
expect(newCacheCount()).toBe(0);
|
|
});
|
|
test("works with empty files", async () => {
|
|
writeFileSync(join(temp_dir, "a.js"), "//" + "a".repeat(50 * 1024 * 1.5));
|
|
const a = bunRun(join(temp_dir, "a.js"), env);
|
|
expect(a.stdout == "");
|
|
expect(existsSync(cache_dir)).toBeTrue();
|
|
expect(newCacheCount()).toBe(1);
|
|
const b = bunRun(join(temp_dir, "a.js"), env);
|
|
expect(b.stdout == "");
|
|
expect(newCacheCount()).toBe(0);
|
|
});
|
|
test("ignores files under 50kb", async () => {
|
|
writeFileSync(join(temp_dir, "a.js"), dummyFile(50 * 1024 - 1, "1", "a"));
|
|
const a = bunRun(join(temp_dir, "a.js"), env);
|
|
expect(a.stdout == "a");
|
|
expect(!existsSync(cache_dir)).toBeTrue();
|
|
});
|
|
test("it is indeed content addressable", async () => {
|
|
writeFileSync(join(temp_dir, "a.js"), dummyFile(50 * 1024, "1", "b"));
|
|
const a = bunRun(join(temp_dir, "a.js"), env);
|
|
expect(a.stdout == "b");
|
|
expect(newCacheCount()).toBe(1);
|
|
|
|
writeFileSync(join(temp_dir, "a.js"), dummyFile(50 * 1024, "1", "c"));
|
|
const b = bunRun(join(temp_dir, "a.js"), env);
|
|
expect(b.stdout == "c");
|
|
expect(newCacheCount()).toBe(1);
|
|
|
|
writeFileSync(join(temp_dir, "b.js"), dummyFile(50 * 1024, "1", "b"));
|
|
const c = bunRun(join(temp_dir, "b.js"), env);
|
|
expect(b.stdout == "b");
|
|
expect(newCacheCount()).toBe(0);
|
|
});
|
|
test("doing 50 buns at once does not crash", async () => {
|
|
writeFileSync(join(temp_dir, "a.js"), dummyFile(50 * 1024, "1", "b"));
|
|
writeFileSync(join(temp_dir, "b.js"), dummyFile(50 * 1024, "2", "b"));
|
|
|
|
const remover = Bun.spawn({
|
|
cmd: [bunExe(), join(import.meta.dir, "transpiler-cache-aggressive-remover.js"), cache_dir],
|
|
env,
|
|
cwd: temp_dir,
|
|
});
|
|
|
|
let processes: Subprocess<"ignore", "pipe", "inherit">[] = [];
|
|
let killing = false;
|
|
for (let i = 0; i < 50; i++) {
|
|
processes.push(
|
|
Bun.spawn({
|
|
cmd: [bunExe(), i % 2 == 0 ? "a.js" : "b.js"],
|
|
env,
|
|
cwd: temp_dir,
|
|
onExit(subprocess, exitCode, signalCode, error) {
|
|
if (exitCode != 0 && !killing) {
|
|
killing = true;
|
|
processes.forEach(x => x.kill(9));
|
|
remover.kill(9);
|
|
}
|
|
},
|
|
}),
|
|
);
|
|
}
|
|
|
|
await Promise.all(processes.map(x => x.exited));
|
|
|
|
expect(!killing).toBeTrue();
|
|
|
|
remover.kill(9);
|
|
|
|
for (const proc of processes) {
|
|
expect(proc.exitCode).toBe(0);
|
|
expect(await proc.stdout.text()).toBe("b\n");
|
|
}
|
|
}, 99999999);
|
|
test("works if the cache is not user-readable", () => {
|
|
mkdirSync(cache_dir, { recursive: true });
|
|
writeFileSync(join(temp_dir, "a.js"), dummyFile((50 * 1024 * 1.5) | 0, "1", "b"));
|
|
const a = bunRun(join(temp_dir, "a.js"), env);
|
|
expect(a.stdout == "b");
|
|
expect(newCacheCount()).toBe(1);
|
|
|
|
const cache_item = readdirSync(cache_dir)[0];
|
|
|
|
chmodSync(join(cache_dir, cache_item), 0);
|
|
const b = bunRun(join(temp_dir, "a.js"), env);
|
|
expect(b.stdout == "b");
|
|
expect(newCacheCount()).toBe(0);
|
|
|
|
chmodSync(join(cache_dir), "0");
|
|
try {
|
|
const c = bunRun(join(temp_dir, "a.js"), env);
|
|
expect(c.stdout == "b");
|
|
} finally {
|
|
chmodSync(join(cache_dir), "777");
|
|
}
|
|
});
|
|
test("works if the cache is not user-writable", () => {
|
|
mkdirSync(cache_dir, { recursive: true });
|
|
writeFileSync(join(temp_dir, "a.js"), dummyFile((50 * 1024 * 1.5) | 0, "1", "b"));
|
|
|
|
try {
|
|
chmodSync(join(cache_dir), "0");
|
|
const a = bunRun(join(temp_dir, "a.js"), env);
|
|
expect(a.stdout == "b");
|
|
} finally {
|
|
chmodSync(join(cache_dir), "777");
|
|
}
|
|
});
|
|
test("does not inline process.env", () => {
|
|
writeFileSync(
|
|
join(temp_dir, "a.js"),
|
|
dummyFile((50 * 1024 * 1.5) | 0, "1", { code: "process.env.NODE_ENV, process.env.HELLO" }),
|
|
);
|
|
const a = bunRun(join(temp_dir, "a.js"), { ...env, NODE_ENV: undefined, HELLO: "1" });
|
|
expect(a.stdout == "development 1");
|
|
expect(existsSync(cache_dir)).toBeTrue();
|
|
expect(newCacheCount()).toBe(1);
|
|
const b = bunRun(join(temp_dir, "a.js"), { ...env, NODE_ENV: "production", HELLO: "5" });
|
|
expect(b.stdout == "production 5");
|
|
expect(newCacheCount()).toBe(0);
|
|
});
|
|
});
|