@firfi/quint-connect
Version:
Model-based testing framework connecting Quint specifications to TypeScript implementations
66 lines • 3.59 kB
JavaScript
import { Array as Arr, Effect } from "effect";
import { resolveTraceGenerationPolicy } from "../cli/trace-generation-policy.js";
import { TraceGeneration, traceGenerationLayer } from "../cli/trace-generation.js";
import { defaultConfig } from "../driver/types.js";
import { buildPicksDecoders, decodeReplayStep, extractReplayAction } from "./replay-actions.js";
import { dispatchReplayAction } from "./replay-dispatch.js";
import { actionContext, jsonReplacer, NoTracesError, StateMismatchError, TraceReplayError, traceReplayError } from "./replay-errors.js";
import { checkReplayState } from "./state-check.js";
import { stripMetadata } from "./trace-state.js";
export { jsonReplacer, NoTracesError, StateMismatchError, stripMetadata, TraceReplayError };
/** @internal */
export const replayTrace = (trace, traceIndex, driver, config, stateCheck, seed) => Effect.gen(function* () {
const picksDecoders = buildPicksDecoders(driver.actions);
for (const [stepIndex, rawState] of trace.states.entries()) {
const stepContext = { traceIndex, stepIndex };
const decodedStep = stateCheck === undefined
? undefined
: yield* decodeReplayStep(rawState, config, stepContext);
const step = decodedStep ?? (yield* extractReplayAction(rawState, config.nondetPath ?? [], stepContext));
const { action, nondetPicks } = step;
const context = actionContext(stepContext, action);
// Defensive: skip step 0 if actionTaken is empty (both backends normally produce "init").
if (action === "") {
if (stepIndex === 0)
continue;
return yield* traceReplayError(context, `Anonymous action at trace ${traceIndex}, step ${stepIndex}`);
}
const dispatchResult = yield* dispatchReplayAction(driver.actions, action, nondetPicks, context, picksDecoders);
if (dispatchResult === "skipped")
continue;
if (stateCheck !== undefined && decodedStep !== undefined) {
yield* checkReplayState({
step: decodedStep,
driver,
stateCheck,
context,
seed
});
}
}
});
/** Resolve the seed. Always generates a real seed so failures are reproducible. */
const resolveSeed = (opts) => {
return opts.seed ?? process.env["QUINT_SEED"]
?? `0x${Math.floor(Math.random() * 0xFFFFFFFF).toString(16).padStart(8, "0")}`;
};
export const quintRunWithTraceGeneration = (opts) => Effect.gen(function* () {
const seed = resolveSeed(opts);
const traceOpts = { ...opts, seed };
const generationPolicy = resolveTraceGenerationPolicy(traceOpts);
const traceGeneration = yield* TraceGeneration;
const traces = yield* traceGeneration.generate(traceOpts);
if (traces.length === 0) {
return yield* new NoTracesError({
message: `${generationPolicy.command} produced no traces`
});
}
const results = yield* Effect.forEach(Arr.map(traces, (trace, traceIndex) => ({ trace, traceIndex })), ({ trace, traceIndex }) => Effect.gen(function* () {
const driver = yield* opts.driverFactory.create();
const config = { ...defaultConfig, ...driver.config?.() };
yield* replayTrace(trace, traceIndex, driver, config, opts.stateCheck, seed);
}), { concurrency: opts.concurrency ?? 1 });
return { tracesReplayed: results.length, seed };
});
export const quintRun = (opts) => quintRunWithTraceGeneration(opts).pipe(Effect.provide(traceGenerationLayer));
//# sourceMappingURL=runner.js.map