fast-check
Version:
Property based testing framework for JavaScript (like QuickCheck)
194 lines (193 loc) • 7.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.schedulerFor = exports.scheduler = void 0;
const symbols_1 = require("../symbols");
const Arbitrary_1 = require("./definition/Arbitrary");
const Shrinkable_1 = require("./definition/Shrinkable");
const stringify_1 = require("../../utils/stringify");
const TextEscaper_1 = require("./helpers/TextEscaper");
class SchedulerImplem {
constructor(act, taskSelector) {
this.act = act;
this.taskSelector = taskSelector;
this.lastTaskId = 0;
this.sourceTaskSelector = taskSelector.clone();
this.scheduledTasks = [];
this.triggeredTasks = [];
}
static buildLog(reportItem) {
return `[task\${${reportItem.taskId}}] ${reportItem.label.length !== 0 ? `${reportItem.schedulingType}::${reportItem.label}` : reportItem.schedulingType} ${reportItem.status}${reportItem.outputValue !== undefined ? ` with value ${TextEscaper_1.escapeForTemplateString(reportItem.outputValue)}` : ''}`;
}
log(schedulingType, taskId, label, metadata, status, data) {
this.triggeredTasks.push({
status,
schedulingType,
taskId,
label,
metadata,
outputValue: data !== undefined ? stringify_1.stringify(data) : undefined,
});
}
scheduleInternal(schedulingType, label, task, metadata, thenTaskToBeAwaited) {
let trigger = null;
const taskId = ++this.lastTaskId;
const scheduledPromise = new Promise((resolve, reject) => {
trigger = () => {
(thenTaskToBeAwaited ? task.then(() => thenTaskToBeAwaited()) : task).then((data) => {
this.log(schedulingType, taskId, label, metadata, 'resolved', data);
return resolve(data);
}, (err) => {
this.log(schedulingType, taskId, label, metadata, 'rejected', err);
return reject(err);
});
};
});
this.scheduledTasks.push({
original: task,
scheduled: scheduledPromise,
trigger: trigger,
schedulingType,
taskId,
label,
metadata,
});
return scheduledPromise;
}
schedule(task, label, metadata) {
return this.scheduleInternal('promise', label || '', task, metadata);
}
scheduleFunction(asyncFunction) {
return (...args) => this.scheduleInternal('function', `${asyncFunction.name}(${args.map(stringify_1.stringify).join(',')})`, asyncFunction(...args), undefined);
}
scheduleSequence(sequenceBuilders) {
const status = { done: false, faulty: false };
const dummyResolvedPromise = { then: (f) => f() };
let resolveSequenceTask = () => { };
const sequenceTask = new Promise((resolve) => (resolveSequenceTask = resolve));
sequenceBuilders
.reduce((previouslyScheduled, item) => {
const [builder, label, metadata] = typeof item === 'function' ? [item, item.name, undefined] : [item.builder, item.label, item.metadata];
return previouslyScheduled.then(() => {
const scheduled = this.scheduleInternal('sequence', label, dummyResolvedPromise, metadata, () => builder());
scheduled.catch(() => {
status.faulty = true;
resolveSequenceTask();
});
return scheduled;
});
}, dummyResolvedPromise)
.then(() => {
status.done = true;
resolveSequenceTask();
}, () => {
});
return Object.assign(status, {
task: Promise.resolve(sequenceTask).then(() => {
return { done: status.done, faulty: status.faulty };
}),
});
}
count() {
return this.scheduledTasks.length;
}
async internalWaitOne() {
if (this.scheduledTasks.length === 0) {
throw new Error('No task scheduled');
}
const taskIndex = this.taskSelector.nextTaskIndex(this.scheduledTasks);
const [scheduledTask] = this.scheduledTasks.splice(taskIndex, 1);
scheduledTask.trigger();
try {
await scheduledTask.scheduled;
}
catch (_err) {
}
}
async waitOne() {
await this.act(async () => await this.internalWaitOne());
}
async waitAll() {
while (this.scheduledTasks.length > 0) {
await this.waitOne();
}
}
report() {
return [
...this.triggeredTasks,
...this.scheduledTasks.map((t) => ({
status: 'pending',
schedulingType: t.schedulingType,
taskId: t.taskId,
label: t.label,
metadata: t.metadata,
})),
];
}
toString() {
return ('schedulerFor()`\n' +
this.report()
.map(SchedulerImplem.buildLog)
.map((log) => `-> ${log}`)
.join('\n') +
'`');
}
[symbols_1.cloneMethod]() {
return new SchedulerImplem(this.act, this.sourceTaskSelector);
}
}
class SchedulerArbitrary extends Arbitrary_1.Arbitrary {
constructor(act) {
super();
this.act = act;
}
generate(mrng) {
const buildNextTaskIndex = (r) => {
return {
clone: () => buildNextTaskIndex(r.clone()),
nextTaskIndex: (scheduledTasks) => {
return r.nextInt(0, scheduledTasks.length - 1);
},
};
};
return new Shrinkable_1.Shrinkable(new SchedulerImplem(this.act, buildNextTaskIndex(mrng.clone())));
}
}
function scheduler(constraints) {
const { act = (f) => f() } = constraints || {};
return new SchedulerArbitrary(act);
}
exports.scheduler = scheduler;
function schedulerFor(customOrderingOrConstraints, constraintsOrUndefined) {
const { act = (f) => f() } = Array.isArray(customOrderingOrConstraints)
? constraintsOrUndefined || {}
: customOrderingOrConstraints || {};
const buildSchedulerFor = function (ordering) {
const buildNextTaskIndex = () => {
let numTasks = 0;
return {
clone: () => buildNextTaskIndex(),
nextTaskIndex: (scheduledTasks) => {
if (ordering.length <= numTasks) {
throw new Error(`Invalid schedulerFor defined: too many tasks have been scheduled`);
}
const taskIndex = scheduledTasks.findIndex((t) => t.taskId === ordering[numTasks]);
if (taskIndex === -1) {
throw new Error(`Invalid schedulerFor defined: unable to find next task`);
}
++numTasks;
return taskIndex;
},
};
};
return new SchedulerImplem(act, buildNextTaskIndex());
};
if (Array.isArray(customOrderingOrConstraints)) {
return buildSchedulerFor(customOrderingOrConstraints);
}
else {
return function (_strs, ...ordering) {
return buildSchedulerFor(ordering);
};
}
}
exports.schedulerFor = schedulerFor;