mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -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-core": "6.24.1",
|
||||||
"babel-polyfill": "6.23.0",
|
"babel-polyfill": "6.23.0",
|
||||||
"babel-preset-env": "1.4.0",
|
"babel-preset-env": "1.4.0",
|
||||||
"chrome-remote-interface": "0.20.0"
|
"websocket": "1.0.24"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "babel src -d bin --source-maps"
|
"build": "babel src -d bin --source-maps"
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
|
|
||||||
function wrapFsFunction(fsFunction) {
|
function wrapFsFunction(fsFunction) {
|
||||||
|
|
|
@ -14,8 +14,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
import * as fs from "./promise-fs.js";
|
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 TEST_FILE_NAME = "test.js";
|
||||||
const RUNTIME_FILE_NAME = "runtime.js";
|
const RUNTIME_FILE_NAME = "runtime.js";
|
||||||
|
@ -24,6 +26,7 @@ const TEST_FILES = [
|
||||||
{ file: "test-min.js", name: "minified" },
|
{ file: "test-min.js", name: "minified" },
|
||||||
{ file: "test-optimized.js", name: "optimized" }
|
{ file: "test-optimized.js", name: "optimized" }
|
||||||
];
|
];
|
||||||
|
let totalTests = 0;
|
||||||
|
|
||||||
class TestSuite {
|
class TestSuite {
|
||||||
constructor(name) {
|
constructor(name) {
|
||||||
|
@ -46,31 +49,38 @@ async function runAll() {
|
||||||
|
|
||||||
console.log("Running tests");
|
console.log("Running tests");
|
||||||
const stats = { testRun: 0, testsFailed: [] };
|
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();
|
const startTime = new Date().getTime();
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
CDP(async (client) => {
|
wsServer.on("connect", async (conn) => {
|
||||||
try {
|
try {
|
||||||
const {Page, Runtime} = client;
|
const runner = new TestRunner(conn);
|
||||||
await Promise.all([Runtime.enable(), Page.enable()]);
|
|
||||||
await Page.navigate({url: "about:blank"});
|
|
||||||
|
|
||||||
const runner = new TestRunner(Page, Runtime);
|
|
||||||
await runner.runTests(rootSuite, "", 0);
|
await runner.runTests(rootSuite, "", 0);
|
||||||
stats.testRun = runner.testsRun;
|
stats.testRun = runner.testsRun;
|
||||||
stats.testsFailed = runner.testsFailed;
|
stats.testsFailed = runner.testsFailed;
|
||||||
await client.close();
|
|
||||||
resolve();
|
resolve();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(e);
|
reject(e);
|
||||||
}
|
}
|
||||||
}).on("error", err => {
|
})
|
||||||
reject(err);
|
|
||||||
}).on("disconnect", () => {
|
|
||||||
reject("disconnected from chrome");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
wsServer.unmount();
|
||||||
|
server.close();
|
||||||
|
|
||||||
const endTime = new Date().getTime();
|
const endTime = new Date().getTime();
|
||||||
for (let i = 0; i < stats.testsFailed.length; i++) {
|
for (let i = 0; i < stats.testsFailed.length; i++) {
|
||||||
const failedTest = stats.testsFailed[i];
|
const failedTest = stats.testsFailed[i];
|
||||||
|
@ -84,6 +94,8 @@ async function runAll() {
|
||||||
|
|
||||||
if (stats.testsFailed.length > 0) {
|
if (stats.testsFailed.length > 0) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
} else {
|
||||||
|
process.exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +107,7 @@ async function walkDir(path, name, suite) {
|
||||||
suite.testCases.push(new TestCase(
|
suite.testCases.push(new TestCase(
|
||||||
name + " " + profileName,
|
name + " " + profileName,
|
||||||
[path + "/" + RUNTIME_FILE_NAME, path + "/" + fileName]));
|
[path + "/" + RUNTIME_FILE_NAME, path + "/" + fileName]));
|
||||||
|
totalTests++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (files) {
|
} else if (files) {
|
||||||
|
@ -111,11 +124,20 @@ async function walkDir(path, name, suite) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestRunner {
|
class TestRunner {
|
||||||
constructor(page, runtime) {
|
constructor(ws) {
|
||||||
this.page = page;
|
this.ws = ws;
|
||||||
this.runtime = runtime;
|
|
||||||
this.testsRun = 0;
|
this.testsRun = 0;
|
||||||
this.testsFailed = [];
|
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) {
|
async runTests(suite, path, depth) {
|
||||||
|
@ -123,32 +145,41 @@ class TestRunner {
|
||||||
console.log("Running " + path);
|
console.log("Running " + path);
|
||||||
let testsFailedInSuite = 0;
|
let testsFailedInSuite = 0;
|
||||||
const startTime = new Date().getTime();
|
const startTime = new Date().getTime();
|
||||||
for (const testCase of suite.testCases) {
|
let request = { id: this.requestIdGen++ };
|
||||||
this.testsRun++;
|
request.tests = suite.testCases.map(testCase => {
|
||||||
try {
|
return {
|
||||||
const testRun = Promise.race([
|
name: testCase.name,
|
||||||
this.runTeaVMTest(testCase),
|
files: testCase.files.map(fileName => process.cwd() + "/" + fileName)
|
||||||
new Promise(resolve => {
|
};
|
||||||
setTimeout(() => resolve({status: "failed", errorMessage: "timeout"}), 1000);
|
});
|
||||||
})
|
this.testsRun += suite.testCases.length;
|
||||||
]);
|
|
||||||
const result = await testRun;
|
const resultPromise = new Promise(resolve => {
|
||||||
switch (result.status) {
|
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":
|
case "OK":
|
||||||
break;
|
break;
|
||||||
case "failed":
|
case "failed":
|
||||||
this.logFailure(path, testCase, result.errorMessage);
|
this.logFailure(path, testCase, testResult.errorMessage);
|
||||||
testsFailedInSuite++;
|
testsFailedInSuite++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
this.logFailure(path, testCase, e.stack);
|
|
||||||
testsFailedInSuite++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const endTime = new Date().getTime();
|
const endTime = new Date().getTime();
|
||||||
|
const percentComplete = (this.testsRun / totalTests * 100).toFixed(1);
|
||||||
console.log("Tests run: " + suite.testCases.length + ", failed: " + testsFailedInSuite
|
console.log("Tests run: " + suite.testCases.length + ", failed: " + testsFailedInSuite
|
||||||
+ ", elapsed: " + ((endTime - startTime) / 1000) + " seconds");
|
+ ", elapsed: " + ((endTime - startTime) / 1000) + " seconds "
|
||||||
|
+ "(" + percentComplete + "% complete)");
|
||||||
console.log();
|
console.log();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user