Merge pull request #21173 from timvandermeij/github-actions-integration-tests-coverage

Collect coverage information for the integration tests
This commit is contained in:
Tim van der Meij 2026-05-14 13:40:05 +02:00 committed by GitHub
commit 056837dace
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 63 additions and 14 deletions

View File

@ -41,6 +41,7 @@ jobs:
skip: --noFirefox
runs-on: ${{ matrix.os }}
environment: code-coverage
steps:
- name: Checkout repository
@ -75,13 +76,13 @@ jobs:
if: ${{ matrix.os == 'windows-latest' }}
run: Set-DisplayResolution -Width 1920 -Height 1080 -Force
- name: Run integration tests (Windows)
- name: Run integration tests with code coverage (Windows)
if: ${{ matrix.os == 'windows-latest' }}
run: npx gulp integrationtest ${{ matrix.skip }}
run: npx gulp integrationtest --coverage --coverage-output build/coverage/integration ${{ matrix.skip }}
- name: Run integration tests (Linux)
- name: Run integration tests with code coverage (Linux)
if: ${{ matrix.os == 'ubuntu-latest' }}
run: xvfb-run -a --server-args="-screen 0, 1920x1080x24" npx gulp integrationtest ${{ matrix.skip }}
run: xvfb-run -a --server-args="-screen 0, 1920x1080x24" npx gulp integrationtest --coverage --coverage-output build/coverage/integration ${{ matrix.skip }}
- name: Save cached PDF files
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
@ -89,3 +90,15 @@ jobs:
path: test/pdfs/*.pdf
key: cached-pdf-files-${{ hashFiles('test/pdfs/*.pdf') }}
enableCrossOsArchive: true
- name: Upload results to Codecov
uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
files: ./build/coverage/integration/lcov.info
flags: integrationtest
name: codecov-umbrella
disable_search: true
disable_telem: true
verbose: true

View File

@ -1036,6 +1036,9 @@ class WorkerMessageHandler {
.getPage(data.pageIndex)
.then(page => page.annotations.map(a => a.toString()));
});
handler.on("GetWorkerCoverage", function () {
return globalThis.__coverage__ ?? {};
});
}
return workerHandlerName;

View File

@ -15,15 +15,15 @@
// Istanbul coverage objects use s (statements), b (branches), and f (functions)
// as shorthand keys for the hit-count maps.
function mergeWorkerCoverageIntoWindow(coverage) {
function mergeCoverageIntoGlobal(coverage) {
if (!coverage || Object.keys(coverage).length === 0) {
return;
}
window.__coverage__ ??= {};
globalThis.__coverage__ ??= {};
for (const [key, fileCoverage] of Object.entries(coverage)) {
const existing = window.__coverage__[key];
const existing = globalThis.__coverage__[key];
if (!existing) {
window.__coverage__[key] = fileCoverage;
globalThis.__coverage__[key] = fileCoverage;
continue;
}
for (const id of Object.keys(fileCoverage.s)) {
@ -49,10 +49,10 @@ async function fetchAndMergeWorkerCoverage(pdfWorker) {
"GetWorkerCoverage",
null
);
mergeWorkerCoverageIntoWindow(coverage);
mergeCoverageIntoGlobal(coverage);
} catch (e) {
console.warn(`Failed to collect worker coverage: ${e}`);
}
}
export { fetchAndMergeWorkerCoverage, mergeWorkerCoverageIntoWindow };
export { fetchAndMergeWorkerCoverage, mergeCoverageIntoGlobal };

View File

@ -13,6 +13,7 @@
* limitations under the License.
*/
import { mergeCoverageIntoGlobal } from "../coverage_utils.js";
import os from "os";
const isMac = os.platform() === "darwin";
@ -149,11 +150,42 @@ function closePages(pages) {
}
async function closeSinglePage(page) {
// Avoid to keep something from a previous test.
await page.evaluate(async () => {
const coverage = await page.evaluate(async () => {
// Collect coverage data from the worker before the document is closed.
let workerCoverage = null;
const handler =
window.PDFViewerApplication.pdfDocument?._transport?.messageHandler;
if (handler) {
try {
workerCoverage = await handler.sendWithPromise(
"GetWorkerCoverage",
null
);
} catch {}
}
// Close the viewer gracefully, and clear local storage to avoid state
// leaking from one test to another.
await window.PDFViewerApplication.testingClose();
window.localStorage.clear();
// Serialize the coverage data to a JSON string because that is a lot
// faster/cheaper to transfer from the browser to Node.js over the WebDriver
// BiDi protocol, otherwise Puppeteer's (significantly slower) serialization
// logic kicks in (see https://github.com/puppeteer/puppeteer/issues/2427).
return {
page: window.__coverage__ ? JSON.stringify(window.__coverage__) : null,
worker: workerCoverage ? JSON.stringify(workerCoverage) : null,
};
});
if (coverage.page) {
mergeCoverageIntoGlobal(JSON.parse(coverage.page));
}
if (coverage.worker) {
mergeCoverageIntoGlobal(JSON.parse(coverage.worker));
}
await page.close({ runBeforeUnload: false });
}

View File

@ -890,6 +890,7 @@ async function startIntegrationTest() {
sessions[0].numRuns = results.runs;
sessions[0].numErrors = results.failures;
sessions[0].failures = results.failureList;
sessions[0].coverage = globalThis.__coverage__;
await Promise.all(sessions.map(session => closeSession(session.name)));
}

View File

@ -42,7 +42,7 @@
import { GlobalWorkerOptions } from "pdfjs/display/worker_options.js";
import { isNodeJS } from "../../src/shared/util.js";
import { mergeWorkerCoverageIntoWindow } from "../coverage_utils.js";
import { mergeCoverageIntoGlobal } from "../coverage_utils.js";
import { MessageHandler } from "pdfjs/shared/message_handler.js";
import { PDFWorker } from "pdfjs/display/api.js";
import { TestReporter } from "../reporter.js";
@ -156,7 +156,7 @@ function installWorkerCoverageHook() {
const handler = new MessageHandler("main", "worker", webWorker);
const promise = handler
.sendWithPromise("GetWorkerCoverage", null)
.then(mergeWorkerCoverageIntoWindow)
.then(mergeCoverageIntoGlobal)
.catch(e => {
console.warn(`Failed to collect worker coverage: ${e}`);
})