org.eclipse.n4js.mangelhaft
Version:
An xUnit Testing Framework for N4JS.
233 lines (230 loc) • 7.46 kB
JavaScript
// Generated by N4JS transpiler; for copyright see original N4JS source file.
import 'n4js-runtime'
import {N4Injector} from 'n4js-runtime/src-gen/n4js/lang/N4Injector.js'
import {InstrumentedTest} from './InstrumentedTest.js'
import {TestExecutor} from './TestExecutor.js'
import {PreconditionNotMet} from 'org.eclipse.n4js.mangelhaft.assert/src-gen/org/eclipse/n4js/mangelhaft/precondition/PreconditionNotMet.js'
import {ResultGroups} from './types/ResultGroups.js'
import {TestFunctionType} from './types/TestFunctionType.js'
import {TestMethodDescriptor} from './types/TestMethodDescriptor.js'
import {TestSpy} from './types/TestSpy.js'
import {readJsonFile} from './util/fs.js'
import {findPackageJson} from './util/npm.js'
import {Module} from 'node:module'
import * as lib_path from 'path'
const MAX_GROUPS_PER_TEST_BATCH = 10;
const notNull = (thing)=>thing !== null;
export class TestController extends N4Object {
constructor() {
super();
this.spy = undefined;
this.executor = undefined;
this.injector = undefined;
this.reportersVal = [];
}
get reporters() {
return this.reportersVal;
}
set reporters(reporters) {
reporters.forEach(function(reporter) {
let dummy = reporter.register();
dummy;
});
this.reportersVal = reporters;
}
async runGroups(testCatalog, numTests, scope) {
if (!testCatalog) {
throw new Error("TestController::runGroups called with a null testCatalog");
}
const testInfos = testCatalog.testDescriptors.sort((x, y)=>{
let xVal = x.fqn ? x.fqn : x.module;
let yVal = y.fqn ? y.fqn : y.module;
return xVal.localeCompare(yVal);
});
if (numTests === undefined) {
numTests = testInfos.reduce((acc, info)=>acc + info.testMethods.length, 0);
}
try {
await this.spy.testingStarted.dispatch([
testInfos.length,
testCatalog.sessionId,
numTests
]);
} catch(ex) {
console.log("testingStarted.dispatch is bad", ex);
}
const res = await this.runInBatches(testCatalog, testInfos, scope);
await this.spy.testingFinished.dispatch([
res
]);
return res;
}
async runInBatches(testCatalog, testInfos, scope) {
let res;
const batchedTestInfos = [];
for(let batchedCount = 0;batchedCount < testInfos.length;batchedCount += MAX_GROUPS_PER_TEST_BATCH) {
const batch = testInfos.slice(batchedCount, batchedCount + MAX_GROUPS_PER_TEST_BATCH);
batchedTestInfos.push(batch);
}
const resultGroups = [];
for(let numOfBatches = 0;numOfBatches < batchedTestInfos.length;++numOfBatches) {
try {
const testInfosBatch = batchedTestInfos[numOfBatches];
const instrumentedTestsBatch = await this.instrumentBatch(testCatalog, testInfosBatch, resultGroups);
res = await this.executor.runTestsAsync(instrumentedTestsBatch, scope);
resultGroups.push(res);
} catch(er) {
console.error(er);
throw er;
}
}
res = ResultGroups.concatArray(resultGroups);
return res;
}
async instrumentBatch(testCatalog, testInfosBatch, resultGroups) {
let result = [];
for(const testInfo of testInfosBatch) {
const instrumentedTest = await this.instrument(testCatalog, testInfo, resultGroups);
result = result.concat(instrumentedTest);
}
return result;
}
async instrument(testCatalog, info, resultGroups) {
let testClasses;
const originOrPackageName = info.origin || testCatalog.packageName;
const parts = info.fqn.split("/");
const ctorName = parts.pop();
const moduleName = parts.join("/");
try {
let groupModule;
try {
const mod = `${testCatalog.location}/${moduleName}.js`;
groupModule = await import(mod);
info.filePath = Module.createRequire(import.meta.url).resolve(mod);
} catch(exc) {
const packageJsonPath = findPackageJson(process.cwd());
if (!packageJsonPath || readJsonFile(packageJsonPath).name !== originOrPackageName) {
throw exc;
}
const parts = moduleName.split("/");
parts.unshift(lib_path.dirname(packageJsonPath));
info.filePath = `${parts.join(lib_path.sep)}.js`;
groupModule = await import(info.filePath);
}
testClasses = [
groupModule[ctorName]
];
} catch(ex) {
const errorTest = this.createErrorTest(info, originOrPackageName + "/" + moduleName, null, ex);
return [
errorTest
];
}
if (!testClasses) {
const errorTest = this.createErrorTest(info, originOrPackageName + "/" + parts.join("/"));
return [
errorTest
];
}
let instrumentedTests = [];
for(const testClass of testClasses) {
if (!testClass) {
const errorTest = this.createErrorTest(info, originOrPackageName + "/" + moduleName, null, new Error("Empty object loaded (is the test class exported?)"));
instrumentedTests.push(errorTest);
continue;
}
try {
const testInjector = this.getTestInjector(testClass);
let classITO = InstrumentedTest.getInstrumentedTest(testClass, info, testInjector, this);
instrumentedTests.push(classITO);
} catch(ex2) {
const stubTestInstrumentationError = new InstrumentedTest(testClass, info).setTestObject(new N4Object()).setError(ex2);
instrumentedTests.push(stubTestInstrumentationError);
}
}
const result = instrumentedTests.filter(notNull);
return result;
}
createErrorTest(info, loadPath, testObject, originalError) {
if (!testObject) {
testObject = this.getInstrumentedTestStub(info);
}
const error = originalError ? originalError : new Error("could not load test " + loadPath);
testObject.setError(error);
return testObject;
}
getInstrumentedTestStub(info) {
const testObject = new InstrumentedTest();
info.module = info.module || "";
info.fqn = info.fqn || info.module.replace(/\//g, ".") + ".*";
testObject.load(N4Object, info).setTestObject(new N4Object());
if (info.testMethods) {
testObject.tests = info.testMethods.map((methName)=>new TestMethodDescriptor({
name: methName,
type: TestFunctionType.TEST,
value: function() {}
}));
} else {
testObject.tests = [
new TestMethodDescriptor({
name: "",
type: TestFunctionType.TEST,
value: function() {}
})
];
}
return testObject;
}
getTestInjector(testClass) {
let testInjector;
let diClass = testClass;
let testType = testClass.n4type;
while(testType) {
if (testType.hasAnnotation("GenerateInjector")) {
if (testType.hasAnnotation("WithParentInjector")) {
if (!this.injector.canBeParentOf(diClass)) {
throw new PreconditionNotMet("Test called with incompatible parent injector");
}
testInjector = N4Injector.of(diClass, this.injector);
} else {
testInjector = N4Injector.of(diClass);
}
break;
}
testType = testType.n4superType;
diClass = Object.getPrototypeOf(diClass);
}
if (!testType) {
testInjector = this.injector;
}
return testInjector;
}
static get n4type() {
return $getReflectionForClass(this, '["TestController","org/eclipse/n4js/mangelhaft/TestController","org.eclipse.n4js.mangelhaft",["f.spy","f.executor","f.injector","f.reportersVal"],{"f.spy":["Inject"],"f.executor":["Inject"],"f.injector":["Inject"]}]');
}
}
Object.defineProperty(TestController, Symbol.for('org.eclipse.n4js/diInfo'), {
value: {
fieldsInjectedTypes: [
{
name: 'spy',
get type() {
return TestSpy;
}
},
{
name: 'executor',
get type() {
return TestExecutor;
}
},
{
name: 'injector',
get type() {
return N4Injector;
}
}
]
}
});
//# sourceMappingURL=TestController.map