mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Use websocket client to run tests
This commit is contained in:
parent
3c3448e812
commit
60bcf97933
81
tests/src/test/js/client.js
Normal file
81
tests/src/test/js/client.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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("Connected established");
|
||||
listen(ws);
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
ws.close();
|
||||
setTimeout(() => {
|
||||
tryConnect();
|
||||
}, 500);
|
||||
};
|
||||
}
|
||||
|
||||
function listen(ws) {
|
||||
ws.onmessage = (event) => {
|
||||
let resultConsumer = [];
|
||||
let request = JSON.parse(event.data);
|
||||
console.log("Request #" + request.id + " received");
|
||||
runTests(request.tests, resultConsumer, 0, () => {
|
||||
console.log("Sending response #" + request.id);
|
||||
ws.send(JSON.stringify({
|
||||
id: request.id,
|
||||
result: resultConsumer
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function runTests(tests, consumer, index, callback) {
|
||||
if (index === tests.length) {
|
||||
callback();
|
||||
} else {
|
||||
let test = tests[index];
|
||||
runSingleTest(test, result => {
|
||||
consumer.push(result);
|
||||
runTests(tests, consumer, index + 1, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function runSingleTest(test, callback) {
|
||||
console.log("Running test " + test.name + " consisting of " + test.files);
|
||||
let iframe = document.getElementById("test");
|
||||
let handshakeListener = () => {
|
||||
window.removeEventListener("message", handshakeListener);
|
||||
|
||||
let listener = event => {
|
||||
window.removeEventListener("message", listener);
|
||||
callback(event.data);
|
||||
};
|
||||
window.addEventListener("message", listener);
|
||||
|
||||
iframe.contentWindow.postMessage(test, "*");
|
||||
};
|
||||
window.addEventListener("message", handshakeListener);
|
||||
iframe.src = "about:blank";
|
||||
iframe.src = "frame.html";
|
||||
}
|
||||
|
||||
tryConnect();
|
25
tests/src/test/js/frame.html
Normal file
25
tests/src/test/js/frame.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
<!--
|
||||
~ 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>
|
81
tests/src/test/js/frame.js
Normal file
81
tests/src/test/js/frame.js
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* 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;
|
||||
appendFiles(request.files, 0, () => {
|
||||
launchTest(response => {
|
||||
event.source.postMessage(response, "*");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function appendFiles(files, index, callback) {
|
||||
if (index === files.length) {
|
||||
callback();
|
||||
} else {
|
||||
let fileName = "file://" + files[index];
|
||||
let script = document.createElement("script");
|
||||
script.src = fileName;
|
||||
script.onload = () => {
|
||||
appendFiles(files, index + 1, callback);
|
||||
};
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
}
|
||||
|
||||
function launchTest(callback) {
|
||||
$rt_startThread(() => {
|
||||
let thread = $rt_nativeThread();
|
||||
let instance;
|
||||
let message;
|
||||
if (thread.isResuming()) {
|
||||
instance = thread.pop();
|
||||
}
|
||||
try {
|
||||
runTest();
|
||||
} catch (e) {
|
||||
message = buildErrorMessage(e);
|
||||
callback({
|
||||
status: "failed",
|
||||
errorMessage: buildErrorMessage(e)
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (thread.isSuspending()) {
|
||||
thread.push(instance);
|
||||
} else {
|
||||
callback({ status: "OK" });
|
||||
}
|
||||
});
|
||||
|
||||
function buildErrorMessage(e) {
|
||||
let stack = e.stack;
|
||||
if (e.$javaException && e.$javaException.constructor.$meta) {
|
||||
stack = e.$javaException.constructor.$meta.name + ": ";
|
||||
let exceptionMessage = extractException(e.$javaException);
|
||||
stack += exceptionMessage ? $rt_ustr(exceptionMessage) : "";
|
||||
}
|
||||
stack += "\n" + stack;
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
window.parent.postMessage("ready", "*");
|
||||
}
|
26
tests/src/test/js/index.html
Normal file
26
tests/src/test/js/index.html
Normal file
|
@ -0,0 +1,26 @@
|
|||
<!--
|
||||
~ 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>
|
||||
<iframe id="test" src="frame.html"></iframe>
|
||||
</body>
|
||||
</html>
|
|
@ -5,7 +5,7 @@
|
|||
"babel-core": "6.24.1",
|
||||
"babel-polyfill": "6.23.0",
|
||||
"babel-preset-env": "1.4.0",
|
||||
"chrome-remote-interface": "0.20.0"
|
||||
"websocket": "1.0.24"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel src -d bin --source-maps"
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
import * as fs from "fs";
|
||||
|
||||
function wrapFsFunction(fsFunction) {
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
import * as fs from "./promise-fs.js";
|
||||
import { default as CDP } from 'chrome-remote-interface';
|
||||
import * as http from "http";
|
||||
import { server as WebSocketServer } from "websocket";
|
||||
|
||||
const TEST_FILE_NAME = "test.js";
|
||||
const RUNTIME_FILE_NAME = "runtime.js";
|
||||
|
@ -24,6 +26,7 @@ const TEST_FILES = [
|
|||
{ file: "test-min.js", name: "minified" },
|
||||
{ file: "test-optimized.js", name: "optimized" }
|
||||
];
|
||||
let totalTests = 0;
|
||||
|
||||
class TestSuite {
|
||||
constructor(name) {
|
||||
|
@ -46,31 +49,38 @@ async function runAll() {
|
|||
|
||||
console.log("Running tests");
|
||||
const stats = { testRun: 0, testsFailed: [] };
|
||||
|
||||
const server = http.createServer((request, response) => {
|
||||
response.writeHead(404);
|
||||
response.end();
|
||||
});
|
||||
server.listen(9090, () => {
|
||||
console.log((new Date()) + ' Server is listening on port 8080');
|
||||
});
|
||||
|
||||
const wsServer = new WebSocketServer({
|
||||
httpServer: server,
|
||||
autoAcceptConnections: true
|
||||
});
|
||||
|
||||
const startTime = new Date().getTime();
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
CDP(async (client) => {
|
||||
wsServer.on("connect", async (conn) => {
|
||||
try {
|
||||
const {Page, Runtime} = client;
|
||||
await Promise.all([Runtime.enable(), Page.enable()]);
|
||||
await Page.navigate({url: "about:blank"});
|
||||
|
||||
const runner = new TestRunner(Page, Runtime);
|
||||
const runner = new TestRunner(conn);
|
||||
await runner.runTests(rootSuite, "", 0);
|
||||
stats.testRun = runner.testsRun;
|
||||
stats.testsFailed = runner.testsFailed;
|
||||
await client.close();
|
||||
resolve();
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}).on("error", err => {
|
||||
reject(err);
|
||||
}).on("disconnect", () => {
|
||||
reject("disconnected from chrome");
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
wsServer.unmount();
|
||||
server.close();
|
||||
|
||||
const endTime = new Date().getTime();
|
||||
for (let i = 0; i < stats.testsFailed.length; i++) {
|
||||
const failedTest = stats.testsFailed[i];
|
||||
|
@ -84,6 +94,8 @@ async function runAll() {
|
|||
|
||||
if (stats.testsFailed.length > 0) {
|
||||
process.exit(1);
|
||||
} else {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,6 +107,7 @@ async function walkDir(path, name, suite) {
|
|||
suite.testCases.push(new TestCase(
|
||||
name + " " + profileName,
|
||||
[path + "/" + RUNTIME_FILE_NAME, path + "/" + fileName]));
|
||||
totalTests++;
|
||||
}
|
||||
}
|
||||
} else if (files) {
|
||||
|
@ -111,11 +124,20 @@ async function walkDir(path, name, suite) {
|
|||
}
|
||||
|
||||
class TestRunner {
|
||||
constructor(page, runtime) {
|
||||
this.page = page;
|
||||
this.runtime = runtime;
|
||||
constructor(ws) {
|
||||
this.ws = ws;
|
||||
this.testsRun = 0;
|
||||
this.testsFailed = [];
|
||||
|
||||
this.pendingRequests = Object.create(null);
|
||||
this.requestIdGen = 0;
|
||||
|
||||
this.ws.on("message", (message) => {
|
||||
const response = JSON.parse(message.utf8Data);
|
||||
const pendingRequest = this.pendingRequests[response.id];
|
||||
delete this.pendingRequests[response.id];
|
||||
pendingRequest(response.result);
|
||||
})
|
||||
}
|
||||
|
||||
async runTests(suite, path, depth) {
|
||||
|
@ -123,32 +145,41 @@ class TestRunner {
|
|||
console.log("Running " + path);
|
||||
let testsFailedInSuite = 0;
|
||||
const startTime = new Date().getTime();
|
||||
for (const testCase of suite.testCases) {
|
||||
this.testsRun++;
|
||||
try {
|
||||
const testRun = Promise.race([
|
||||
this.runTeaVMTest(testCase),
|
||||
new Promise(resolve => {
|
||||
setTimeout(() => resolve({status: "failed", errorMessage: "timeout"}), 1000);
|
||||
})
|
||||
]);
|
||||
const result = await testRun;
|
||||
switch (result.status) {
|
||||
let request = { id: this.requestIdGen++ };
|
||||
request.tests = suite.testCases.map(testCase => {
|
||||
return {
|
||||
name: testCase.name,
|
||||
files: testCase.files.map(fileName => process.cwd() + "/" + fileName)
|
||||
};
|
||||
});
|
||||
this.testsRun += suite.testCases.length;
|
||||
|
||||
const resultPromise = new Promise(resolve => {
|
||||
this.pendingRequests[request.id] = resolve;
|
||||
});
|
||||
const timeoutPromise = new Promise((_, reject) => {
|
||||
setTimeout(() => reject(new Error("connection timeout")), 120000);
|
||||
});
|
||||
this.ws.send(JSON.stringify(request));
|
||||
const result = await Promise.race([resultPromise, timeoutPromise]);
|
||||
|
||||
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, result.errorMessage);
|
||||
this.logFailure(path, testCase, testResult.errorMessage);
|
||||
testsFailedInSuite++;
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
this.logFailure(path, testCase, e.stack);
|
||||
testsFailedInSuite++;
|
||||
}
|
||||
}
|
||||
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");
|
||||
+ ", elapsed: " + ((endTime - startTime) / 1000) + " seconds "
|
||||
+ "(" + percentComplete + "% complete)");
|
||||
console.log();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user