Adapt the js batched test runner to emrun interface
This makes the js batched test runner cooperate with emrun. The runner sends back the output and exit messages to emrun to inform it about the test execution state. The code is based on emrun_postjs.js from the emsdk. Change-Id: I758f2c185797f4000810eb4314423eebc1c5d457 Reviewed-by: David Skoland <david.skoland@qt.io>
This commit is contained in:
parent
84c494d0f2
commit
7dbbe0a222
@ -31,8 +31,12 @@ function(_qt_internal_wasm_add_target_helpers target)
|
||||
"${target_output_directory}/batchedtestrunner.html" COPYONLY)
|
||||
configure_file("${WASM_BUILD_DIR}/libexec/batchedtestrunner.js"
|
||||
"${target_output_directory}/batchedtestrunner.js" COPYONLY)
|
||||
configure_file("${WASM_BUILD_DIR}/libexec/emrunadapter.js"
|
||||
"${target_output_directory}/emrunadapter.js" COPYONLY)
|
||||
configure_file("${WASM_BUILD_DIR}/libexec/qwasmjsruntime.js"
|
||||
"${target_output_directory}/qwasmjsruntime.js" COPYONLY)
|
||||
configure_file("${WASM_BUILD_DIR}/libexec/qwasmtestmain.js"
|
||||
"${target_output_directory}/qwasmtestmain.js" COPYONLY)
|
||||
configure_file("${WASM_BUILD_DIR}/libexec/util.js"
|
||||
"${target_output_directory}/util.js" COPYONLY)
|
||||
else()
|
||||
|
@ -171,8 +171,12 @@ if(QT_FEATURE_batch_test_support AND WASM)
|
||||
${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/batchedtestrunner.html)
|
||||
list(APPEND wasm_support_libexec_files
|
||||
${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/batchedtestrunner.js)
|
||||
list(APPEND wasm_support_libexec_files
|
||||
${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/emrunadapter.js)
|
||||
list(APPEND wasm_support_libexec_files
|
||||
${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/qwasmjsruntime.js)
|
||||
list(APPEND wasm_support_libexec_files
|
||||
${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/qwasmtestmain.js)
|
||||
list(APPEND wasm_support_libexec_files
|
||||
${QtBase_SOURCE_DIR}/util/wasm/batchedtestrunner/util.js)
|
||||
|
||||
|
@ -5,8 +5,24 @@ prints out a list of test classes inside its module. Then, when run with the fir
|
||||
equal to the name of one of the test classes, the test program will execute all tests within
|
||||
that single class.
|
||||
|
||||
The scripts in the page will load the wasm file called 'test_batch.wasm' with its corresponding
|
||||
js script 'test_batch.js'.
|
||||
The following query parameters are recognized by the webpage:
|
||||
|
||||
qtestname=testname - the test case to run. When batched test module is used, the test is assumed to
|
||||
be a part of the batch. If a standalone test module is used, this is assumed to be the name of
|
||||
the wasm module.
|
||||
|
||||
quseemrun - if specified, the test communicates with the emrun instance via the protocol expected
|
||||
by emrun.
|
||||
|
||||
qtestoutputformat=txt|xml|lightxml|junitxml|tap - specifies the output format for the test case.
|
||||
|
||||
qbatchedtest - if specified, the script will load the test_batch.wasm module and either run all
|
||||
testcases in it or a specific test case, depending on the existence of the qtestname parameter.
|
||||
Otherwise, the test is assumed to be a standalone binary whose name is determined by the
|
||||
qtestname parameter.
|
||||
|
||||
The scripts in the page will load the wasm file as specified by a combination of qbatchedtest and
|
||||
qtestname.
|
||||
|
||||
Public interface for querying the test execution status is accessible via the global object
|
||||
'qtTestRunner':
|
||||
@ -15,8 +31,8 @@ qtTestRunner.status - this contains the status of the test runner itself, of the
|
||||
RunnerStatus.
|
||||
|
||||
qtTestRunner.results - a map of test class name to test result. The result contains a test status
|
||||
(status, of the enumeration TestStatus), and in case of a terminal status, also the test's exit code
|
||||
(exitCode) and xml text output (textOutput), if available.
|
||||
(status, of the enumeration TestStatus), text output chunks (output), and in case of a terminal
|
||||
status, also the test's exit code (exitCode)
|
||||
|
||||
qtTestRunner.onStatusChanged - an event for changes in state of the runner itself. The possible
|
||||
values are those of the enumeration RunnerStatus.
|
||||
@ -39,3 +55,6 @@ Query for test execution state:
|
||||
- qtTestRunner.status === (...)
|
||||
- qtTestRunner.results['tst_mytest'].status === (...)
|
||||
- qtTestRunner.results['tst_mytest'].textOutput
|
||||
|
||||
When queseemrun is specified, the built-in emrun support module will POST the test output to the
|
||||
emrun instance and will report ^exit^ with a suitable exit code to it when testing is finished.
|
||||
|
@ -7,8 +7,8 @@ SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>WASM batched test runner</title>
|
||||
<script type="module" defer="defer" src="batchedtestrunner.js"></script>
|
||||
<title>WASM batched test runner (emrun-enabled)</title>
|
||||
<script type="module" defer="defer" src="qwasmtestmain.js"></script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
|
@ -3,12 +3,9 @@
|
||||
|
||||
import {
|
||||
AbortedError,
|
||||
ModuleLoader,
|
||||
ResourceFetcher,
|
||||
ResourceLocator,
|
||||
} from './qwasmjsruntime.js';
|
||||
|
||||
import { parseQuery, EventSource } from './util.js';
|
||||
import { EventSource } from './util.js';
|
||||
|
||||
class ProgramError extends Error {
|
||||
constructor(exitCode) {
|
||||
@ -16,26 +13,34 @@ class ProgramError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
class RunnerStatus {
|
||||
export class RunnerStatus {
|
||||
static Running = 'Running';
|
||||
static Completed = 'Completed';
|
||||
static Passed = 'Passed';
|
||||
static Error = 'Error';
|
||||
static TestCrashed = 'TestCrashed';
|
||||
static TestsFailed = 'TestsFailed';
|
||||
}
|
||||
|
||||
class TestStatus {
|
||||
export class TestStatus {
|
||||
static Pending = 'Pending';
|
||||
static Running = 'Running';
|
||||
static Completed = 'Completed';
|
||||
static Error = 'Error';
|
||||
static Failed = 'Failed';
|
||||
static Crashed = 'Crashed';
|
||||
}
|
||||
|
||||
// Represents the public API of the runner.
|
||||
class WebApi {
|
||||
export class BatchedTestRunner {
|
||||
static #TestBatchModuleName = 'test_batch';
|
||||
|
||||
#loader;
|
||||
|
||||
#results = new Map();
|
||||
#status = RunnerStatus.Running;
|
||||
#numberOfFailed = 0;
|
||||
#statusChangedEventPrivate;
|
||||
#testStatusChangedEventPrivate;
|
||||
#testOutputChangedEventPrivate;
|
||||
#errorDetails;
|
||||
|
||||
onStatusChanged =
|
||||
@ -43,25 +48,95 @@ class WebApi {
|
||||
onTestStatusChanged =
|
||||
new EventSource((privateInterface) =>
|
||||
this.#testStatusChangedEventPrivate = privateInterface);
|
||||
onTestOutputChanged =
|
||||
new EventSource(
|
||||
(privateInterface) => this.#testOutputChangedEventPrivate = privateInterface);
|
||||
|
||||
// The callback receives the private interface of this object, meant not to be used by the
|
||||
// end user on the web side.
|
||||
constructor(receivePrivateInterface) {
|
||||
receivePrivateInterface({
|
||||
registerTest: testName => this.#registerTest(testName),
|
||||
setTestStatus: (testName, status) => this.#setTestStatus(testName, status),
|
||||
setTestResultData: (testName, testStatus, exitCode, textOutput) =>
|
||||
this.#setTestResultData(testName, testStatus, exitCode, textOutput),
|
||||
setTestRunnerStatus: status => this.#setTestRunnerStatus(status),
|
||||
setTestRunnerError: details => this.#setTestRunnerError(details),
|
||||
});
|
||||
constructor(loader) {
|
||||
this.#loader = loader;
|
||||
}
|
||||
|
||||
get results() { return this.#results; }
|
||||
|
||||
get status() { return this.#status; }
|
||||
|
||||
get numberOfFailed() {
|
||||
if (this.#status !== RunnerStatus.TestsFailed)
|
||||
throw new Error(`numberOfFailed called with status=${this.#status}`);
|
||||
return this.#numberOfFailed;
|
||||
}
|
||||
|
||||
get errorDetails() { return this.#errorDetails; }
|
||||
|
||||
#registerTest(testName) { this.#results.set(testName, { status: TestStatus.Pending }); }
|
||||
async run(targetIsBatch, testName, testOutputFormat) {
|
||||
try {
|
||||
await this.#doRun(targetIsBatch, testName, testOutputFormat);
|
||||
} catch (e) {
|
||||
this.#setTestRunnerError(e.message);
|
||||
return;
|
||||
}
|
||||
|
||||
const status = (() => {
|
||||
const hasAnyCrashedTest =
|
||||
!![...window.qtTestRunner.results.values()].find(
|
||||
result => result.status === TestStatus.Crashed);
|
||||
if (hasAnyCrashedTest)
|
||||
return { code: RunnerStatus.TestCrashed };
|
||||
const numberOfFailed = [...window.qtTestRunner.results.values()].reduce(
|
||||
(previous, current) => previous + current.exitCode, 0);
|
||||
return {
|
||||
code: (numberOfFailed ? RunnerStatus.TestsFailed : RunnerStatus.Passed),
|
||||
numberOfFailed
|
||||
};
|
||||
})();
|
||||
|
||||
this.#setTestRunnerStatus(status.code, status.numberOfFailed);
|
||||
}
|
||||
|
||||
async #doRun(targetIsBatch, testName, testOutputFormat) {
|
||||
const module = await this.#loader.loadEmscriptenModule(
|
||||
targetIsBatch ? BatchedTestRunner.#TestBatchModuleName : testName,
|
||||
() => { }
|
||||
);
|
||||
|
||||
const testsToExecute = (testName || !targetIsBatch)
|
||||
? [testName] : await this.#getTestClassNames(module);
|
||||
testsToExecute.forEach(testClassName => this.#registerTest(testClassName));
|
||||
|
||||
for (const testClassName of testsToExecute) {
|
||||
let result = {};
|
||||
this.#setTestStatus(testClassName, TestStatus.Running);
|
||||
|
||||
try {
|
||||
const LogToStdoutSpecialFilename = '-';
|
||||
result = await module.exec({
|
||||
args: [...(targetIsBatch ? [testClassName] : []),
|
||||
'-o', `${LogToStdoutSpecialFilename},${testOutputFormat}`],
|
||||
onStdout: (output) => {
|
||||
this.#addTestOutput(testClassName, output);
|
||||
}
|
||||
});
|
||||
|
||||
if (result.exitCode < 0)
|
||||
throw new ProgramError(result.exitCode);
|
||||
result.status = result.exitCode > 0 ? TestStatus.Failed : TestStatus.Completed;
|
||||
// Yield to other tasks on the main thread.
|
||||
await new Promise(resolve => window.setTimeout(resolve, 0));
|
||||
} catch (e) {
|
||||
result.status = e instanceof ProgramError ? TestStatus.Error : TestStatus.Crashed;
|
||||
result.stdout = e instanceof AbortedError ? e.stdout : result.stdout;
|
||||
}
|
||||
this.#setTestResultData(testClassName, result.status, result.exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
async #getTestClassNames(module) {
|
||||
return (await module.exec()).stdout.trim().split(' ');
|
||||
}
|
||||
|
||||
#registerTest(testName) {
|
||||
this.#results.set(testName, { status: TestStatus.Pending, output: [] });
|
||||
}
|
||||
|
||||
#setTestStatus(testName, status) {
|
||||
const testData = this.#results.get(testName);
|
||||
@ -71,109 +146,32 @@ class WebApi {
|
||||
this.#testStatusChangedEventPrivate.fireEvent(testName, status);
|
||||
}
|
||||
|
||||
#setTestResultData(testName, testStatus, exitCode, textOutput) {
|
||||
#setTestResultData(testName, testStatus, exitCode) {
|
||||
const testData = this.#results.get(testName);
|
||||
const statusChanged = testStatus !== testData.status;
|
||||
testData.status = testStatus;
|
||||
testData.exitCode = exitCode;
|
||||
testData.textOutput = textOutput;
|
||||
if (statusChanged)
|
||||
this.#testStatusChangedEventPrivate.fireEvent(testName, testStatus);
|
||||
}
|
||||
|
||||
#setTestRunnerStatus(status) {
|
||||
#setTestRunnerStatus(status, numberOfFailed) {
|
||||
if (status === this.#status)
|
||||
return;
|
||||
this.#status = status;
|
||||
this.#numberOfFailed = numberOfFailed;
|
||||
this.#statusChangedEventPrivate.fireEvent(status);
|
||||
}
|
||||
|
||||
#setTestRunnerError(details) {
|
||||
this.#status = RunnerStatus.Error;
|
||||
this.#errorDetails = details;
|
||||
this.#statusChangedEventPrivate.fireEvent(RunnerStatus.Error);
|
||||
}
|
||||
this.#statusChangedEventPrivate.fireEvent(this.#status);
|
||||
}
|
||||
|
||||
class BatchedTestRunner {
|
||||
static #TestBatchModuleName = 'test_batch';
|
||||
|
||||
#loader;
|
||||
#privateWebApi;
|
||||
|
||||
constructor(loader, privateWebApi) {
|
||||
this.#loader = loader;
|
||||
this.#privateWebApi = privateWebApi;
|
||||
}
|
||||
|
||||
async #doRun(testName, testOutputFormat) {
|
||||
const module = await this.#loader.loadEmscriptenModule(
|
||||
BatchedTestRunner.#TestBatchModuleName,
|
||||
() => { }
|
||||
);
|
||||
|
||||
const testsToExecute = testName ? [testName] : await this.#getTestClassNames(module);
|
||||
testsToExecute.forEach(testClassName => this.#privateWebApi.registerTest(testClassName));
|
||||
for (const testClassName of testsToExecute) {
|
||||
let result = {};
|
||||
this.#privateWebApi.setTestStatus(testClassName, TestStatus.Running);
|
||||
|
||||
try {
|
||||
const LogToStdoutSpecialFilename = '-';
|
||||
result = await module.exec({
|
||||
args: [testClassName, '-o', `${LogToStdoutSpecialFilename},${testOutputFormat}`],
|
||||
});
|
||||
|
||||
if (result.exitCode < 0)
|
||||
throw new ProgramError(result.exitCode);
|
||||
result.status = TestStatus.Completed;
|
||||
} catch (e) {
|
||||
result.status = e instanceof ProgramError ? TestStatus.Error : TestStatus.Crashed;
|
||||
result.stdout = e instanceof AbortedError ? e.stdout : result.stdout;
|
||||
}
|
||||
this.#privateWebApi.setTestResultData(
|
||||
testClassName, result.status, result.exitCode, result.stdout);
|
||||
#addTestOutput(testName, output) {
|
||||
const testData = this.#results.get(testName);
|
||||
testData.output.push(output);
|
||||
this.#testOutputChangedEventPrivate.fireEvent(testName, testData.output);
|
||||
}
|
||||
}
|
||||
|
||||
async run(testName, testOutputFormat) {
|
||||
|
||||
await this.#doRun(testName, testOutputFormat);
|
||||
this.#privateWebApi.setTestRunnerStatus(RunnerStatus.Completed);
|
||||
|
||||
}
|
||||
|
||||
async #getTestClassNames(module) {
|
||||
return (await module.exec()).stdout.trim().split(' ');
|
||||
}
|
||||
}
|
||||
|
||||
(() => {
|
||||
let privateWebApi;
|
||||
window.qtTestRunner = new WebApi(privateApi => privateWebApi = privateApi);
|
||||
|
||||
const parsed = parseQuery(location.search);
|
||||
const testName = parsed.get('qtestname');
|
||||
try {
|
||||
if (typeof testName !== 'undefined' && (typeof testName !== 'string' || testName === ''))
|
||||
throw new Error('The testName parameter is incorrect');
|
||||
|
||||
const testOutputFormat = (() => {
|
||||
const format = parsed.get('qtestoutputformat') ?? 'txt';
|
||||
console.log(format);
|
||||
if (-1 === ['txt', 'xml', 'lightxml', 'junitxml', 'tap'].indexOf(format))
|
||||
throw new Error(`Bad file format: ${format}`);
|
||||
return format;
|
||||
})();
|
||||
|
||||
const resourceLocator = new ResourceLocator('');
|
||||
const testRunner = new BatchedTestRunner(
|
||||
new ModuleLoader(new ResourceFetcher(resourceLocator), resourceLocator),
|
||||
privateWebApi
|
||||
);
|
||||
|
||||
testRunner.run(testName, testOutputFormat);
|
||||
} catch (e) {
|
||||
privateWebApi.setTestRunnerError(e.message);
|
||||
}
|
||||
})();
|
||||
|
119
util/wasm/batchedtestrunner/emrunadapter.js
Normal file
119
util/wasm/batchedtestrunner/emrunadapter.js
Normal file
@ -0,0 +1,119 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
import { RunnerStatus, TestStatus } from './batchedtestrunner.js';
|
||||
|
||||
// Sends messages to the running emrun instance via POST requests.
|
||||
export class EmrunCommunication {
|
||||
#indexOfMessage = 0;
|
||||
#postOutputPromises = [];
|
||||
|
||||
#post(body) {
|
||||
return fetch('stdio.html', {
|
||||
method: 'POST',
|
||||
body
|
||||
});
|
||||
}
|
||||
|
||||
// Returns a promise whose resolution signals that all outstanding traffic to the emrun instance
|
||||
// has been completed.
|
||||
waitUntilAllSent() {
|
||||
return Promise.all(this.#postOutputPromises);
|
||||
}
|
||||
|
||||
// Posts the exit status to the running emrun instance. Emrun will drop connection unless it is
|
||||
// run with --serve_after_exit, therefore this method will throw most of the times.
|
||||
postExit(status) {
|
||||
return this.#post(`^exit^${status}`);
|
||||
}
|
||||
|
||||
// Posts an indexed output chunk to the running emrun instance. Each consecutive call to this
|
||||
// method increments the output index by 1.
|
||||
postOutput(output) {
|
||||
const newPromise = this.#post(`^out^${this.#indexOfMessage++}^${output}`);
|
||||
this.#postOutputPromises.push(newPromise);
|
||||
newPromise.finally(() => {
|
||||
this.#postOutputPromises.splice(this.#postOutputPromises.indexOf(newPromise), 1);
|
||||
});
|
||||
return newPromise;
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps a test module runner; forwards its output and resolution state to the running emrun
|
||||
// instance.
|
||||
export class EmrunAdapter {
|
||||
#communication;
|
||||
#batchedTestRunner;
|
||||
#sentLines = 0;
|
||||
#onExitSent;
|
||||
|
||||
constructor(communication, batchedTestRunner, onExitSent) {
|
||||
this.#communication = communication;
|
||||
this.#batchedTestRunner = batchedTestRunner;
|
||||
this.#onExitSent = onExitSent;
|
||||
}
|
||||
|
||||
// Starts listening to test module runner's state changes. When the test module runner finishes
|
||||
// or reports output, sends suitable messages to the emrun instance.
|
||||
run() {
|
||||
this.#batchedTestRunner.onStatusChanged.addEventListener(
|
||||
status => this.#onRunnerStatusChanged(status));
|
||||
this.#batchedTestRunner.onTestStatusChanged.addEventListener(
|
||||
(test, status) => this.#onTestStatusChanged(test, status));
|
||||
this.#batchedTestRunner.onTestOutputChanged.addEventListener(
|
||||
(test, output) => this.#onTestOutputChanged(test, output));
|
||||
|
||||
const currentTest = [...this.#batchedTestRunner.results.entries()].find(
|
||||
entry => entry[1].status === TestStatus.Running)?.[0];
|
||||
|
||||
const output = this.#batchedTestRunner.results.get(currentTest)?.output;
|
||||
if (output)
|
||||
this.#onTestOutputChanged(testName, output);
|
||||
this.#onRunnerStatusChanged(this.#batchedTestRunner.status);
|
||||
}
|
||||
|
||||
#toExitCode(status) {
|
||||
switch (status) {
|
||||
case RunnerStatus.Error:
|
||||
return -1;
|
||||
case RunnerStatus.Passed:
|
||||
return 0;
|
||||
case RunnerStatus.Running:
|
||||
throw new Error('No exit code when still running');
|
||||
case RunnerStatus.TestCrashed:
|
||||
return -2;
|
||||
case RunnerStatus.TestsFailed:
|
||||
return this.#batchedTestRunner.numberOfFailed;
|
||||
}
|
||||
}
|
||||
|
||||
async #onRunnerStatusChanged(status) {
|
||||
if (RunnerStatus.Running === status)
|
||||
return;
|
||||
|
||||
const exit = this.#toExitCode(status);
|
||||
if (RunnerStatus.Error === status)
|
||||
this.#communication.postOutput(this.#batchedTestRunner.errorDetails);
|
||||
|
||||
await this.#communication.waitUntilAllSent();
|
||||
try {
|
||||
await this.#communication.postExit(exit);
|
||||
} catch {
|
||||
// no-op: The remote end will drop connection on exit.
|
||||
} finally {
|
||||
this.#onExitSent?.();
|
||||
}
|
||||
}
|
||||
|
||||
async #onTestOutputChanged(_, output) {
|
||||
const notSent = output.slice(this.#sentLines);
|
||||
for (const out of notSent)
|
||||
this.#communication.postOutput(out);
|
||||
this.#sentLines = output.length;
|
||||
}
|
||||
|
||||
async #onTestStatusChanged(_, status) {
|
||||
if (status === TestStatus.Running)
|
||||
this.#sentLines = 0;
|
||||
}
|
||||
}
|
@ -145,8 +145,7 @@ export class CompiledModule {
|
||||
params.arguments = parameters?.args;
|
||||
let data = '';
|
||||
params.print = (out) => {
|
||||
if (parameters?.printStdout === true)
|
||||
console.log(out);
|
||||
parameters?.onStdout?.(out);
|
||||
data += `${out}\n`;
|
||||
};
|
||||
params.printErr = () => { };
|
||||
|
59
util/wasm/batchedtestrunner/qwasmtestmain.js
Normal file
59
util/wasm/batchedtestrunner/qwasmtestmain.js
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
import { BatchedTestRunner } from './batchedtestrunner.js'
|
||||
import { EmrunAdapter, EmrunCommunication } from './emrunadapter.js'
|
||||
import {
|
||||
ModuleLoader,
|
||||
ResourceFetcher,
|
||||
ResourceLocator,
|
||||
} from './qwasmjsruntime.js';
|
||||
import { parseQuery } from './util.js';
|
||||
|
||||
(() => {
|
||||
const setPageTitle = (useEmrun, testName, isBatch) => {
|
||||
document.title = 'Qt WASM test runner';
|
||||
if (useEmrun || testName || isBatch) {
|
||||
document.title += `(${[
|
||||
...[useEmrun ? ['emrun'] : []],
|
||||
...[testName ? ['test=' + testName] : []],
|
||||
...[isBatch ? ['batch'] : []]
|
||||
].flat().join(", ")})`;
|
||||
}
|
||||
}
|
||||
|
||||
const parsed = parseQuery(location.search);
|
||||
const testName = parsed.get('qtestname');
|
||||
const isBatch = parsed.get('qbatchedtest') !== undefined;
|
||||
const useEmrun = parsed.get('quseemrun') !== undefined;
|
||||
|
||||
if (testName === undefined) {
|
||||
if (!isBatch)
|
||||
throw new Error('The qtestname parameter is required if not running a batch');
|
||||
} else if (testName === '') {
|
||||
throw new Error(`The qtestname=${testName} parameter is incorrect`);
|
||||
}
|
||||
|
||||
const testOutputFormat = (() => {
|
||||
const format = parsed.get('qtestoutputformat') ?? 'txt';
|
||||
if (-1 === ['txt', 'xml', 'lightxml', 'junitxml', 'tap'].indexOf(format))
|
||||
throw new Error(`Bad file format: ${format}`);
|
||||
return format;
|
||||
})();
|
||||
|
||||
const resourceLocator = new ResourceLocator('');
|
||||
const testRunner = new BatchedTestRunner(
|
||||
new ModuleLoader(new ResourceFetcher(resourceLocator), resourceLocator),
|
||||
);
|
||||
window.qtTestRunner = testRunner;
|
||||
|
||||
if (useEmrun) {
|
||||
const adapter = new EmrunAdapter(new EmrunCommunication(), testRunner, () => {
|
||||
window.close();
|
||||
});
|
||||
adapter.run();
|
||||
}
|
||||
setPageTitle(useEmrun, testName, isBatch);
|
||||
|
||||
testRunner.run(isBatch, testName, testOutputFormat);
|
||||
})();
|
Loading…
Reference in New Issue
Block a user