Optimize memory usage in the unit tests

This commit fixes a number of missing cleanup steps in the unit tests
that kept state alive for longer than necessary:

- the loading tasks were not all being destroyed;
- the find controllers were not being reset;
- the state set in `beforeAll`/`beforeEach` was not all being nulled in
  the correspoding `afterAll`/`afterEach` blocks.

Combined this resulted in a steady increase in memory usage of the test
process as the tests ran, climbing up to ~1.5 GB. After this patch the
memory usage remains stable at ~800 MB.
This commit is contained in:
Tim van der Meij 2026-04-29 16:02:01 +02:00
parent b71be8a501
commit 232506f4e8
No known key found for this signature in database
GPG Key ID: 8C3FD2925A5F2762
4 changed files with 194 additions and 60 deletions

View File

@ -893,7 +893,7 @@ describe("api", function () {
const pdfDocument = await Promise.race([loadingTask.promise, promise]);
expect(pdfDocument?.numPages).toEqual(1);
loadingTask._worker.destroy();
await loadingTask.destroy();
});
});
@ -1119,6 +1119,8 @@ describe("api", function () {
afterAll(async function () {
await pdfLoadingTask.destroy();
pdfDocument = null;
pdfLoadingTask = null;
});
function findNode(parent, node, index, check) {
@ -1271,7 +1273,7 @@ describe("api", function () {
const pageIndex = await pdfDoc.getPageIndex(ref);
expect(pageIndex).toEqual(499);
await pdfDoc.destroy();
await loadingTask.destroy();
});
it("gets invalid page index", async function () {
@ -2382,9 +2384,12 @@ describe("api", function () {
);
const pdfDoc = await loadingTask.promise;
const markInfo = await pdfDoc.getMarkInfo();
expect(markInfo.Marked).toEqual(true);
expect(markInfo.UserProperties).toEqual(false);
expect(markInfo.Suspects).toEqual(false);
await loadingTask.destroy();
});
it("gets data", async function () {
@ -3362,6 +3367,9 @@ describe("api", function () {
afterAll(async function () {
await pdfLoadingTask.destroy();
page = null;
pdfDocument = null;
pdfLoadingTask = null;
});
it("gets page number", function () {
@ -5538,6 +5546,8 @@ a dynamic compiler for JavaScript based on our`);
ular since they are expressive, accessible to non-experts, and make
deployment as easy as distributing a source file. They are used for
small scripts as well as for`);
await loadingTask.destroy();
});
});
@ -5955,6 +5965,8 @@ small scripts as well as for`);
const pdfPage = await pdfDoc.getPage(3);
const annots = await pdfPage.getAnnotations();
expect(annots.length).toEqual(0);
await loadingTask.destroy();
});
});

View File

@ -79,7 +79,7 @@ async function initPdfFindController(
});
pdfFindController.setDocument(pdfDocument); // Enable searching.
return { eventBus, pdfFindController };
return { eventBus, loadingTask, pdfFindController };
}
function testSearch({
@ -209,7 +209,8 @@ function testEmptySearch({ eventBus, pdfFindController, state }) {
describe("pdf_find_controller", function () {
it("performs a normal search", async function () {
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
const updateFindMatchesCount = [0];
await testSearch({
@ -227,13 +228,14 @@ describe("pdf_find_controller", function () {
});
expect(updateFindMatchesCount[0]).toBe(9);
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a normal search but the total counts is only updated one time", async function () {
const { eventBus, pdfFindController } = await initPdfFindController(
null,
false
);
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController(null, false);
const updateFindMatchesCount = [0];
const updateFindControlState = [0];
@ -254,13 +256,17 @@ describe("pdf_find_controller", function () {
expect(updateFindMatchesCount[0]).toBe(1);
expect(updateFindControlState[0]).toBe(0);
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a normal search and finds the previous result", async function () {
// Page 14 (with page index 13) contains five results. By default, the
// first result (match index 0) is selected, so the previous result
// should be the fifth result (match index 4).
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testSearch({
eventBus,
@ -275,10 +281,14 @@ describe("pdf_find_controller", function () {
matchIndex: 4,
},
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a case sensitive search", async function () {
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testSearch({
eventBus,
@ -293,12 +303,16 @@ describe("pdf_find_controller", function () {
matchIndex: 0,
},
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs an entire word search", async function () {
// Page 13 contains both 'Government' and 'Governmental', so the latter
// should not be found with entire word search.
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testSearch({
eventBus,
@ -313,12 +327,16 @@ describe("pdf_find_controller", function () {
matchIndex: 0,
},
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a multiple term (no phrase) search", async function () {
// Page 9 contains 'alternate' and pages 6 and 9 contain 'solution'.
// Both should be found for multiple term (no phrase) search.
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testSearch({
eventBus,
@ -332,12 +350,16 @@ describe("pdf_find_controller", function () {
matchIndex: 0,
},
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a multiple term (phrase) search", async function () {
// Page 9 contains 'alternate solution' and pages 6 and 9 contain
// 'solution'. Both should be found for multiple term (phrase) search.
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testSearch({
eventBus,
@ -351,12 +373,14 @@ describe("pdf_find_controller", function () {
matchIndex: 0,
},
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a normal search, where the text is normalized", async function () {
const { eventBus, pdfFindController } = await initPdfFindController(
"fraction-highlight.pdf"
);
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("fraction-highlight.pdf");
await testSearch({
eventBus,
@ -462,12 +486,14 @@ describe("pdf_find_controller", function () {
pageMatches: [[54]],
pageMatchesLength: [[2]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a normal search, where the text with diacritics is normalized", async function () {
const { eventBus, pdfFindController } = await initPdfFindController(
"french_diacritics.pdf"
);
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("french_diacritics.pdf");
await testSearch({
eventBus,
@ -514,10 +540,14 @@ describe("pdf_find_controller", function () {
pageMatches: [[28, 30]],
pageMatchesLength: [[1, 1]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search where one of the results contains an hyphen", async function () {
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testSearch({
eventBus,
@ -531,10 +561,14 @@ describe("pdf_find_controller", function () {
matchIndex: 0,
},
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search where the result is on two lines", async function () {
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testSearch({
eventBus,
@ -550,10 +584,14 @@ describe("pdf_find_controller", function () {
pageMatches: [[2734]],
pageMatchesLength: [[14]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search where the result is on two lines with a punctuation at eol", async function () {
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testSearch({
eventBus,
@ -569,10 +607,14 @@ describe("pdf_find_controller", function () {
pageMatches: [[], [1486]],
pageMatchesLength: [[], [11]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search with a minus sign in the query", async function () {
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testSearch({
eventBus,
@ -618,10 +660,14 @@ describe("pdf_find_controller", function () {
[24],
],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search with square brackets in the query", async function () {
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testSearch({
eventBus,
@ -637,10 +683,14 @@ describe("pdf_find_controller", function () {
pageMatches: [[1498]],
pageMatchesLength: [[24]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search with parenthesis in the query", async function () {
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testSearch({
eventBus,
@ -656,10 +706,14 @@ describe("pdf_find_controller", function () {
pageMatches: [[], [201]],
pageMatchesLength: [[], [9]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search with a final dot in the query", async function () {
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
// The whitespace after the dot mustn't be matched.
const query = "complex applications.";
@ -678,10 +732,14 @@ describe("pdf_find_controller", function () {
pageMatches: [[1941]],
pageMatchesLength: [[21]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search with a dot in the query and a missing whitespace", async function () {
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
// The whitespace after the dot must be matched.
const query = "complex applications.J";
@ -700,10 +758,14 @@ describe("pdf_find_controller", function () {
pageMatches: [[1941]],
pageMatchesLength: [[23]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search with a dot followed by a whitespace in the query", async function () {
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
const query = "complex applications. j";
await testSearch({
@ -720,10 +782,13 @@ describe("pdf_find_controller", function () {
pageMatches: [[1941]],
pageMatchesLength: [[23]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search in a text containing diacritics before -\\n", async function () {
const { eventBus, pdfFindController } =
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("issue14562.pdf");
await testSearch({
@ -759,10 +824,13 @@ describe("pdf_find_controller", function () {
],
],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search in a text containing some Hangul syllables", async function () {
const { eventBus, pdfFindController } =
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("bug1771477.pdf");
await testSearch({
@ -779,10 +847,13 @@ describe("pdf_find_controller", function () {
pageMatches: [[139]],
pageMatchesLength: [[8]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search in a text containing an ideographic at the end of a line", async function () {
const { eventBus, pdfFindController } =
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("issue15340.pdf");
await testSearch({
@ -799,10 +870,13 @@ describe("pdf_find_controller", function () {
pageMatches: [[29]],
pageMatchesLength: [[4]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search in a text containing fullwidth chars", async function () {
const { eventBus, pdfFindController } =
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("issue15690.pdf");
await testSearch({
@ -819,10 +893,13 @@ describe("pdf_find_controller", function () {
pageMatches: [[0, 10, 13, 30, 39, 41, 55, 60, 66, 84, 102, 117, 134]],
pageMatchesLength: [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search in a text with some Katakana at the end of a line", async function () {
const { eventBus, pdfFindController } =
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("issue15759.pdf");
await testSearch({
@ -839,10 +916,14 @@ describe("pdf_find_controller", function () {
pageMatches: [[6]],
pageMatchesLength: [[5]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search with a single diacritic", async function () {
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testEmptySearch({
eventBus,
@ -851,10 +932,13 @@ describe("pdf_find_controller", function () {
query: "\u064E",
},
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search in a text containing combining diacritics", async function () {
const { eventBus, pdfFindController } =
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("issue12909.pdf");
await testSearch({
@ -884,10 +968,13 @@ describe("pdf_find_controller", function () {
matchIndex: 0,
},
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search in a text with some Hiragana diacritics at the end of a line", async function () {
const { eventBus, pdfFindController } =
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("issue16063.pdf");
await testSearch({
@ -919,10 +1006,13 @@ describe("pdf_find_controller", function () {
pageMatches: [[205]],
pageMatchesLength: [[7]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search in a text with some UTF-32 chars", async function () {
const { eventBus, pdfFindController } =
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("bug1820909.pdf");
await testSearch({
@ -939,10 +1029,13 @@ describe("pdf_find_controller", function () {
pageMatches: [[41], [131, 1359]],
pageMatchesLength: [[5], [5, 5]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search in a text with some UTF-32 chars followed by a dash at the end of a line", async function () {
const { eventBus, pdfFindController } =
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("bug1820909.1.pdf");
await testSearch({
@ -959,12 +1052,14 @@ describe("pdf_find_controller", function () {
pageMatches: [[42, 95]],
pageMatchesLength: [[5, 5]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search in a text with some arabic chars in different unicode ranges but with same normalized form", async function () {
const { eventBus, pdfFindController } = await initPdfFindController(
"ArabicCIDTrueType.pdf"
);
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("ArabicCIDTrueType.pdf");
await testSearch({
eventBus,
@ -995,12 +1090,14 @@ describe("pdf_find_controller", function () {
pageMatches: [[6, 25, 44, 63]],
pageMatchesLength: [[1, 1, 1, 1]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search in a text with some f ligatures", async function () {
const { eventBus, pdfFindController } = await initPdfFindController(
"copy_paste_ligatures.pdf"
);
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("copy_paste_ligatures.pdf");
await testSearch({
eventBus,
@ -1016,6 +1113,9 @@ describe("pdf_find_controller", function () {
pageMatches: [[5, 6, 6, 7, 8, 9, 9, 10, 10]],
pageMatchesLength: [[1, 1, 1, 1, 1, 1, 1, 1, 1]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("dispatches updatefindcontrolstate with correct properties", async function () {
@ -1046,12 +1146,16 @@ describe("pdf_find_controller", function () {
});
});
const { eventBus } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testOnFind({ eventBus });
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search in a text with a compound word on two lines", async function () {
const { eventBus, pdfFindController } =
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("issue18693.pdf");
const query = "hel-Lo";
@ -1069,10 +1173,13 @@ describe("pdf_find_controller", function () {
pageMatches: [[6]],
pageMatchesLength: [[query.length]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search after a compound word on two lines", async function () {
const { eventBus, pdfFindController } =
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("issue19120.pdf");
const query = "a";
@ -1090,10 +1197,14 @@ describe("pdf_find_controller", function () {
pageMatches: [[0, 4, 15]],
pageMatchesLength: [[query.length, query.length, query.length]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search with a dash between two digits", async () => {
const { eventBus, pdfFindController } = await initPdfFindController();
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController();
await testSearch({
eventBus,
@ -1124,10 +1235,13 @@ describe("pdf_find_controller", function () {
[7],
],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search with a group of punctuation signs", async () => {
const { eventBus, pdfFindController } =
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("issue20225.pdf");
await testSearch({
@ -1144,10 +1258,13 @@ describe("pdf_find_controller", function () {
pageMatches: [[8]],
pageMatchesLength: [[4]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("performs a search with a group of punctuation signs to escape", async () => {
const { eventBus, pdfFindController } =
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController("issue20516.pdf");
await testSearch({
@ -1164,6 +1281,9 @@ describe("pdf_find_controller", function () {
pageMatches: [[6]],
pageMatchesLength: [[10]],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
describe("custom matcher", () => {
@ -1174,11 +1294,8 @@ describe("pdf_find_controller", function () {
.createSpy("custom find matcher")
.and.callFake(() => [{ index: 0, length: 1 }]);
const { eventBus, pdfFindController } = await initPdfFindController(
null,
false,
spy
);
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController(null, false, spy);
const PAGES_COUNT = 14;
@ -1202,6 +1319,9 @@ describe("pdf_find_controller", function () {
expect(spy.calls.argsFor(1)[1]).toMatch(/^Hence, recording and /);
expect(spy.calls.argsFor(12)[1]).toMatch(/Figure 12. Fraction of time /);
expect(spy.calls.argsFor(13)[1]).toMatch(/^not be interpreted as /);
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
it("uses the results returned by the custom matcher", async () => {
@ -1226,11 +1346,8 @@ describe("pdf_find_controller", function () {
{ index: 80, length: 4 },
]);
const { eventBus, pdfFindController } = await initPdfFindController(
null,
false,
spy
);
const { eventBus, loadingTask, pdfFindController } =
await initPdfFindController(null, false, spy);
await testSearch({
eventBus,
@ -1239,6 +1356,9 @@ describe("pdf_find_controller", function () {
selectedMatch: { pageIndex: 0, matchIndex: 0 },
matchesPerPage: [2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3],
});
pdfFindController.setDocument(null);
await loadingTask.destroy();
});
});
});

View File

@ -76,6 +76,7 @@ describe("Scripting", function () {
sandbox = null;
send_queue = null;
window.alert = windowAlert;
windowAlert = test_id = ref = null;
});
describe("Sandbox", function () {

View File

@ -300,6 +300,7 @@ describe("struct tree", function () {
},
struct
);
await loadingTask.destroy();
});
it("should collect all list and table items in StructTree", async function () {