12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109 |
- /*
- * Copyright 2014 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /* eslint-disable no-var, unicorn/prefer-at */
- "use strict";
- var WebServer = require("./webserver.js").WebServer;
- var path = require("path");
- var fs = require("fs");
- var os = require("os");
- var puppeteer = require("puppeteer");
- var url = require("url");
- var testUtils = require("./testutils.js");
- const dns = require("dns");
- const readline = require("readline");
- const yargs = require("yargs");
- // Chrome uses host `127.0.0.1` in the browser's websocket endpoint URL while
- // Firefox uses `localhost`, which before Node.js 17 also resolved to the IPv4
- // address `127.0.0.1` by Node.js' DNS resolver. However, this behavior changed
- // in Node.js 17 where the default is to prefer an IPv6 address if one is
- // offered (which varies based on the OS and/or how the `localhost` hostname
- // resolution is configured), so it can now also resolve to `::1`. This causes
- // Firefox to not start anymore since it doesn't bind on the `::1` interface.
- // To avoid this, we switch Node.js' DNS resolver back to preferring IPv4
- // since we connect to a local browser anyway. Only do this for Node.js versions
- // that actually have this API since it got introduced in Node.js 14.18.0 and
- // it's not relevant for older versions anyway.
- if (dns.setDefaultResultOrder !== undefined) {
- dns.setDefaultResultOrder("ipv4first");
- }
- function parseOptions() {
- yargs
- .usage("Usage: $0")
- .option("downloadOnly", {
- default: false,
- describe: "Download test PDFs without running the tests.",
- type: "boolean",
- })
- .option("fontTest", {
- default: false,
- describe: "Run the font tests.",
- type: "boolean",
- })
- .option("help", {
- alias: "h",
- default: false,
- describe: "Show this help message.",
- type: "boolean",
- })
- .option("integration", {
- default: false,
- describe: "Run the integration tests.",
- type: "boolean",
- })
- .option("manifestFile", {
- default: "test_manifest.json",
- describe: "A path to JSON file in the form of `test_manifest.json`.",
- type: "string",
- })
- .option("masterMode", {
- alias: "m",
- default: false,
- describe: "Run the script in master mode.",
- type: "boolean",
- })
- .option("noChrome", {
- default: false,
- describe: "Skip Chrome when running tests.",
- type: "boolean",
- })
- .option("noDownload", {
- default: false,
- describe: "Skip downloading of test PDFs.",
- type: "boolean",
- })
- .option("noPrompts", {
- default: false,
- describe: "Uses default answers (intended for CLOUD TESTS only!).",
- type: "boolean",
- })
- .option("port", {
- default: 0,
- describe: "The port the HTTP server should listen on.",
- type: "number",
- })
- .option("reftest", {
- default: false,
- describe:
- "Automatically start reftest showing comparison test failures, if there are any.",
- type: "boolean",
- })
- .option("statsDelay", {
- default: 0,
- describe:
- "The amount of time in milliseconds the browser should wait before starting stats.",
- type: "number",
- })
- .option("statsFile", {
- default: "",
- describe: "The file where to store stats.",
- type: "string",
- })
- .option("strictVerify", {
- default: false,
- describe: "Error if verifying the manifest files fails.",
- type: "boolean",
- })
- .option("testfilter", {
- alias: "t",
- default: [],
- describe: "Run specific reftest(s).",
- type: "array",
- })
- .example(
- "testfilter",
- "$0 -t=issue5567 -t=issue5909\n" +
- "Run the reftest identified by issue5567 and issue5909."
- )
- .option("unitTest", {
- default: false,
- describe: "Run the unit tests.",
- type: "boolean",
- })
- .option("xfaOnly", {
- default: false,
- describe: "Only run the XFA reftest(s).",
- type: "boolean",
- })
- .check(argv => {
- if (
- +argv.reftest + argv.unitTest + argv.fontTest + argv.masterMode <=
- 1
- ) {
- return true;
- }
- throw new Error(
- "--reftest, --unitTest, --fontTest, and --masterMode must not be specified together."
- );
- })
- .check(argv => {
- if (
- +argv.unitTest + argv.fontTest + argv.integration + argv.xfaOnly <=
- 1
- ) {
- return true;
- }
- throw new Error(
- "--unitTest, --fontTest, --integration, and --xfaOnly must not be specified together."
- );
- })
- .check(argv => {
- if (argv.testfilter && argv.testfilter.length > 0 && argv.xfaOnly) {
- throw new Error("--testfilter and --xfaOnly cannot be used together.");
- }
- return true;
- })
- .check(argv => {
- if (!argv.noDownload || !argv.downloadOnly) {
- return true;
- }
- throw new Error(
- "--noDownload and --downloadOnly cannot be used together."
- );
- })
- .check(argv => {
- if (!argv.masterMode || argv.manifestFile === "test_manifest.json") {
- return true;
- }
- throw new Error(
- "when --masterMode is specified --manifestFile shall be equal to `test_manifest.json`."
- );
- });
- const result = yargs.argv;
- if (result.help) {
- yargs.showHelp();
- process.exit(0);
- }
- result.testfilter = Array.isArray(result.testfilter)
- ? result.testfilter
- : [result.testfilter];
- return result;
- }
- var refsTmpDir = "tmp";
- var testResultDir = "test_snapshots";
- var refsDir = "ref";
- var eqLog = "eq.log";
- var browserTimeout = 120;
- function monitorBrowserTimeout(session, onTimeout) {
- if (session.timeoutMonitor) {
- clearTimeout(session.timeoutMonitor);
- }
- if (!onTimeout) {
- session.timeoutMonitor = null;
- return;
- }
- session.timeoutMonitor = setTimeout(function () {
- onTimeout(session);
- }, browserTimeout * 1000);
- }
- function updateRefImages() {
- function sync(removeTmp) {
- console.log(" Updating ref/ ... ");
- testUtils.copySubtreeSync(refsTmpDir, refsDir);
- if (removeTmp) {
- testUtils.removeDirSync(refsTmpDir);
- }
- console.log("done");
- }
- if (options.noPrompts) {
- sync(false); // don't remove tmp/ for botio
- return;
- }
- const reader = readline.createInterface(process.stdin, process.stdout);
- reader.question(
- "Would you like to update the master copy in ref/? [yn] ",
- function (answer) {
- if (answer.toLowerCase() === "y") {
- sync(true);
- } else {
- console.log(" OK, not updating.");
- }
- reader.close();
- }
- );
- }
- function examineRefImages() {
- startServer();
- const startUrl = `http://${host}:${server.port}/test/resources/reftest-analyzer.html#web=/test/eq.log`;
- startBrowser("firefox", startUrl).then(function (browser) {
- browser.on("disconnected", function () {
- stopServer();
- process.exit(0);
- });
- });
- }
- function startRefTest(masterMode, showRefImages) {
- function finalize() {
- stopServer();
- var numErrors = 0;
- var numFBFFailures = 0;
- var numEqFailures = 0;
- var numEqNoSnapshot = 0;
- sessions.forEach(function (session) {
- numErrors += session.numErrors;
- numFBFFailures += session.numFBFFailures;
- numEqFailures += session.numEqFailures;
- numEqNoSnapshot += session.numEqNoSnapshot;
- });
- var numFatalFailures = numErrors + numFBFFailures;
- console.log();
- if (numFatalFailures + numEqFailures > 0) {
- console.log("OHNOES! Some tests failed!");
- if (numErrors > 0) {
- console.log(" errors: " + numErrors);
- }
- if (numEqFailures > 0) {
- console.log(" different ref/snapshot: " + numEqFailures);
- }
- if (numFBFFailures > 0) {
- console.log(" different first/second rendering: " + numFBFFailures);
- }
- } else {
- console.log("All regression tests passed.");
- }
- var runtime = (Date.now() - startTime) / 1000;
- console.log("Runtime was " + runtime.toFixed(1) + " seconds");
- if (options.statsFile) {
- fs.writeFileSync(options.statsFile, JSON.stringify(stats, null, 2));
- }
- if (masterMode) {
- if (numEqFailures + numEqNoSnapshot > 0) {
- console.log();
- console.log("Some eq tests failed or didn't have snapshots.");
- console.log("Checking to see if master references can be updated...");
- if (numFatalFailures > 0) {
- console.log(" No. Some non-eq tests failed.");
- } else {
- console.log(
- " Yes! The references in tmp/ can be synced with ref/."
- );
- updateRefImages();
- }
- }
- } else if (showRefImages && numEqFailures > 0) {
- console.log();
- console.log(
- `Starting reftest harness to examine ${numEqFailures} eq test failures.`
- );
- examineRefImages();
- }
- }
- function setup() {
- if (fs.existsSync(refsTmpDir)) {
- console.error("tmp/ exists -- unable to proceed with testing");
- process.exit(1);
- }
- if (fs.existsSync(eqLog)) {
- fs.unlinkSync(eqLog);
- }
- if (fs.existsSync(testResultDir)) {
- testUtils.removeDirSync(testResultDir);
- }
- startTime = Date.now();
- startServer();
- server.hooks.POST.push(refTestPostHandler);
- onAllSessionsClosed = finalize;
- const startUrl = `http://${host}:${server.port}/test/test_slave.html`;
- startBrowsers(function (session) {
- session.masterMode = masterMode;
- session.taskResults = {};
- session.tasks = {};
- session.remaining = manifest.length;
- manifest.forEach(function (item) {
- var rounds = item.rounds || 1;
- var roundsResults = [];
- roundsResults.length = rounds;
- session.taskResults[item.id] = roundsResults;
- session.tasks[item.id] = item;
- });
- session.numErrors = 0;
- session.numFBFFailures = 0;
- session.numEqNoSnapshot = 0;
- session.numEqFailures = 0;
- monitorBrowserTimeout(session, handleSessionTimeout);
- }, makeTestUrl(startUrl));
- }
- function checkRefsTmp() {
- if (masterMode && fs.existsSync(refsTmpDir)) {
- if (options.noPrompts) {
- testUtils.removeDirSync(refsTmpDir);
- setup();
- return;
- }
- console.log("Temporary snapshot dir tmp/ is still around.");
- console.log("tmp/ can be removed if it has nothing you need.");
- const reader = readline.createInterface(process.stdin, process.stdout);
- reader.question(
- "SHOULD THIS SCRIPT REMOVE tmp/? THINK CAREFULLY [yn] ",
- function (answer) {
- if (answer.toLowerCase() === "y") {
- testUtils.removeDirSync(refsTmpDir);
- }
- setup();
- reader.close();
- }
- );
- } else {
- setup();
- }
- }
- var startTime;
- var manifest = getTestManifest();
- if (!manifest) {
- return;
- }
- if (options.noDownload) {
- checkRefsTmp();
- } else {
- ensurePDFsDownloaded(checkRefsTmp);
- }
- }
- function handleSessionTimeout(session) {
- if (session.closed) {
- return;
- }
- var browser = session.name;
- console.log(
- "TEST-UNEXPECTED-FAIL | test failed " +
- browser +
- " has not responded in " +
- browserTimeout +
- "s"
- );
- session.numErrors += session.remaining;
- session.remaining = 0;
- closeSession(browser);
- }
- function getTestManifest() {
- var manifest = JSON.parse(fs.readFileSync(options.manifestFile));
- const testFilter = options.testfilter.slice(0),
- xfaOnly = options.xfaOnly;
- if (testFilter.length || xfaOnly) {
- manifest = manifest.filter(function (item) {
- var i = testFilter.indexOf(item.id);
- if (i !== -1) {
- testFilter.splice(i, 1);
- return true;
- }
- if (xfaOnly && item.enableXfa) {
- return true;
- }
- return false;
- });
- if (testFilter.length) {
- console.error("Unrecognized test IDs: " + testFilter.join(" "));
- return undefined;
- }
- }
- return manifest;
- }
- function checkEq(task, results, browser, masterMode) {
- var taskId = task.id;
- var refSnapshotDir = path.join(refsDir, os.platform(), browser, taskId);
- var testSnapshotDir = path.join(
- testResultDir,
- os.platform(),
- browser,
- taskId
- );
- var pageResults = results[0];
- var taskType = task.type;
- var numEqNoSnapshot = 0;
- var numEqFailures = 0;
- for (var page = 0; page < pageResults.length; page++) {
- if (!pageResults[page]) {
- continue;
- }
- const pageResult = pageResults[page];
- let testSnapshot = pageResult.snapshot;
- if (testSnapshot && testSnapshot.startsWith("data:image/png;base64,")) {
- testSnapshot = Buffer.from(testSnapshot.substring(22), "base64");
- } else {
- console.error("Valid snapshot was not found.");
- }
- var refSnapshot = null;
- var eq = false;
- var refPath = path.join(refSnapshotDir, page + 1 + ".png");
- if (!fs.existsSync(refPath)) {
- numEqNoSnapshot++;
- if (!masterMode) {
- console.log("WARNING: no reference snapshot " + refPath);
- }
- } else {
- refSnapshot = fs.readFileSync(refPath);
- eq = refSnapshot.toString("hex") === testSnapshot.toString("hex");
- if (!eq) {
- console.log(
- "TEST-UNEXPECTED-FAIL | " +
- taskType +
- " " +
- taskId +
- " | in " +
- browser +
- " | rendering of page " +
- (page + 1) +
- " != reference rendering"
- );
- testUtils.ensureDirSync(testSnapshotDir);
- fs.writeFileSync(
- path.join(testSnapshotDir, page + 1 + ".png"),
- testSnapshot
- );
- fs.writeFileSync(
- path.join(testSnapshotDir, page + 1 + "_ref.png"),
- refSnapshot
- );
- // This no longer follows the format of Mozilla reftest output.
- const viewportString = `(${pageResult.viewportWidth}x${pageResult.viewportHeight}x${pageResult.outputScale})`;
- fs.appendFileSync(
- eqLog,
- "REFTEST TEST-UNEXPECTED-FAIL | " +
- browser +
- "-" +
- taskId +
- "-page" +
- (page + 1) +
- " | image comparison (==)\n" +
- `REFTEST IMAGE 1 (TEST)${viewportString}: ` +
- path.join(testSnapshotDir, page + 1 + ".png") +
- "\n" +
- `REFTEST IMAGE 2 (REFERENCE)${viewportString}: ` +
- path.join(testSnapshotDir, page + 1 + "_ref.png") +
- "\n"
- );
- numEqFailures++;
- }
- }
- if (masterMode && (!refSnapshot || !eq)) {
- var tmpSnapshotDir = path.join(
- refsTmpDir,
- os.platform(),
- browser,
- taskId
- );
- testUtils.ensureDirSync(tmpSnapshotDir);
- fs.writeFileSync(
- path.join(tmpSnapshotDir, page + 1 + ".png"),
- testSnapshot
- );
- }
- }
- var session = getSession(browser);
- session.numEqNoSnapshot += numEqNoSnapshot;
- if (numEqFailures > 0) {
- session.numEqFailures += numEqFailures;
- } else {
- console.log(
- "TEST-PASS | " + taskType + " test " + taskId + " | in " + browser
- );
- }
- }
- function checkFBF(task, results, browser, masterMode) {
- var numFBFFailures = 0;
- var round0 = results[0],
- round1 = results[1];
- if (round0.length !== round1.length) {
- console.error("round 1 and 2 sizes are different");
- }
- for (var page = 0; page < round1.length; page++) {
- var r0Page = round0[page],
- r1Page = round1[page];
- if (!r0Page) {
- continue;
- }
- if (r0Page.snapshot !== r1Page.snapshot) {
- // The FBF tests fail intermittently in Firefox and Google Chrome when run
- // on the bots, ignoring `makeref` failures for now; see
- // - https://github.com/mozilla/pdf.js/pull/12368
- // - https://github.com/mozilla/pdf.js/pull/11491
- //
- // TODO: Figure out why this happens, so that we can remove the hack; see
- // https://github.com/mozilla/pdf.js/issues/12371
- if (masterMode) {
- console.log(
- "TEST-SKIPPED | forward-back-forward test " +
- task.id +
- " | in " +
- browser +
- " | page" +
- (page + 1)
- );
- continue;
- }
- console.log(
- "TEST-UNEXPECTED-FAIL | forward-back-forward test " +
- task.id +
- " | in " +
- browser +
- " | first rendering of page " +
- (page + 1) +
- " != second"
- );
- numFBFFailures++;
- }
- }
- if (numFBFFailures > 0) {
- getSession(browser).numFBFFailures += numFBFFailures;
- } else {
- console.log(
- "TEST-PASS | forward-back-forward test " + task.id + " | in " + browser
- );
- }
- }
- function checkLoad(task, results, browser) {
- // Load just checks for absence of failure, so if we got here the
- // test has passed
- console.log("TEST-PASS | load test " + task.id + " | in " + browser);
- }
- function checkRefTestResults(browser, id, results) {
- var failed = false;
- var session = getSession(browser);
- var task = session.tasks[id];
- results.forEach(function (roundResults, round) {
- roundResults.forEach(function (pageResult, page) {
- if (!pageResult) {
- return; // no results
- }
- if (pageResult.failure) {
- failed = true;
- if (fs.existsSync(task.file + ".error")) {
- console.log(
- "TEST-SKIPPED | PDF was not downloaded " +
- id +
- " | in " +
- browser +
- " | page" +
- (page + 1) +
- " round " +
- (round + 1) +
- " | " +
- pageResult.failure
- );
- } else {
- session.numErrors++;
- console.log(
- "TEST-UNEXPECTED-FAIL | test failed " +
- id +
- " | in " +
- browser +
- " | page" +
- (page + 1) +
- " round " +
- (round + 1) +
- " | " +
- pageResult.failure
- );
- }
- }
- });
- });
- if (failed) {
- return;
- }
- switch (task.type) {
- case "eq":
- case "text":
- checkEq(task, results, browser, session.masterMode);
- break;
- case "fbf":
- checkFBF(task, results, browser, session.masterMode);
- break;
- case "load":
- checkLoad(task, results, browser);
- break;
- default:
- throw new Error("Unknown test type");
- }
- // clear memory
- results.forEach(function (roundResults, round) {
- roundResults.forEach(function (pageResult, page) {
- pageResult.snapshot = null;
- });
- });
- }
- function refTestPostHandler(req, res) {
- var parsedUrl = url.parse(req.url, true);
- var pathname = parsedUrl.pathname;
- if (
- pathname !== "/tellMeToQuit" &&
- pathname !== "/info" &&
- pathname !== "/submit_task_results"
- ) {
- return false;
- }
- var body = "";
- req.on("data", function (data) {
- body += data;
- });
- req.on("end", function () {
- res.writeHead(200, { "Content-Type": "text/plain" });
- res.end();
- var session;
- if (pathname === "/tellMeToQuit") {
- session = getSession(parsedUrl.query.browser);
- monitorBrowserTimeout(session, null);
- closeSession(session.name);
- return;
- }
- var data = JSON.parse(body);
- if (pathname === "/info") {
- console.log(data.message);
- return;
- }
- var browser = data.browser;
- var round = data.round;
- var id = data.id;
- var page = data.page - 1;
- var failure = data.failure;
- var snapshot = data.snapshot;
- var lastPageNum = data.lastPageNum;
- session = getSession(browser);
- monitorBrowserTimeout(session, handleSessionTimeout);
- var taskResults = session.taskResults[id];
- if (!taskResults[round]) {
- taskResults[round] = [];
- }
- if (taskResults[round][page]) {
- console.error(
- "Results for " +
- browser +
- ":" +
- id +
- ":" +
- round +
- ":" +
- page +
- " were already submitted"
- );
- // TODO abort testing here?
- }
- taskResults[round][page] = {
- failure,
- snapshot,
- viewportWidth: data.viewportWidth,
- viewportHeight: data.viewportHeight,
- outputScale: data.outputScale,
- };
- if (stats) {
- stats.push({
- browser,
- pdf: id,
- page,
- round,
- stats: data.stats,
- });
- }
- var isDone =
- taskResults[taskResults.length - 1] &&
- taskResults[taskResults.length - 1][lastPageNum - 1];
- if (isDone) {
- checkRefTestResults(browser, id, taskResults);
- session.remaining--;
- }
- });
- return true;
- }
- function onAllSessionsClosedAfterTests(name) {
- const startTime = Date.now();
- return function () {
- stopServer();
- var numRuns = 0,
- numErrors = 0;
- sessions.forEach(function (session) {
- numRuns += session.numRuns;
- numErrors += session.numErrors;
- });
- console.log();
- console.log("Run " + numRuns + " tests");
- if (numErrors > 0) {
- console.log("OHNOES! Some " + name + " tests failed!");
- console.log(" " + numErrors + " of " + numRuns + " failed");
- } else {
- console.log("All " + name + " tests passed.");
- }
- var runtime = (Date.now() - startTime) / 1000;
- console.log(name + " tests runtime was " + runtime.toFixed(1) + " seconds");
- };
- }
- function makeTestUrl(startUrl) {
- return function (browserName) {
- const queryParameters =
- `?browser=${encodeURIComponent(browserName)}` +
- `&manifestFile=${encodeURIComponent("/test/" + options.manifestFile)}` +
- `&testFilter=${JSON.stringify(options.testfilter)}` +
- `&xfaOnly=${options.xfaOnly}` +
- `&delay=${options.statsDelay}` +
- `&masterMode=${options.masterMode}`;
- return startUrl + queryParameters;
- };
- }
- function startUnitTest(testUrl, name) {
- onAllSessionsClosed = onAllSessionsClosedAfterTests(name);
- startServer();
- server.hooks.POST.push(unitTestPostHandler);
- const startUrl = `http://${host}:${server.port}${testUrl}`;
- startBrowsers(function (session) {
- session.numRuns = 0;
- session.numErrors = 0;
- }, makeTestUrl(startUrl));
- }
- function startIntegrationTest() {
- onAllSessionsClosed = onAllSessionsClosedAfterTests("integration");
- startServer();
- const { runTests } = require("./integration-boot.js");
- startBrowsers(function (session) {
- session.numRuns = 0;
- session.numErrors = 0;
- });
- global.integrationBaseUrl = `http://${host}:${server.port}/build/generic/web/viewer.html`;
- global.integrationSessions = sessions;
- Promise.all(sessions.map(session => session.browserPromise)).then(
- async () => {
- const results = { runs: 0, failures: 0 };
- await runTests(results);
- sessions[0].numRuns = results.runs;
- sessions[0].numErrors = results.failures;
- await Promise.all(sessions.map(session => closeSession(session.name)));
- }
- );
- }
- function unitTestPostHandler(req, res) {
- var parsedUrl = url.parse(req.url);
- var pathname = parsedUrl.pathname;
- if (
- pathname !== "/tellMeToQuit" &&
- pathname !== "/info" &&
- pathname !== "/ttx" &&
- pathname !== "/submit_task_results"
- ) {
- return false;
- }
- var body = "";
- req.on("data", function (data) {
- body += data;
- });
- req.on("end", function () {
- if (pathname === "/ttx") {
- var translateFont = require("./font/ttxdriver.js").translateFont;
- var onCancel = null,
- ttxTimeout = 10000;
- var timeoutId = setTimeout(function () {
- onCancel?.("TTX timeout");
- }, ttxTimeout);
- translateFont(
- body,
- function (fn) {
- onCancel = fn;
- },
- function (err, xml) {
- clearTimeout(timeoutId);
- res.writeHead(200, { "Content-Type": "text/xml" });
- res.end(err ? "<error>" + err + "</error>" : xml);
- }
- );
- return;
- }
- res.writeHead(200, { "Content-Type": "text/plain" });
- res.end();
- var data = JSON.parse(body);
- if (pathname === "/tellMeToQuit") {
- closeSession(data.browser);
- return;
- }
- if (pathname === "/info") {
- console.log(data.message);
- return;
- }
- var session = getSession(data.browser);
- session.numRuns++;
- var message =
- data.status + " | " + data.description + " | in " + session.name;
- if (data.status === "TEST-UNEXPECTED-FAIL") {
- session.numErrors++;
- }
- if (data.error) {
- message += " | " + data.error;
- }
- console.log(message);
- });
- return true;
- }
- async function startBrowser(browserName, startUrl = "") {
- const revisions =
- require("puppeteer-core/lib/cjs/puppeteer/revisions.js").PUPPETEER_REVISIONS;
- const wantedRevision =
- browserName === "chrome" ? revisions.chromium : revisions.firefox;
- // Remove other revisions than the one we want to use. Updating Puppeteer can
- // cause a new revision to be used, and not removing older revisions causes
- // the disk to fill up.
- const browserFetcher = puppeteer.createBrowserFetcher({
- product: browserName,
- });
- const localRevisions = await browserFetcher.localRevisions();
- if (localRevisions.length > 1) {
- for (const localRevision of localRevisions) {
- if (localRevision !== wantedRevision) {
- console.log(`Removing old ${browserName} revision ${localRevision}...`);
- await browserFetcher.remove(localRevision);
- }
- }
- }
- const options = {
- product: browserName,
- headless: false,
- defaultViewport: null,
- ignoreDefaultArgs: ["--disable-extensions"],
- };
- if (!tempDir) {
- tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "pdfjs-"));
- }
- const printFile = path.join(tempDir, "print.pdf");
- if (browserName === "chrome") {
- // avoid crash
- options.args = ["--no-sandbox", "--disable-setuid-sandbox"];
- // silent printing in a pdf
- options.args.push("--kiosk-printing");
- }
- if (browserName === "firefox") {
- options.extraPrefsFirefox = {
- // avoid to have a prompt when leaving a page with a form
- "dom.disable_beforeunload": true,
- // Disable dialog when saving a pdf
- "pdfjs.disabled": true,
- "browser.helperApps.neverAsk.saveToDisk": "application/pdf",
- // Avoid popup when saving is done
- "browser.download.always_ask_before_handling_new_types": true,
- "browser.download.panel.shown": true,
- "browser.download.alwaysOpenPanel": false,
- // Save file in output
- "browser.download.folderList": 2,
- "browser.download.dir": tempDir,
- // Print silently in a pdf
- "print.always_print_silent": true,
- "print.show_print_progress": false,
- print_printer: "PDF",
- "print.printer_PDF.print_to_file": true,
- "print.printer_PDF.print_to_filename": printFile,
- // Enable OffscreenCanvas
- "gfx.offscreencanvas.enabled": true,
- // Disable gpu acceleration
- "gfx.canvas.accelerated": false,
- };
- }
- const browser = await puppeteer.launch(options);
- if (startUrl) {
- const pages = await browser.pages();
- const page = pages[0];
- await page.goto(startUrl, { timeout: 0, waitUntil: "domcontentloaded" });
- }
- return browser;
- }
- function startBrowsers(initSessionCallback, makeStartUrl = null) {
- const browserNames = options.noChrome ? ["firefox"] : ["firefox", "chrome"];
- sessions = [];
- for (const browserName of browserNames) {
- // The session must be pushed first and augmented with the browser once
- // it's initialized. The reason for this is that browser initialization
- // takes more time when the browser is not found locally yet and we don't
- // want `onAllSessionsClosed` to trigger if one of the browsers is done
- // and the other one is still initializing, since that would mean that
- // once the browser is initialized the server would have stopped already.
- // Pushing the session first ensures that `onAllSessionsClosed` will
- // only trigger once all browsers are initialized and done.
- const session = {
- name: browserName,
- browser: undefined,
- closed: false,
- };
- sessions.push(session);
- const startUrl = makeStartUrl ? makeStartUrl(browserName) : "";
- session.browserPromise = startBrowser(browserName, startUrl)
- .then(function (browser) {
- session.browser = browser;
- initSessionCallback?.(session);
- })
- .catch(function (ex) {
- console.log(`Error while starting ${browserName}: ${ex.message}`);
- closeSession(browserName);
- });
- }
- }
- function startServer() {
- server = new WebServer();
- server.host = host;
- server.port = options.port;
- server.root = "..";
- server.cacheExpirationTime = 3600;
- server.start();
- }
- function stopServer() {
- server.stop();
- }
- function getSession(browser) {
- return sessions.find(session => session.name === browser);
- }
- async function closeSession(browser) {
- for (const session of sessions) {
- if (session.name !== browser) {
- continue;
- }
- if (session.browser !== undefined) {
- for (const page of await session.browser.pages()) {
- await page.close();
- }
- await session.browser.close();
- }
- session.closed = true;
- const allClosed = sessions.every(function (s) {
- return s.closed;
- });
- if (allClosed) {
- if (tempDir) {
- const rimraf = require("rimraf");
- rimraf.sync(tempDir);
- }
- onAllSessionsClosed?.();
- }
- }
- }
- function ensurePDFsDownloaded(callback) {
- var downloadUtils = require("./downloadutils.js");
- var manifest = getTestManifest();
- downloadUtils.downloadManifestFiles(manifest, function () {
- downloadUtils.verifyManifestFiles(manifest, function (hasErrors) {
- if (hasErrors) {
- console.log(
- "Unable to verify the checksum for the files that are " +
- "used for testing."
- );
- console.log(
- "Please re-download the files, or adjust the MD5 " +
- "checksum in the manifest for the files listed above.\n"
- );
- if (options.strictVerify) {
- process.exit(1);
- }
- }
- callback();
- });
- });
- }
- function main() {
- if (options.statsFile) {
- stats = [];
- }
- if (options.downloadOnly) {
- ensurePDFsDownloaded(function () {});
- } else if (options.unitTest) {
- // Allows linked PDF files in unit-tests as well.
- ensurePDFsDownloaded(function () {
- startUnitTest("/test/unit/unit_test.html", "unit");
- });
- } else if (options.fontTest) {
- startUnitTest("/test/font/font_test.html", "font");
- } else if (options.integration) {
- // Allows linked PDF files in integration-tests as well.
- ensurePDFsDownloaded(function () {
- startIntegrationTest();
- });
- } else {
- startRefTest(options.masterMode, options.reftest);
- }
- }
- var server;
- var sessions;
- var onAllSessionsClosed;
- var host = "127.0.0.1";
- var options = parseOptions();
- var stats;
- var tempDir = null;
- main();
|