org.eclipse.n4js.mangelhaft
Version:
An xUnit Testing Framework for N4JS.
322 lines (319 loc) • 9.23 kB
JavaScript
// Generated by N4JS transpiler; for copyright see original N4JS source file.
import 'n4js-runtime'
import {AssertionError} from 'org.eclipse.n4js.mangelhaft.assert/src-gen/org/eclipse/n4js/mangelhaft/assert/AssertionError.js'
import {PreconditionNotMet} from 'org.eclipse.n4js.mangelhaft.assert/src-gen/org/eclipse/n4js/mangelhaft/precondition/PreconditionNotMet.js'
import {ResultGroup} from './types/ResultGroup.js'
import {ResultGroups} from './types/ResultGroups.js'
import {TestResult} from './types/TestResult.js'
import {TestSpy} from './types/TestSpy.js'
export class TestExecutor extends N4Object {
constructor(spy) {
super();
this.spy = undefined;
this.constext = undefined;
this.spy = spy;
}
static getStringFromAbiguous(thing) {
let str;
if (thing === null) {
str = "null";
} else if (typeof thing === "object") {
str = Object.prototype.hasOwnProperty.call(thing, "toString") ? "" + thing : "prototypeless object";
} else {
str = "" + thing;
}
return str;
}
static generateFailureTestResult(ex, description) {
const status = this.getStatus(ex);
const error = this.getError(ex, description);
const reason = this.getReason(error, description);
const trace = this.getTrace(error);
const expected = this.getStringifiedOwnProperty(error, "expected");
const actual = this.getStringifiedOwnProperty(error, "actual");
let tr = new TestResult({
testStatus: status,
message: reason,
trace: trace,
description: description,
expected: expected,
actual: actual
});
return tr;
}
static getReason(error, description) {
if (error.message) {
return String(error);
}
let reason = error.toString();
if (reason.charAt(0) === "[") {
reason = error.name ? `${error.name} : ${description}` : description;
}
return reason;
}
static getStringifiedOwnProperty(object, name) {
if (!object.hasOwnProperty(name)) {
return undefined;
}
const prop = object[name];
const res = this.getStringFromAbiguous(prop);
return res;
}
static getError(ex, description) {
if (!ex) {
return new Error("Unknown error: " + description);
}
if (typeof ex === "string") {
return new Error(ex);
}
if (ex instanceof AssertionError) {
return ex;
}
return ex;
}
static getStatus(ex) {
if (ex instanceof AssertionError) {
return 'FAILED';
}
if (ex instanceof PreconditionNotMet) {
return 'SKIPPED_PRECONDITION';
}
if (ex instanceof N4ApiNotImplementedError) {
return 'SKIPPED_NOT_IMPLEMENTED';
}
return 'ERROR';
}
static getTrace(e) {
let trace;
if (e['stack']) {
const stack = e['stack'];
if (typeof stack === "string") {
trace = (stack).split("\n");
} else if (Array.isArray(stack)) {
trace = stack;
} else {
trace = [
(stack).toString()
];
}
trace = trace.map((line)=>line.trim());
}
return trace;
}
handleFixme(testObject, scope, testRes) {
if (testObject.fixme && (!testObject.fixmeScopes || testObject.fixmeScopes.has(scope))) {
if (testRes.testStatus === 'PASSED') {
testRes.testStatus = 'FAILED';
testRes.message = "Test marked with @Fixme annotation but was successful. Issue blocking test has probably been fixed. Try removing annotation.";
if (testObject.fixmeReason != null) {
testRes.message += " (reason was '" + testObject.fixmeReason + "')";
}
testRes.trace = [
String(testRes)
];
} else if (testRes.testStatus === 'FAILED' || testRes.testStatus === 'ERROR') {
testRes.testStatus = 'SKIPPED_FIXME';
testRes.message = testObject.fixmeReason;
testRes.actual = testRes.expected = testRes.trace = null;
}
}
return testRes;
}
async callAllGrouped(instrumentedTest, testMethodDescriptors) {
for(const batch of testMethodDescriptors) {
await this.callAll(instrumentedTest, batch);
}
}
async callAll(instrumentedTest, testMethodDescriptors) {
if (testMethodDescriptors) {
const runTestMethod = getExecutingFunction(instrumentedTest);
await Promise.all(testMethodDescriptors.map(runTestMethod));
}
}
getAncestorTestMethods(iTest, testMethodName) {
let testMethods = [];
let node = iTest;
while(node.parent) {
node = node.parent;
}
do {
let nodeObj = node;
const nodeTestMethods = nodeObj[testMethodName];
if (nodeTestMethods && nodeTestMethods.length) {
testMethods.push(nodeTestMethods);
}
} while(node = node.child);
return testMethods;
}
async runTestAsync(instrumentedTest) {
return await this.runTestsAsync([
instrumentedTest
]);
}
async runGroup(iTest, scope) {
let testResults = [];
await this.spy.groupStarted.dispatch([
iTest
]);
if (!iTest.error) {
try {
const beforeAlls = this.getAncestorTestMethods(iTest, "beforeAlls");
await this.callAllGrouped(iTest, beforeAlls);
await this.runBeforesTestsAfters(testResults, iTest, scope);
const afterAlls = this.getAncestorTestMethods(iTest, "afterAlls").reverse();
await this.callAllGrouped(iTest, afterAlls);
} catch(error) {
let results = await this.errorTests(iTest, error);
testResults = testResults.concat(results);
}
} else {
testResults = await this.errorTests(iTest, iTest.error);
}
const result = new ResultGroup(testResults, `${iTest.name}`);
await this.spy.groupFinished.dispatch([
iTest,
result
]);
return result;
}
async runBeforesTestsAfters(testResults, iTest, scope) {
const numTests = iTest.tests.length;
for(let ii = 0;ii < numTests;++ii) {
let testMethod = iTest.tests[ii];
let start, end;
let testRes;
try {
await this.spy.testStarted.dispatch([
iTest,
testMethod
]);
start = new Date().getTime();
if (!testMethod.ignore) {
try {
const befores = this.getAncestorTestMethods(iTest, "befores");
await this.callAllGrouped(iTest, befores);
await this.callAll(iTest, [
testMethod
]);
testRes = new TestResult({
testStatus: 'PASSED',
description: testMethod.name
});
} finally {
const afters = this.getAncestorTestMethods(iTest, "afters").reverse();
await this.callAllGrouped(iTest, afters);
}
} else {
testRes = new TestResult({
testStatus: 'SKIPPED_IGNORE',
message: testMethod.ignoreReason,
description: testMethod.name
});
}
end = new Date().getTime();
} catch(er) {
end = new Date().getTime();
testRes = TestExecutor.generateFailureTestResult(er, testMethod.name);
}
testRes.elapsedTime = end - start;
testRes = this.handleFixme(testMethod, scope, testRes);
await this.spy.testFinished.dispatch([
iTest,
testMethod,
testRes,
async()=>{
let allTests = iTest.tests;
iTest.tests = [
testMethod
];
try {
await this.runTestsAsync([
iTest
]);
} finally {
iTest.tests = allTests;
}
}
]);
testResults.push(testRes);
}
}
async runTestsAsync(instrumentedTests, scope) {
let results = [];
for(let test of instrumentedTests) {
if (test) {
if (test.hasParameterizedTests) {
let pResults = [];
await this.spy.parameterizedGroupsStarted.dispatch([
test
]);
for(let ptest of test.parameterizedTests) {
let testRes = await this.runGroup(ptest, scope);
pResults.push(testRes);
results.push(testRes);
}
await this.spy.parameterizedGroupsFinished.dispatch([
new ResultGroups(pResults)
]);
} else {
let testRes = await this.runGroup(test, scope);
results.push(testRes);
}
}
}
let resultGroups = new ResultGroups(results);
return resultGroups;
}
async errorTests(instrumentedTest, error) {
const testResults = [];
const len = instrumentedTest.tests.length;
for(let ii = 0;ii < len;++ii) {
const test = instrumentedTest.tests[ii];
await this.spy.testStarted.dispatch([
instrumentedTest,
test
]);
const testResult = TestExecutor.generateFailureTestResult(error, test.name);
testResult.elapsedTime = 0;
await this.spy.testFinished.dispatch([
instrumentedTest,
test,
testResult
]);
testResults.push(testResult);
}
return testResults;
}
static get n4type() {
return $getReflectionForClass(this, '["TestExecutor","org/eclipse/n4js/mangelhaft/TestExecutor","org.eclipse.n4js.mangelhaft",["f.spy","f.constext"],{"f.spy":["Inject"]}]');
}
}
Object.defineProperty(TestExecutor, Symbol.for('org.eclipse.n4js/diInfo'), {
value: {
fieldsInjectedTypes: [
{
name: 'spy',
get type() {
return TestSpy;
}
}
]
}
});
function getExecutingFunction(instrumentedTest) {
return (testMethodDescriptor)=>(new Promise((resolve, reject)=>{
const pr = Promise.resolve(testMethodDescriptor.value.call(instrumentedTest.testObject));
let timeoutId;
if (!process.env.N4JS_RT_mangelhaft_timeout_disabled) {
timeoutId = setTimeout(()=>reject(new Error(`Test object ${testMethodDescriptor.name} timed out after ${testMethodDescriptor.timeout} milliseconds`)), testMethodDescriptor.timeout);
}
pr.then((res)=>{
clearTimeout(timeoutId);
resolve();
}, (err)=>{
clearTimeout(timeoutId);
reject(err);
});
}));
}
//# sourceMappingURL=TestExecutor.map