mirror of
https://github.com/oven-sh/bun
synced 2026-02-14 12:51:54 +00:00
test(bundler): add memory option to avoid disk I/O in tests
Add a `memory: true` option to expectBundled that keeps build outputs in memory instead of writing to disk. This reduces I/O overhead for tests that only need to verify bundler output content. Enable for WPT CSS tests which were timing out on macOS due to slow disk I/O with many small test cases. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
|
||||
@@ -12,6 +12,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
|
||||
@@ -11,6 +11,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
|
||||
@@ -13,6 +13,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -33,6 +34,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -53,6 +55,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -73,6 +76,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -93,6 +97,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -113,6 +118,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -133,6 +139,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -153,6 +160,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -173,6 +181,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -193,6 +202,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -213,6 +223,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -233,6 +244,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -253,6 +265,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -273,6 +286,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -293,6 +307,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -313,6 +328,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -333,6 +349,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -353,6 +370,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -373,6 +391,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -393,6 +412,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -413,6 +433,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -433,6 +454,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -453,6 +475,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -473,6 +496,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -493,6 +517,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -513,6 +538,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
@@ -533,6 +559,7 @@ h1 {
|
||||
`,
|
||||
},
|
||||
outfile: "/out.css",
|
||||
memory: true,
|
||||
|
||||
onAfterBundle(api) {
|
||||
api.expectFile("/out.css").toEqualIgnoringWhitespace(`
|
||||
|
||||
@@ -299,6 +299,9 @@ export interface BundlerTestInput {
|
||||
|
||||
/** Run after the bun.build function is called with its output */
|
||||
onAfterApiBundle?(build: BuildOutput): Promise<void> | void;
|
||||
|
||||
/** If true, build outputs are kept in memory instead of written to disk. Defaults to false. */
|
||||
memory?: boolean;
|
||||
}
|
||||
|
||||
export interface SourceMapTests {
|
||||
@@ -494,6 +497,7 @@ function expectBundled(
|
||||
generateOutput = true,
|
||||
onAfterApiBundle,
|
||||
throw: _throw = false,
|
||||
memory = false,
|
||||
...unknownProps
|
||||
} = opts;
|
||||
|
||||
@@ -584,7 +588,10 @@ function expectBundled(
|
||||
}
|
||||
|
||||
return (async () => {
|
||||
if (!backend) {
|
||||
// memory mode requires API backend since we need in-memory outputs
|
||||
if (memory) {
|
||||
backend = "api";
|
||||
} else if (!backend) {
|
||||
backend =
|
||||
dotenv ||
|
||||
typeof production !== "undefined" ||
|
||||
@@ -701,6 +708,8 @@ function expectBundled(
|
||||
|
||||
// Run bun build cli. In the future we can move to using `Bun.Transpiler.`
|
||||
let warningReference: Record<string, ErrorMeta[]> = {};
|
||||
// Map to store build outputs in memory when memory=true
|
||||
const memoryOutputs: Map<string, string> = new Map();
|
||||
const expectedErrors = bundleErrors
|
||||
? Object.entries(bundleErrors).flatMap(([file, v]) => v.map(error => ({ file, error })))
|
||||
: null;
|
||||
@@ -1111,7 +1120,7 @@ function expectBundled(
|
||||
},
|
||||
plugins: pluginArray,
|
||||
treeShaking,
|
||||
outdir: generateOutput ? buildOutDir : undefined,
|
||||
outdir: generateOutput && !memory ? buildOutDir : undefined,
|
||||
sourcemap: sourceMap,
|
||||
splitting,
|
||||
target,
|
||||
@@ -1284,6 +1293,20 @@ for (const [key, blob] of build.outputs) {
|
||||
} else if (expectedErrors && expectedErrors.length > 0) {
|
||||
throw new Error("Errors were expected while bundling:\n" + expectedErrors.map(formatError).join("\n"));
|
||||
}
|
||||
|
||||
// Populate memoryOutputs if memory mode is enabled
|
||||
if (memory && build.success) {
|
||||
for (const artifact of build.outputs) {
|
||||
let normalizedPath = artifact.path;
|
||||
// Normalize path to have leading slash
|
||||
if (normalizedPath.startsWith("./")) {
|
||||
normalizedPath = normalizedPath.slice(1);
|
||||
} else if (!normalizedPath.startsWith("/")) {
|
||||
normalizedPath = "/" + normalizedPath;
|
||||
}
|
||||
memoryOutputs.set(normalizedPath, await artifact.text());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await esbuild.build({
|
||||
bundle: true,
|
||||
@@ -1295,11 +1318,23 @@ for (const [key, blob] of build.outputs) {
|
||||
}
|
||||
|
||||
const readCache: Record<string, string> = {};
|
||||
const readFile = (file: string) =>
|
||||
readCache[file] || (readCache[file] = readFileSync(path.join(root, file)).toUnixString());
|
||||
const readFile = (file: string) => {
|
||||
if (readCache[file]) return readCache[file];
|
||||
// Check memory outputs first
|
||||
if (memoryOutputs.size > 0) {
|
||||
const memContent = memoryOutputs.get(file);
|
||||
if (memContent !== undefined) {
|
||||
readCache[file] = memContent;
|
||||
return memContent;
|
||||
}
|
||||
}
|
||||
return (readCache[file] = readFileSync(path.join(root, file)).toUnixString());
|
||||
};
|
||||
const writeFile = (file: string, contents: string) => {
|
||||
readCache[file] = contents;
|
||||
writeFileSync(path.join(root, file), contents);
|
||||
if (!memory) {
|
||||
writeFileSync(path.join(root, file), contents);
|
||||
}
|
||||
};
|
||||
const api = {
|
||||
root,
|
||||
@@ -1312,7 +1347,11 @@ for (const [key, blob] of build.outputs) {
|
||||
prependFile: (file, contents) => writeFile(file, dedent(contents) + "\n" + readFile(file)),
|
||||
appendFile: (file, contents) => writeFile(file, readFile(file) + "\n" + dedent(contents)),
|
||||
assertFileExists: file => {
|
||||
if (!existsSync(path.join(root, file))) {
|
||||
if (memoryOutputs.size > 0) {
|
||||
if (!memoryOutputs.has(file)) {
|
||||
throw new Error("Expected file to be in memory: " + file);
|
||||
}
|
||||
} else if (!existsSync(path.join(root, file))) {
|
||||
throw new Error("Expected file to be written: " + file);
|
||||
}
|
||||
},
|
||||
@@ -1369,7 +1408,7 @@ for (const [key, blob] of build.outputs) {
|
||||
|
||||
// Check that the bundle failed with status code 0 by verifying all files exist.
|
||||
// TODO: clean up this entire bit into one main loop\
|
||||
if (!compile) {
|
||||
if (!compile && !memory) {
|
||||
if (outfile) {
|
||||
if (!existsSync(outfile)) {
|
||||
throw new Error("Bundle was not written to disk: " + outfile);
|
||||
|
||||
Reference in New Issue
Block a user