UNPKG

@firfi/quint-connect

Version:

Model-based testing framework connecting Quint specifications to TypeScript implementations

66 lines 3.59 kB
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