Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start web platform tests server before tests run #2057

Merged
merged 1 commit into from Dec 2, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
91 changes: 3 additions & 88 deletions test/web-platform-tests/run-single-wpt.js
@@ -1,95 +1,20 @@
"use strict";
/* eslint-disable no-console, no-process-exit, global-require */
/* eslint-disable no-console */
const fs = require("fs");
const path = require("path");
const dns = require("dns");
const childProcess = require("child_process");
const { EventEmitter } = require("events");
const q = require("q");
const { specify } = require("mocha-sugar-free");
const { inBrowserContext, nodeResolverPromise } = require("../util.js");
const requestHead = require("request-promise-native").head;
const jsdom = require("../../lib/old-api.js");

const wptDir = path.resolve(__dirname, "tests");

const configPaths = {
default: path.resolve(__dirname, "wpt-config.json"),
toUpstream: path.resolve(__dirname, "tuwpt-config.json")
};

const configs = {
default: require(configPaths.default),
toUpstream: require(configPaths.toUpstream)
};

const globalPool = { maxSockets: 6 };

module.exports = ({ toUpstream = false } = {}) => {
module.exports = urlPrefixFactory => {
if (inBrowserContext()) {
return () => {
// TODO: browser support for running WPT
};
}

const configType = toUpstream ? "toUpstream" : "default";
const configPath = configPaths[configType];
const config = configs[configType];

const server = new EventEmitter();

let serverHasStarted;
server.started = new Promise(resolve => {
serverHasStarted = resolve;
});
server.isStarted = false;

let urlPrefix = `http://${config.host}:${config.ports.http[0]}/`;

dns.lookup("web-platform.test", err => {
if (err) {
console.warn();
console.warn("Host entries not present for web platform tests.");
console.warn("See https://github.com/w3c/web-platform-tests#running-the-tests");

if (!toUpstream) {
console.warn("Falling back to hosted versions at w3c-test.org");
urlPrefix = "http://w3c-test.org/";
}
serverHasStarted();
return;
}

const configArg = path.relative(path.resolve(wptDir), configPath);
const args = ["./wpt.py", "serve", "--config", configArg];
const python = childProcess.spawn("python", args, {
cwd: wptDir,
stdio: "inherit"
});

python.on("error", e => {
console.warn();
console.warn("Error starting python server process:", e.message);

if (toUpstream) {
console.error("Cannot proceed with running the tests.");
process.exit(1);
} else {
console.warn("Falling back to hosted versions at w3ctest.org");
urlPrefix = "http://w3c-test.org/";
serverHasStarted();
}
});

pollForServer(() => urlPrefix).then(serverHasStarted);

process.on("exit", () => {
// Python doesn't register a default handler for SIGTERM and it doesn't run __exit__() methods of context managers
// when it gets that signal. Using SIGINT avoids this problem
python.kill("SIGINT");
});
});

return (testPath, title = testPath) => {
specify({
title,
Expand All @@ -99,22 +24,12 @@ module.exports = ({ toUpstream = false } = {}) => {
slow: 10000,
skipIfBrowser: true,
fn() {
return server.started.then(() => createJSDOM(urlPrefix, testPath));
return createJSDOM(urlPrefixFactory(), testPath);
}
});
};
};

function pollForServer(urlGetter) {
console.log("Checking if the web platform tests server is up");
return requestHead(urlGetter())
.then(() => console.log("Server is up!"))
.catch(err => {
console.log(`Server is not up yet (${err.message}); trying again`);
return q.delay(500).then(() => pollForServer(urlGetter));
});
}

function createJSDOM(urlPrefix, testPath) {
const reporterPathname = "/resources/testharnessreport.js";
const unhandledExceptions = [];
Expand Down
12 changes: 10 additions & 2 deletions test/web-platform-tests/run-tuwpts.js
@@ -1,13 +1,13 @@
"use strict";
const path = require("path");
const { describe } = require("mocha-sugar-free");
const { describe, before } = require("mocha-sugar-free");
const { spawnSync } = require("child_process");
const { readManifest, getPossibleTestFilePaths } = require("./wpt-manifest-utils.js");
const startWPTServer = require("./start-wpt-server.js");

const wptPath = path.resolve(__dirname, "tests");
const testsPath = path.resolve(__dirname, "to-upstream");
const manifestFilename = path.resolve(__dirname, "tuwpt-manifest.json");
const runSingleWPT = require("./run-single-wpt.js")({ toUpstream: true });

// We can afford to re-generate the manifest each time; we have few enough files that it's cheap.
const testsRootArg = path.relative(wptPath, testsPath);
Expand All @@ -18,6 +18,14 @@ spawnSync("python", args, { cwd: wptPath, stdio: "inherit" });
const manifest = readManifest(manifestFilename);
const possibleTestFilePaths = getPossibleTestFilePaths(manifest);

let wptServerURL;
const runSingleWPT = require("./run-single-wpt.js")(() => wptServerURL);
before({ timeout: 30 * 1000 }, () => {
return startWPTServer({ toUpstream: true }).then(url => {
wptServerURL = url;
});
});

describe("Local tests in web-platform-test format (to-upstream)", () => {
for (const test of possibleTestFilePaths) {
runSingleWPT(test);
Expand Down
12 changes: 10 additions & 2 deletions test/web-platform-tests/run-wpts.js
Expand Up @@ -3,9 +3,9 @@ const path = require("path");
const fs = require("fs");
const jsYAML = require("js-yaml");
const { Minimatch } = require("minimatch");
const { describe, specify } = require("mocha-sugar-free");
const { describe, specify, before } = require("mocha-sugar-free");
const { readManifest, getPossibleTestFilePaths, stripPrefix } = require("./wpt-manifest-utils.js");
const runSingleWPT = require("./run-single-wpt.js")({ toUpstream: false });
const startWPTServer = require("./start-wpt-server.js");

const validReasons = new Set(["fail", "timeout", "needs-await", "needs-node8"]);

Expand All @@ -30,6 +30,14 @@ const minimatchers = new Map();

checkToRun();

let wptServerURL;
const runSingleWPT = require("./run-single-wpt.js")(() => wptServerURL);
before({ timeout: 30 * 1000 }, () => {
return startWPTServer({ toUpstream: false }).then(url => {
wptServerURL = url;
});
});

describe("web-platform-tests", () => {
for (const toRunDoc of toRunDocs) {
describe(toRunDoc.DIR, () => {
Expand Down
74 changes: 74 additions & 0 deletions test/web-platform-tests/start-wpt-server.js
@@ -0,0 +1,74 @@
"use strict";
/* eslint-disable no-console, global-require */
const path = require("path");
const dns = require("dns");
const childProcess = require("child_process");
const q = require("q");
const { inBrowserContext } = require("../util.js");
const requestHead = require("request-promise-native").head;
const dnsLookup = q.denodeify(dns.lookup);

const wptDir = path.resolve(__dirname, "tests");

const configPaths = {
default: path.resolve(__dirname, "wpt-config.json"),
toUpstream: path.resolve(__dirname, "tuwpt-config.json")
};

const configs = {
default: require(configPaths.default),
toUpstream: require(configPaths.toUpstream)
};

module.exports = ({ toUpstream = false } = {}) => {
if (inBrowserContext()) {
return Promise.resolve();
}

const configType = toUpstream ? "toUpstream" : "default";
const configPath = configPaths[configType];
const config = configs[configType];

const urlPrefix = `http://${config.host}:${config.ports.http[0]}/`;

return dnsLookup("web-platform.test").then(
() => {
const configArg = path.relative(path.resolve(wptDir), configPath);
const args = ["./wpt.py", "serve", "--config", configArg];
const python = childProcess.spawn("python", args, {
cwd: wptDir,
stdio: "inherit"
});

return new Promise((resolve, reject) => {
python.on("error", e => {
reject(new Error("Error starting python server process:", e.message));
});

resolve(pollForServer(urlPrefix));

process.on("exit", () => {
// Python doesn't register a default handler for SIGTERM and it doesn't run __exit__() methods of context
// managers when it gets that signal. Using SIGINT avoids this problem.
python.kill("SIGINT");
});
});
},
() => {
throw new Error("Host entries not present for web platform tests. See " +
"https://github.com/w3c/web-platform-tests#running-the-tests");
}
);
};

function pollForServer(url) {
return requestHead(url)
.then(() => {
console.log(`WPT server at ${url} is up!`);
return url;
})
.catch(err => {
console.log(`WPT server at ${url} is not up yet (${err.message}); trying again`);
return q.delay(500).then(() => pollForServer(url));
});
}