From 038ca33f8e91569ddff3dd22f8ca94043d1f7109 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Fri, 1 May 2026 15:56:22 +0200 Subject: [PATCH] Optimize runtime of the find controller unit tests The find controller tests consistently show up in the list of slowest tests reported by Jasmine. Profiling shows that most of the time is spent waiting for the find results to arrive, even though the find command itself is quite fast. It turns out that the slowdown occurs between receiving the `find` event and actually triggering the search. The find controller has a hardcoded delay of 250 milliseconds built in, which was introduced for viewer performance many years ago because otherwise every keystroke would trigger a search even though the user's query was not complete yet. For the unit tests we don't need this delay because, contrary to the viewer use case, we don't have to account for user interaction and instead dispatch complete `find` events on the event bus ourselves. However, since the unit tests were introduced well over a year after the delay was introduced, due to an oversight it was never made configurable so we could skip it for the unit tests. This commit fixes the issue, which locally results in the runtime of `npx gulp unittest --noChrome` dropping from 39.991 seconds before this patch to 29.116 seconds afterwards, which is a 27% speedup. --- test/unit/pdf_find_controller_spec.js | 5 +++++ web/pdf_find_controller.js | 18 ++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/test/unit/pdf_find_controller_spec.js b/test/unit/pdf_find_controller_spec.js index bf325623b..47495cade 100644 --- a/test/unit/pdf_find_controller_spec.js +++ b/test/unit/pdf_find_controller_spec.js @@ -72,10 +72,15 @@ async function initPdfFindController( FindControllerClass.prototype.match = matcher; } + // Note that in the unit tests we dispatch the `find` event with the complete + // query ourselves, so we can safely disable the delay option as there is no + // user interaction involved for typing the query (for which the delay option + // is intended), which provides a significant reduction in unit test runtime. const pdfFindController = new FindControllerClass({ linkService, eventBus, updateMatchesCountOnProgress, + delay: 0, }); pdfFindController.setDocument(pdfDocument); // Enable searching. diff --git a/web/pdf_find_controller.js b/web/pdf_find_controller.js index 481b0aa8d..762929a11 100644 --- a/web/pdf_find_controller.js +++ b/web/pdf_find_controller.js @@ -27,8 +27,6 @@ const FindState = { PENDING: 3, }; -const FIND_TIMEOUT = 250; // ms - const CHARACTERS_TO_NORMALIZE = { "\u2010": "-", // Hyphen "\u2018": "'", // Left single quotation mark @@ -406,6 +404,10 @@ function getOriginalIndex(diffs, pos, len) { * @typedef {Object} PDFFindControllerOptions * @property {PDFLinkService} linkService - The navigation/linking service. * @property {EventBus} eventBus - The application event bus. + * @property {number} [delay] - The number of milliseconds to delay execution of + * find commands. In the viewer each keystroke in the find bar triggers a + * `find` event, so this delay avoids triggering a search prematurely when the + * user is still typing the query. The default value is 250. * @property {boolean} [updateMatchesCountOnProgress] - True if the matches * count must be updated on progress or only when the last page is reached. * The default value is `true`. @@ -419,6 +421,8 @@ class PDFFindController { #updateMatchesCountOnProgress = true; + #delay = 0; + #visitedPagesCount = 0; #copiedPageData = null; @@ -428,10 +432,16 @@ class PDFFindController { /** * @param {PDFFindControllerOptions} options */ - constructor({ linkService, eventBus, updateMatchesCountOnProgress = true }) { + constructor({ + linkService, + eventBus, + delay = 250, + updateMatchesCountOnProgress = true, + }) { this._linkService = linkService; this._eventBus = eventBus; this.#updateMatchesCountOnProgress = updateMatchesCountOnProgress; + this.#delay = delay; /** * Callback used to check if a `pageNumber` is currently visible. @@ -521,7 +531,7 @@ class PDFFindController { this._findTimeout = setTimeout(() => { this.#nextMatch(); this._findTimeout = null; - }, FIND_TIMEOUT); + }, this.#delay); } else if (this._dirtyMatch) { // Immediately trigger searching for non-'find' operations, when the // current state needs to be reset and matches re-calculated.