mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
JS: remove old node-based test runner, use new JUnit browser runner in travis
This commit is contained in:
parent
bc9ad315ff
commit
707d11d9d8
24
.travis.yml
24
.travis.yml
|
@ -20,7 +20,6 @@ branches:
|
|||
|
||||
before_install:
|
||||
- export MVN_CMD="mvn -Dmaven.repo.local=$HOME/mvn_repo -B"
|
||||
- nvm install 7
|
||||
- OLD_VERSION=`$MVN_CMD help:evaluate -Dexpression=project.version 2>/dev/null | grep -Ev "(^\[|Download)"`
|
||||
- BASE_VERSION=${OLD_VERSION%-SNAPSHOT}
|
||||
- NEW_VERSION=${BASE_VERSION}-dev-`printf %d $TRAVIS_BUILD_NUMBER`
|
||||
|
@ -34,29 +33,8 @@ before_install:
|
|||
- rm -rf $HOME/.rvm
|
||||
- rm -rf $HOME/.m2
|
||||
|
||||
install:
|
||||
- pushd tests/src/test/js
|
||||
- npm config set prefix=$HOME/.node_modules
|
||||
- npm install
|
||||
- npm run build
|
||||
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 index.html &
|
||||
- BROWSER_PID=$!
|
||||
- node start.js test
|
||||
- node ./bin/test-chrome.js
|
||||
- kill $BROWSER_PID
|
||||
- popd
|
||||
- rm -rf tools/idea/idea-artifacts/dependencies
|
||||
|
||||
script:
|
||||
- $MVN_CMD -e install -Dteavm.junit.optimized=false -Dteavm.junit.js.decodeStack=false -P with-idea -P with-cli -Dteavm.junit.js.runner=none -V
|
||||
- BASE_PATH=`pwd`
|
||||
- pushd tests/src/test/js
|
||||
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 index.html &
|
||||
- BROWSER_PID=$!
|
||||
- node start.js $BASE_PATH/tests/target/js-tests
|
||||
- node start.js $BASE_PATH/html4j/target/js-tests
|
||||
- kill $BROWSER_PID
|
||||
- popd
|
||||
- $MVN_CMD -e install -Dteavm.junit.optimized=false -Dteavm.junit.js.decodeStack=false -P with-idea -P with-cli -Dteavm.junit.threads=2 -Dteavm.junit.js.runner=browser-chrome -V
|
||||
- rm -rf $BASE_PATH/tests/target/js-tests
|
||||
- rm -rf $BASE_PATH/html4j/target/js-tests
|
||||
- du -sh $HOME/.[!.]* /home/travis/* | sort -h
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"presets": [["env", {
|
||||
"targets": {
|
||||
"node": "current"
|
||||
}
|
||||
}]]
|
||||
}
|
4
tests/src/test/js/.gitignore
vendored
4
tests/src/test/js/.gitignore
vendored
|
@ -1,4 +0,0 @@
|
|||
/node_modules/
|
||||
/bin/
|
||||
/tmp/
|
||||
package-lock.json
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
function tryConnect() {
|
||||
let ws = new WebSocket("ws://localhost:9090");
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log("Connection established");
|
||||
listen(ws);
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
ws.close();
|
||||
setTimeout(() => {
|
||||
tryConnect();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
ws.onerror = err => {
|
||||
console.log("Could not connect WebSocket", err);
|
||||
}
|
||||
}
|
||||
|
||||
function listen(ws) {
|
||||
ws.onmessage = (event) => {
|
||||
let request = JSON.parse(event.data);
|
||||
console.log("Request #" + request.id + " received");
|
||||
runTests(ws, request.id, request.tests, 0);
|
||||
}
|
||||
}
|
||||
|
||||
function runTests(ws, suiteId, tests, index) {
|
||||
if (index === tests.length) {
|
||||
return;
|
||||
}
|
||||
let test = tests[index];
|
||||
runSingleTest(test, result => {
|
||||
console.log("Sending response #" + suiteId);
|
||||
ws.send(JSON.stringify({
|
||||
id: suiteId,
|
||||
index: index,
|
||||
result: result
|
||||
}));
|
||||
runTests(ws, suiteId, tests, index + 1);
|
||||
});
|
||||
}
|
||||
|
||||
function runSingleTest(test, callback) {
|
||||
console.log("Running test " + test.name + " consisting of " + test.files);
|
||||
let iframe = document.createElement("iframe");
|
||||
document.body.appendChild(iframe);
|
||||
let handshakeListener = () => {
|
||||
window.removeEventListener("message", handshakeListener);
|
||||
|
||||
let listener = event => {
|
||||
window.removeEventListener("message", listener);
|
||||
document.body.removeChild(iframe);
|
||||
callback(event.data);
|
||||
};
|
||||
window.addEventListener("message", listener);
|
||||
|
||||
iframe.contentWindow.postMessage(test, "*");
|
||||
};
|
||||
window.addEventListener("message", handshakeListener);
|
||||
iframe.src = "about:blank";
|
||||
iframe.src = "frame.html";
|
||||
}
|
||||
|
||||
tryConnect();
|
|
@ -1,25 +0,0 @@
|
|||
<!--
|
||||
~ Copyright 2017 Alexey Andreev.
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="frame.js"></script>
|
||||
</head>
|
||||
<body onload="start()">
|
||||
</body>
|
||||
</html>
|
|
@ -1,127 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
window.addEventListener("message", event => {
|
||||
let request = event.data;
|
||||
switch (request.type) {
|
||||
case "JAVASCRIPT":
|
||||
appendFiles([request.file], 0, () => {
|
||||
launchTest(request.argument, response => {
|
||||
event.source.postMessage(response, "*");
|
||||
});
|
||||
}, error => {
|
||||
event.source.postMessage({ status: "failed", errorMessage: error }, "*");
|
||||
});
|
||||
break;
|
||||
|
||||
case "WASM":
|
||||
const runtimeFile = request.file + "-runtime.js";
|
||||
appendFiles([runtimeFile], 0, () => {
|
||||
launchWasmTest(request.file, equest.argument, response => {
|
||||
event.source.postMessage(response, "*");
|
||||
});
|
||||
}, error => {
|
||||
event.source.postMessage({ status: "failed", errorMessage: error }, "*");
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
function appendFiles(files, index, callback, errorCallback) {
|
||||
if (index === files.length) {
|
||||
callback();
|
||||
} else {
|
||||
let fileName = files[index];
|
||||
let script = document.createElement("script");
|
||||
script.onload = () => {
|
||||
appendFiles(files, index + 1, callback, errorCallback);
|
||||
};
|
||||
script.onerror = () => {
|
||||
errorCallback("failed to load script " + fileName);
|
||||
};
|
||||
script.src = fileName;
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
}
|
||||
|
||||
function launchTest(argument, callback) {
|
||||
main(argument ? [argument] : [], result => {
|
||||
if (result instanceof Error) {
|
||||
callback({
|
||||
status: "failed",
|
||||
errorMessage: buildErrorMessage(result)
|
||||
});
|
||||
} else {
|
||||
callback({ status: "OK" });
|
||||
}
|
||||
});
|
||||
|
||||
function buildErrorMessage(e) {
|
||||
let stack = "";
|
||||
let je = main.javaException(e);
|
||||
if (je && je.constructor.$meta) {
|
||||
stack = je.constructor.$meta.name + ": ";
|
||||
stack += je.getMessage();
|
||||
stack += "\n";
|
||||
}
|
||||
stack += e.stack;
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
|
||||
function launchWasmTest(path, argument, callback) {
|
||||
let output = [];
|
||||
let outputBuffer = "";
|
||||
|
||||
function putwchar(charCode) {
|
||||
if (charCode === 10) {
|
||||
switch (outputBuffer) {
|
||||
case "SUCCESS":
|
||||
callback({status: "OK"});
|
||||
break;
|
||||
case "FAILURE":
|
||||
callback({
|
||||
status: "failed",
|
||||
errorMessage: output.join("\n")
|
||||
});
|
||||
break;
|
||||
default:
|
||||
output.push(outputBuffer);
|
||||
outputBuffer = "";
|
||||
}
|
||||
} else {
|
||||
outputBuffer += String.fromCharCode(charCode);
|
||||
}
|
||||
}
|
||||
|
||||
TeaVM.wasm.run(path, {
|
||||
installImports: function(o) {
|
||||
o.teavm.putwchar = putwchar;
|
||||
},
|
||||
errorCallback: function(err) {
|
||||
callback({
|
||||
status: "failed",
|
||||
errorMessage: err.message + '\n' + err.stack
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function start() {
|
||||
window.parent.postMessage("ready", "*");
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
<!--
|
||||
~ Copyright 2017 Alexey Andreev.
|
||||
~
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="client.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
|
@ -1,13 +0,0 @@
|
|||
{
|
||||
"name": "teavm-tests",
|
||||
"devDependencies": {
|
||||
"babel-cli": "6.24.1",
|
||||
"babel-core": "6.24.1",
|
||||
"babel-polyfill": "6.23.0",
|
||||
"babel-preset-env": "1.4.0",
|
||||
"websocket": "1.0.24"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel src -d bin --source-maps"
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
npm run build
|
||||
node start.js ../../../target/js-tests/
|
|
@ -1,118 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
import * as http from "http";
|
||||
import { client as WebSocketClient } from "websocket";
|
||||
|
||||
function httpGetJson(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
http.request(url, resp => {
|
||||
if (resp.statusCode !== 200) {
|
||||
reject(new Error(`HTTP status: ${resp.statusCode}`));
|
||||
return;
|
||||
}
|
||||
let data = "";
|
||||
resp.on('data', (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
resp.on('end', () => {
|
||||
resolve(JSON.parse(data));
|
||||
});
|
||||
}).on("error", err => {
|
||||
reject(new Error(`HTTP error: ${err.message}`));
|
||||
}).end();
|
||||
});
|
||||
}
|
||||
|
||||
function connectWs(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const client = new WebSocketClient();
|
||||
client.on("connectFailed", error => reject(new Error('Connect Error: ' + error)));
|
||||
client.on("connect", resolve);
|
||||
client.connect(url);
|
||||
});
|
||||
}
|
||||
|
||||
class CDP {
|
||||
constructor(conn) {
|
||||
this.idGenerator = 1;
|
||||
this.pendingCalls = Object.create(null);
|
||||
this.eventHandlers = Object.create(null);
|
||||
this.conn = conn;
|
||||
}
|
||||
|
||||
start() {
|
||||
this.conn.on("message", message => {
|
||||
if (message.type === 'utf8') {
|
||||
const messageObj = JSON.parse(message.utf8Data);
|
||||
if (messageObj.id !== void 0) {
|
||||
const pendingCall = this.pendingCalls[messageObj.id];
|
||||
delete this.pendingCalls[messageObj.id];
|
||||
if (messageObj.error) {
|
||||
pendingCall.reject(new Error(`Error calling CDP method ${messageObj.error}`));
|
||||
} else {
|
||||
pendingCall.resolve(messageObj.result);
|
||||
}
|
||||
} else {
|
||||
const handlers = this.eventHandlers[messageObj.method];
|
||||
if (handlers) {
|
||||
for (const handler of handlers) {
|
||||
handler(messageObj.params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.conn.on("close", () => {
|
||||
for (const key in Object.getOwnPropertyNames(this.pendingCalls)) {
|
||||
this.pendingCalls[key].reject(new Error("Connection closed before result received"));
|
||||
}
|
||||
});
|
||||
this.conn.on("error", err => {
|
||||
console.error("WS error: %j", err);
|
||||
});
|
||||
}
|
||||
|
||||
call(method, params = undefined) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = this.idGenerator++;
|
||||
this.pendingCalls[id] = { resolve, reject };
|
||||
this.conn.send(JSON.stringify({ id, method, params }));
|
||||
});
|
||||
}
|
||||
|
||||
async on(eventName, handler) {
|
||||
let handlers = this.eventHandlers[eventName];
|
||||
if (handlers === void 0) {
|
||||
handlers = [];
|
||||
this.eventHandlers[eventName] = handlers;
|
||||
}
|
||||
handlers.push(handler);
|
||||
}
|
||||
|
||||
static async connect(url) {
|
||||
const targets = await httpGetJson(url + "/json/list");
|
||||
const wsUrl = targets.find(target => target.type === "page").webSocketDebuggerUrl;
|
||||
console.log("Connected to Chrome");
|
||||
const wsConn = await connectWs(wsUrl);
|
||||
console.log(`Connected to WS endpoint: ${wsUrl}`);
|
||||
return wsConn;
|
||||
}
|
||||
}
|
||||
|
||||
export { CDP };
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
import { CDP } from "./cdp.js";
|
||||
|
||||
async function run() {
|
||||
const wsConn = await CDP.connect("http://localhost:9222");
|
||||
const cdp = new CDP(wsConn);
|
||||
const waitForLog = new Promise(resolve => {
|
||||
let timeout = setTimeout(resolve, 500);
|
||||
cdp.on("Runtime.consoleAPICalled", event => {
|
||||
const value = event.args.filter(arg => arg.type === "string").map(arg => arg.value).join("");
|
||||
if (value !== "") {
|
||||
console.log("[LOG] " + new Date(event.timestamp) + ": " + value);
|
||||
}
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(resolve, 500);
|
||||
});
|
||||
});
|
||||
|
||||
cdp.start();
|
||||
|
||||
try {
|
||||
await cdp.call("Runtime.enable");
|
||||
await waitForLog;
|
||||
} finally {
|
||||
wsConn.close();
|
||||
}
|
||||
}
|
||||
|
||||
run().catch(e => {
|
||||
console.error("Error", e);
|
||||
});
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
import * as fs from "fs";
|
||||
|
||||
function wrapFsFunction(fsFunction) {
|
||||
return function() {
|
||||
const self = this;
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
return new Promise((resolve, reject) => {
|
||||
args.push((error, result) => {
|
||||
if (!error) {
|
||||
resolve(result);
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
});
|
||||
fsFunction.apply(self, args);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export let
|
||||
readdir = wrapFsFunction(fs.readdir),
|
||||
readFile = wrapFsFunction(fs.readFile),
|
||||
stat = wrapFsFunction(fs.stat),
|
||||
open = wrapFsFunction(fs.open);
|
|
@ -1,318 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
import * as fs from "./promise-fs.js";
|
||||
import * as http from "http";
|
||||
import {server as WebSocketServer} from "websocket";
|
||||
|
||||
let totalTests = 0;
|
||||
|
||||
class TestSuite {
|
||||
constructor(name) {
|
||||
this.name = name;
|
||||
this.testSuites = [];
|
||||
this.testCases = [];
|
||||
}
|
||||
}
|
||||
class TestCase {
|
||||
constructor(type, file, argument) {
|
||||
this.type = type;
|
||||
this.file = file;
|
||||
this.argument = argument
|
||||
}
|
||||
}
|
||||
|
||||
let rootDir = process.argv[2];
|
||||
if (rootDir.endsWith("/")) {
|
||||
rootDir = rootDir.substring(0, rootDir.length - 1);
|
||||
}
|
||||
|
||||
async function runAll() {
|
||||
const rootSuite = new TestSuite("root");
|
||||
console.log("Searching tests");
|
||||
await walkDir("", rootSuite);
|
||||
|
||||
console.log("Running tests");
|
||||
|
||||
const server = http.createServer((request, response) => {
|
||||
if (request.url.endsWith(".js") || request.url.endsWith(".wasm")) {
|
||||
serveFile(rootDir + "/" + request.url, response);
|
||||
return;
|
||||
}
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
});
|
||||
server.listen({ host: "localhost", port: 9090 }, () => {
|
||||
console.log((new Date()) + ' Server is listening on port 9090');
|
||||
});
|
||||
|
||||
const wsServer = new WebSocketServer({
|
||||
httpServer: server,
|
||||
autoAcceptConnections: true
|
||||
});
|
||||
|
||||
const startTime = new Date().getTime();
|
||||
const connectPromise = new Promise(resolve => wsServer.on("connect", resolve));
|
||||
const timeoutPromise = new Promise((_, reject) => {
|
||||
setTimeout(() => reject(new Error("Connection time out")), 15000)
|
||||
});
|
||||
const conn = await Promise.race([connectPromise, timeoutPromise]);
|
||||
|
||||
const runner = new TestRunner(conn);
|
||||
await runner.runTests(rootSuite, "", 0);
|
||||
|
||||
wsServer.unmount();
|
||||
server.close();
|
||||
|
||||
const endTime = new Date().getTime();
|
||||
for (let i = 0; i < runner.testsFailed.length; i++) {
|
||||
const failedTest = runner.testsFailed[i];
|
||||
console.log("(" + (i + 1) + ") " + failedTest.path +":");
|
||||
console.log(failedTest.message);
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log("Tests run: " + runner.testsRun + ", failed: " + runner.testsFailed.length
|
||||
+ ", elapsed " + ((endTime - startTime) / 1000) + " seconds");
|
||||
|
||||
if (runner.testsFailed.length > 0) {
|
||||
process.exit(1);
|
||||
} else {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
async function serveFile(path, response) {
|
||||
const stat = await fs.stat(path);
|
||||
const contentType = path.endsWith(".wasm") ? "application/octet-stream" : "text/javascript";
|
||||
if (stat.isFile()) {
|
||||
const content = await fs.readFile(path);
|
||||
response.writeHead(200, { 'Content-Type': contentType, 'Access-Control-Allow-Origin': "*" });
|
||||
response.end(content, 'utf-8');
|
||||
} else {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
}
|
||||
}
|
||||
|
||||
async function walkDir(path, suite) {
|
||||
const files = await fs.readdir(rootDir + "/" + path);
|
||||
if (files.includes("tests.json")) {
|
||||
const descriptor = JSON.parse(await fs.readFile(`${rootDir}/${path}/tests.json`));
|
||||
for (const { baseDir, fileName, kind, argument } of descriptor) {
|
||||
switch (kind) {
|
||||
case "JAVASCRIPT":
|
||||
case "WASM":
|
||||
suite.testCases.push(new TestCase(kind, `${baseDir}/${fileName}`, argument));
|
||||
totalTests++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
await Promise.all(files.map(async file => {
|
||||
const filePath = path + "/" + file;
|
||||
const stat = await fs.stat(rootDir + "/" + filePath);
|
||||
if (stat.isDirectory()) {
|
||||
const childSuite = new TestSuite(file);
|
||||
suite.testSuites.push(childSuite);
|
||||
await walkDir(filePath, childSuite);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
class TestRunner {
|
||||
constructor(ws) {
|
||||
this.ws = ws;
|
||||
this.testsRun = 0;
|
||||
this.testsFailed = [];
|
||||
|
||||
this.pendingRequests = Object.create(null);
|
||||
this.requestIdGen = 0;
|
||||
|
||||
this.timeout = null;
|
||||
|
||||
this.ws.on("message", (message) => {
|
||||
const response = JSON.parse(message.utf8Data);
|
||||
const pendingRequest = this.pendingRequests[response.id + "-" + response.index];
|
||||
delete this.pendingRequests[response.id];
|
||||
if (this.timeout) {
|
||||
this.timeout.refresh();
|
||||
}
|
||||
pendingRequest(response.result);
|
||||
})
|
||||
}
|
||||
|
||||
async runTests(suite, path, depth) {
|
||||
if (suite.testCases.length > 0) {
|
||||
console.log("Running " + path);
|
||||
let testsFailedInSuite = 0;
|
||||
const startTime = new Date().getTime();
|
||||
let request = { id: this.requestIdGen++ };
|
||||
request.tests = suite.testCases.map(testCase => {
|
||||
const result = {
|
||||
type: testCase.type,
|
||||
name: testCase.name,
|
||||
file: testCase.file
|
||||
};
|
||||
if (testCase.argument) {
|
||||
result.argument = testCase.argument;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
this.testsRun += suite.testCases.length;
|
||||
|
||||
const resultPromises = [];
|
||||
|
||||
this.timeout = createRefreshableTimeoutPromise(20000);
|
||||
for (let i = 0; i < suite.testCases.length; ++i) {
|
||||
resultPromises.push(new Promise(resolve => {
|
||||
this.pendingRequests[request.id + "-" + i] = resolve;
|
||||
}));
|
||||
}
|
||||
|
||||
this.ws.send(JSON.stringify(request));
|
||||
const result = await Promise.race([Promise.all(resultPromises), this.timeout.promise]);
|
||||
|
||||
for (let i = 0; i < suite.testCases.length; i++) {
|
||||
const testCase = suite.testCases[i];
|
||||
const testResult = result[i];
|
||||
switch (testResult.status) {
|
||||
case "OK":
|
||||
break;
|
||||
case "failed":
|
||||
this.logFailure(path, testCase, testResult.errorMessage);
|
||||
testsFailedInSuite++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const endTime = new Date().getTime();
|
||||
const percentComplete = (this.testsRun / totalTests * 100).toFixed(1);
|
||||
console.log("Tests run: " + suite.testCases.length + ", failed: " + testsFailedInSuite
|
||||
+ ", elapsed: " + ((endTime - startTime) / 1000) + " seconds "
|
||||
+ "(" + percentComplete + "% complete)");
|
||||
console.log();
|
||||
}
|
||||
|
||||
for (const childSuite of suite.testSuites) {
|
||||
await this.runTests(childSuite, path + "/" + childSuite.name, depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
logFailure(path, testCase, message) {
|
||||
console.log(" " + testCase.name + " failure (" + (this.testsFailed.length + 1) + ")");
|
||||
this.testsFailed.push({
|
||||
path: path + "/" + testCase.name,
|
||||
message: message
|
||||
});
|
||||
}
|
||||
|
||||
async runTeaVMTest(testCase) {
|
||||
await this.page.reload();
|
||||
|
||||
const fileContents = await Promise.all(testCase.files.map(async (file) => {
|
||||
return fs.readFile(file, 'utf8');
|
||||
}));
|
||||
for (let i = 0; i < testCase.files.length; i++) {
|
||||
const fileName = testCase.files[i];
|
||||
const contents = fileContents[i];
|
||||
const { scriptId } = await this.runtime.compileScript({
|
||||
expression: contents,
|
||||
sourceURL: fileName,
|
||||
persistScript: true
|
||||
});
|
||||
TestRunner.checkScriptResult(await this.runtime.runScript({ scriptId : scriptId }), fileName);
|
||||
}
|
||||
|
||||
const { scriptId: runScriptId } = await this.runtime.compileScript({
|
||||
expression: "(" + runTeaVM.toString() + ")()",
|
||||
sourceURL: " ",
|
||||
persistScript: true
|
||||
});
|
||||
const result = TestRunner.checkScriptResult(await this.runtime.runScript({
|
||||
scriptId: runScriptId,
|
||||
awaitPromise: true,
|
||||
returnByValue: true
|
||||
}));
|
||||
|
||||
return result.result.value;
|
||||
}
|
||||
|
||||
static checkScriptResult(scriptResult, scriptUrl) {
|
||||
if (scriptResult.result.subtype === "error") {
|
||||
throw new Error("Exception caught from script " + scriptUrl + ":\n" + scriptResult.result.description);
|
||||
}
|
||||
return scriptResult;
|
||||
}
|
||||
}
|
||||
|
||||
function runTeaVM() {
|
||||
return new Promise(resolve => {
|
||||
main([], result => {
|
||||
const message = {};
|
||||
if (result instanceof Error) {
|
||||
makeErrorMessage(message, result);
|
||||
} else {
|
||||
message.status = "OK";
|
||||
}
|
||||
resolve(message);
|
||||
});
|
||||
|
||||
function makeErrorMessage(message, e) {
|
||||
message.status = "failed";
|
||||
var je = main.javaException(e);
|
||||
if (je) {
|
||||
message.className = je.constructor.name;
|
||||
message.message = je.getMessage();
|
||||
} else {
|
||||
message.className = Object.getPrototypeOf(e).name;
|
||||
message.message = e.message;
|
||||
}
|
||||
message.exception = e;
|
||||
message.stack = e.stack;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function createRefreshableTimeoutPromise(timeout) {
|
||||
const obj = {
|
||||
currentId: 0,
|
||||
resolveFun: () => {},
|
||||
refresh: () => {
|
||||
const expectedId = ++obj.currentId;
|
||||
setTimeout(() => {
|
||||
if (expectedId === obj.currentId) {
|
||||
obj.resolveFun(new Error("Connection timeout"))
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
};
|
||||
|
||||
const result = {
|
||||
promise: new Promise((_, reject) => {
|
||||
obj.resolveFun = reject;
|
||||
}),
|
||||
refresh: () => obj.refresh()
|
||||
};
|
||||
obj.refresh();
|
||||
return result;
|
||||
}
|
||||
|
||||
runAll().catch(e => {
|
||||
console.log(e.stack);
|
||||
process.exit(1);
|
||||
});
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
import { CDP } from "./cdp.js";
|
||||
|
||||
function waitForTest(cdp) {
|
||||
return new Promise(resolve => {
|
||||
let stage = 'initial';
|
||||
let requestId = null;
|
||||
cdp.on("Runtime.consoleAPICalled", event => {
|
||||
const value = event.args.filter(arg => arg.type === "string").map(arg => arg.value).join("");
|
||||
switch (stage) {
|
||||
case 'initial':
|
||||
if (value === 'Connection established') {
|
||||
stage = 'connected';
|
||||
}
|
||||
break;
|
||||
case 'connected': {
|
||||
const result = /Request #([0-9]+) received/.exec(value);
|
||||
if (result) {
|
||||
requestId = result[1];
|
||||
stage = 'sent';
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'sent':
|
||||
if (value === "Running test only simple consisting of http://localhost:9090//only/test.js") {
|
||||
stage = 'ran';
|
||||
}
|
||||
break;
|
||||
case 'ran':
|
||||
if (value === "Sending response #" + requestId) {
|
||||
stage = 'received';
|
||||
resolve();
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function timeout(time) {
|
||||
let timer;
|
||||
return {
|
||||
promise: new Promise((resolve, reject) => {
|
||||
timer = setTimeout(() => { reject(new Error("Timeout expired")); }, time);
|
||||
}),
|
||||
cancel: () => clearTimeout(timer)
|
||||
}
|
||||
}
|
||||
|
||||
async function run() {
|
||||
const wsConn = await CDP.connect("http://localhost:9222");
|
||||
const cdp = new CDP(wsConn);
|
||||
cdp.on("Runtime.consoleAPICalled", event => {
|
||||
const value = event.args.filter(arg => arg.type === "string").map(arg => arg.value).join("");
|
||||
if (value !== "") {
|
||||
console.log("[LOG] " + new Date(event.timestamp) + ": " + value);
|
||||
}
|
||||
});
|
||||
|
||||
const wait = waitForTest(cdp);
|
||||
let timer;
|
||||
cdp.start();
|
||||
|
||||
try {
|
||||
await cdp.call("Runtime.enable");
|
||||
timer = timeout(10000);
|
||||
await Promise.race([wait, timer.promise]);
|
||||
} finally {
|
||||
if (timer) {
|
||||
timer.cancel();
|
||||
}
|
||||
wsConn.close();
|
||||
}
|
||||
}
|
||||
|
||||
run().catch(e => {
|
||||
console.error("Error", e);
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
require("babel-core/register");
|
||||
require("babel-polyfill");
|
||||
|
||||
require("./bin/run-tests");
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
function main(args, callback) {
|
||||
callback();
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Copyright 2017 Alexey Andreev.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
user_pref("browser.dom.window.dump.enabled", true);
|
||||
user_pref("browser.shell.checkDefaultBrowser", false);
|
Loading…
Reference in New Issue
Block a user