Skip to content

Commit 18caa4c

Browse files
committed
test: cover the memory changes
- With one thread, chunks are processed one at a time, in order. - Workers get both the old and young generation limits. - jsx-ast and legacy-html leave their input unchanged on the main thread. - resolveBundler sets the bundler's thread and allocator variables from `threads`, keeps any already set, and leaves a custom bundler alone. Each fails against `main`. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent f0e25a7 commit 18caa4c

5 files changed

Lines changed: 182 additions & 0 deletions

File tree

‎packages/core/src/threading/__tests__/index.test.mjs‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url';
44

55
import { LogLevel } from '../../logger/constants.mjs';
66
import logger from '../../logger/index.mjs';
7+
import { WORKER_GEN_SIZE, WORKER_HEAP_SIZE } from '../constants.mjs';
78
import createWorkerPool from '../index.mjs';
89

910
const reporterSpecifier = fileURLToPath(
@@ -114,4 +115,17 @@ describe('createWorkerPool', () => {
114115
await pool.destroy();
115116
}
116117
});
118+
119+
it("should limit each worker's old and young generation", async () => {
120+
const pool = createWorkerPool(1);
121+
122+
try {
123+
deepStrictEqual(pool.options.resourceLimits, {
124+
maxOldGenerationSizeMb: WORKER_HEAP_SIZE,
125+
maxYoungGenerationSizeMb: WORKER_GEN_SIZE,
126+
});
127+
} finally {
128+
await pool.destroy();
129+
}
130+
});
117131
});

‎packages/core/src/threading/__tests__/parallel.test.mjs‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,32 @@ describe('createParallelWorker', () => {
177177

178178
await pool.destroy();
179179
});
180+
181+
it('should process one chunk at a time, in order, with a single thread', async () => {
182+
let running = 0;
183+
let mostRunning = 0;
184+
185+
const generator = {
186+
name: 'sequential',
187+
async processChunk(items, indices) {
188+
mostRunning = Math.max(mostRunning, ++running);
189+
190+
await new Promise(resolve => setImmediate(resolve));
191+
192+
running--;
193+
194+
return indices.map(index => items[index]);
195+
},
196+
};
197+
198+
const worker = createParallelWorker('sequential', generator, null, {
199+
threads: 1,
200+
chunkSize: 2,
201+
});
202+
203+
const chunks = await collectChunks(worker.stream([1, 2, 3, 4, 5], {}));
204+
205+
strictEqual(mostRunning, 1);
206+
deepStrictEqual(chunks, [[1, 2], [3, 4], [5]]);
207+
});
180208
});
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import assert from 'node:assert/strict';
2+
import { describe, it } from 'node:test';
3+
4+
import { loadGenerator } from '@doc-kit/core/generators/loader.mjs';
5+
import { setConfig } from '@doc-kit/core/utils/configuration/index.mjs';
6+
import { loadMarkdownPlugins } from '@doc-kit/core/utils/markdown/plugins.mjs';
7+
8+
import { processChunk } from '../generate.mjs';
9+
10+
await loadMarkdownPlugins(
11+
await loadGenerator(import.meta.resolve('../index.mjs'))
12+
);
13+
14+
const createEntry = (api, name) => {
15+
const heading = {
16+
type: 'heading',
17+
depth: 1,
18+
children: [{ type: 'text', value: name }],
19+
data: { name, text: name, slug: api, type: 'module' },
20+
};
21+
22+
return {
23+
api,
24+
path: `/${api}`,
25+
heading,
26+
stability: null,
27+
content: {
28+
type: 'root',
29+
children: [
30+
heading,
31+
{
32+
type: 'paragraph',
33+
children: [{ type: 'text', value: `${name} body` }],
34+
},
35+
],
36+
},
37+
};
38+
};
39+
40+
describe('legacy-html processChunk', () => {
41+
it('does not change its input on the main thread', async () => {
42+
await setConfig({ target: ['legacy-html'] });
43+
44+
const fs = createEntry('fs', 'File system');
45+
const before = structuredClone(fs);
46+
47+
const [page] = await processChunk(
48+
[{ head: fs, nodes: [fs], headNodes: [fs] }],
49+
[0],
50+
'<a class="nav-fs" href="fs.html">File system</a>'
51+
);
52+
53+
assert.match(page.content, /File system body/);
54+
assert.deepEqual(fs, before);
55+
});
56+
});
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import assert from 'node:assert/strict';
2+
import { afterEach, describe, it, mock } from 'node:test';
3+
4+
import { setConfig } from '@doc-kit/core/utils/configuration/index.mjs';
5+
6+
mock.module('../vite.mjs', {
7+
namedExports: { createViteBundler: () => 'vite' },
8+
});
9+
10+
const { resolveBundler } = await import('../index.mjs');
11+
12+
const VARIABLES = [
13+
'ROLLDOWN_WORKER_THREADS',
14+
'RAYON_NUM_THREADS',
15+
'MIMALLOC_PURGE_DELAY',
16+
'MIMALLOC_ALLOW_THP',
17+
];
18+
19+
const original = Object.fromEntries(
20+
VARIABLES.map(name => [name, process.env[name]])
21+
);
22+
23+
afterEach(() => {
24+
for (const name of VARIABLES) {
25+
if (original[name] === undefined) {
26+
delete process.env[name];
27+
} else {
28+
process.env[name] = original[name];
29+
}
30+
}
31+
});
32+
33+
describe('resolveBundler', () => {
34+
it("sizes the bundler's thread pools and allocator before loading it", async () => {
35+
await setConfig({ target: ['html'], threads: 3 });
36+
37+
for (const name of VARIABLES) {
38+
delete process.env[name];
39+
}
40+
41+
assert.equal(await resolveBundler(), 'vite');
42+
assert.equal(process.env.ROLLDOWN_WORKER_THREADS, '3');
43+
assert.equal(process.env.RAYON_NUM_THREADS, '3');
44+
assert.equal(process.env.MIMALLOC_PURGE_DELAY, '0');
45+
assert.equal(process.env.MIMALLOC_ALLOW_THP, '0');
46+
});
47+
48+
it('keeps what the environment already sets', async () => {
49+
await setConfig({ target: ['html'], threads: 3 });
50+
51+
process.env.ROLLDOWN_WORKER_THREADS = '8';
52+
process.env.MIMALLOC_PURGE_DELAY = '250';
53+
54+
await resolveBundler();
55+
56+
assert.equal(process.env.ROLLDOWN_WORKER_THREADS, '8');
57+
assert.equal(process.env.MIMALLOC_PURGE_DELAY, '250');
58+
});
59+
60+
it('leaves the environment alone for a custom bundler', async () => {
61+
for (const name of VARIABLES) {
62+
delete process.env[name];
63+
}
64+
65+
const bundler = {};
66+
67+
assert.equal(await resolveBundler(bundler), bundler);
68+
69+
for (const name of VARIABLES) {
70+
assert.equal(process.env[name], undefined);
71+
}
72+
});
73+
});

‎packages/react/src/jsx-ast/__tests__/generate.test.mjs‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,15 @@ describe('jsx-ast generate', () => {
108108
['index', 'fs']
109109
);
110110
});
111+
112+
it('does not change its input on the main thread', async () => {
113+
await setConfig({ target: ['jsx-ast'] });
114+
115+
const fs = createEntry('fs', 'File system');
116+
const before = structuredClone(fs);
117+
118+
await processChunk([{ head: fs, entries: [fs] }], [0]);
119+
120+
assert.deepEqual(fs, before);
121+
});
111122
});

0 commit comments

Comments
 (0)