Liking cljdoc? Tell your friends :D

de.levering-it.missionary-testkit


*is-deterministic*clj/s

True when inside with-determinism scope. Use this to check if deterministic mode is active (m/sleep, m/timeout, m/cpu, m/blk are virtualized).

True when inside with-determinism scope. Use this to check if deterministic
mode is active (m/sleep, m/timeout, m/cpu, m/blk are virtualized).
raw docstring

*scheduler*clj/s

Dynamically bound to the current TestScheduler in deterministic tests.

Dynamically bound to the current TestScheduler in deterministic tests.
raw docstring

advance!clj/s

(advance! sched dt-ms)

Advance virtual time by dt-ms (>=0), enqueue due timers, then tick.

Binds scheduler to sched for the duration of execution.

Advance virtual time by dt-ms (>=0), enqueue due timers, then tick.

Binds *scheduler* to sched for the duration of execution.
raw docstring

advance-to!clj/s

(advance-to! sched t)

Set time to t (>= now), enqueue due timers, then tick. Returns number of microtasks executed by tick.

Binds scheduler to sched for the duration of execution.

Set time to t (>= now), enqueue due timers, then tick. Returns number of microtasks executed by tick.

Binds *scheduler* to sched for the duration of execution.
raw docstring

blk-executorclj/s≠

(blk-executor)
clj

Deterministic blocking executor for m/via. Lane :blk retained for introspection/trace. Must be called inside with-determinism with a scheduler bound to scheduler.

Deterministic blocking executor for m/via. Lane :blk retained for introspection/trace.
Must be called inside with-determinism with a scheduler bound to *scheduler*.
raw docstring

budget-exceededclj/s


cancel!clj/s

(cancel! x)

Cancel a Job handle.

Cancel a Job handle.
raw docstring

check-interleavingclj/s

(check-interleaving task-fn
                    {:keys [num-tests seed property max-steps max-time-ms]
                     :or {num-tests 100 max-steps 10000 max-time-ms 60000}})

Run a task with many different interleavings to find failures.

IMPORTANT: Must be called inside a with-determinism body.

task-fn should be a 0-arg function that returns a fresh task for each test. This ensures mutable state (like atoms) is reset between iterations.

Options:

  • :num-tests - number of different interleavings to try (default 100)
  • :seed - base seed for RNG (default: current time)
  • :property - (fn [result] boolean) - returns true if result is valid
  • :max-steps - max scheduler steps per run (default 10000)
  • :max-time-ms - max virtual time per run (default 60000)

Returns on success: {:ok? true :seed base seed used (for reproducibility) :iterations-run number of iterations completed}

Returns on failure: {:ok? false :kind :exception | :property-failed :seed seed used for this iteration :schedule schedule that caused failure (for replay) :trace full trace :iteration which iteration failed :error exception (present when :kind is :exception) :value result value (present when :kind is :property-failed)}

Note: For reproducible tests, always specify :seed. Without it, the current system time is used, making results non-reproducible across runs.

Run a task with many different interleavings to find failures.

IMPORTANT: Must be called inside a with-determinism body.

task-fn should be a 0-arg function that returns a fresh task for each test.
This ensures mutable state (like atoms) is reset between iterations.

Options:
- :num-tests   - number of different interleavings to try (default 100)
- :seed        - base seed for RNG (default: current time)
- :property    - (fn [result] boolean) - returns true if result is valid
- :max-steps   - max scheduler steps per run (default 10000)
- :max-time-ms - max virtual time per run (default 60000)

Returns on success:
{:ok?            true
 :seed           base seed used (for reproducibility)
 :iterations-run number of iterations completed}

Returns on failure:
{:ok?       false
 :kind      :exception | :property-failed
 :seed      seed used for this iteration
 :schedule  schedule that caused failure (for replay)
 :trace     full trace
 :iteration which iteration failed
 :error     exception (present when :kind is :exception)
 :value     result value (present when :kind is :property-failed)}

Note: For reproducible tests, always specify :seed. Without it, the current
system time is used, making results non-reproducible across runs.
raw docstring

clockclj/s

(clock)

Returns the current time in milliseconds.

  • Production (outside with-determinism): System/currentTimeMillis (JVM) or js/Date.now (CLJS)
  • Test mode (inside with-determinism): virtual time from the scheduler

Use this in production code that needs timestamps, so tests can control time:

(defn log-with-timestamp [msg] {:time (mt/clock) :msg msg})

Returns the current time in milliseconds.

- Production (outside with-determinism): System/currentTimeMillis (JVM) or js/Date.now (CLJS)
- Test mode (inside with-determinism): virtual time from the scheduler

Use this in production code that needs timestamps, so tests can control time:

(defn log-with-timestamp [msg]
  {:time (mt/clock) :msg msg})
raw docstring

collectclj/s

(collect flow)
(collect flow {:keys [xf timeout-ms] :or {xf nil}})

Convenience: consume a flow into a task yielding a vector (or reduced value).

(mt/collect flow {:xf (take 10) :timeout-ms 1000 :label "optional"})

Notes:

  • If :timeout-ms is provided, wraps with mt/timeout and returns ::mt/timeout on expiry.
Convenience: consume a flow into a task yielding a vector (or reduced value).

(mt/collect flow {:xf (take 10)
                  :timeout-ms 1000
                  :label "optional"})

Notes:
- If :timeout-ms is provided, wraps with mt/timeout and returns ::mt/timeout on expiry.
raw docstring

cpu-executorclj/s≠

(cpu-executor)
clj

Deterministic CPU executor for m/via. Lane :cpu retained for introspection/trace. Must be called inside with-determinism with a scheduler bound to scheduler.

Deterministic CPU executor for m/via. Lane :cpu retained for introspection/trace.
Must be called inside with-determinism with a scheduler bound to *scheduler*.
raw docstring

deadlockclj/s


done?clj/s

(done? job)

Has the job completed (success or failure)?

Has the job completed (success or failure)?
raw docstring

executorclj/s≠

(executor)
clj

Deterministic java.util.concurrent.Executor. Enqueues runnables as scheduler microtasks. Must be called inside with-determinism with a scheduler bound to scheduler.

Deterministic java.util.concurrent.Executor. Enqueues runnables as scheduler microtasks.
Must be called inside with-determinism with a scheduler bound to *scheduler*.
raw docstring

explore-interleavingsclj/s

(explore-interleavings task-fn
                       {:keys [num-samples seed max-steps]
                        :or {num-samples 100 max-steps 10000}})

Explore different interleavings of a task and return a summary.

IMPORTANT: Must be called inside a with-determinism body.

task-fn should be a 0-arg function that returns a fresh task for each test. This ensures mutable state (like atoms) is reset between iterations.

Options:

  • :num-samples - number of different interleavings to try (default 100)
  • :seed - base seed for RNG (default: current time)
  • :max-steps - max scheduler steps per run (default 10000)

Returns: {:unique-results - count of distinct results seen :results - vector of {:result r :micro-schedule s} maps :seed - the base seed used (for reproducibility)}

Note: For reproducible tests, always specify :seed. Without it, the current system time is used, making results non-reproducible across runs.

Explore different interleavings of a task and return a summary.

IMPORTANT: Must be called inside a with-determinism body.

task-fn should be a 0-arg function that returns a fresh task for each test.
This ensures mutable state (like atoms) is reset between iterations.

Options:
- :num-samples - number of different interleavings to try (default 100)
- :seed        - base seed for RNG (default: current time)
- :max-steps   - max scheduler steps per run (default 10000)

Returns:
{:unique-results - count of distinct results seen
 :results        - vector of {:result r :micro-schedule s} maps
 :seed           - the base seed used (for reproducibility)}

Note: For reproducible tests, always specify :seed. Without it, the current
system time is used, making results non-reproducible across runs.
raw docstring

ICancellableclj/sprotocol

-cancel!clj/s

(-cancel! x)

idleclj/s


illegal-blocking-emissionclj/s


illegal-transferclj/s


make-schedulerclj/s

(make-scheduler)
(make-scheduler {:keys [initial-ms seed trace? micro-schedule]
                 :or {initial-ms 0 trace? false micro-schedule nil}})

Create a deterministic TestScheduler.

Options: {:initial-ms 0 :seed nil | number ; nil (default) = FIFO ordering ; number = random ordering with that seed :trace? true|false :micro-schedule nil | vector ; explicit selection decisions (overrides seed)}

Ordering behavior:

  • No seed (or nil): FIFO ordering for both timers and microtasks. Predictable, good for unit tests with specific expected order.
  • With seed: Random ordering (seeded RNG) for both timers and microtasks. Deterministic but shuffled, good for fuzz/property testing.

The :micro-schedule option provides explicit control over microtask selection when you need to replay a specific interleaving or test a particular order.

Thread safety: On JVM, all scheduler operations must be performed from a single thread. Cross-thread callbacks will throw an error to catch accidental nondeterminism.

Create a deterministic TestScheduler.

Options:
{:initial-ms     0
 :seed           nil | number        ; nil (default) = FIFO ordering
                                     ; number = random ordering with that seed
 :trace?         true|false
 :micro-schedule nil | vector        ; explicit selection decisions (overrides seed)}

Ordering behavior:
- No seed (or nil): FIFO ordering for both timers and microtasks.
  Predictable, good for unit tests with specific expected order.
- With seed: Random ordering (seeded RNG) for both timers and microtasks.
  Deterministic but shuffled, good for fuzz/property testing.

The :micro-schedule option provides explicit control over microtask selection
when you need to replay a specific interleaving or test a particular order.

Thread safety: On JVM, all scheduler operations must be performed from a single
thread. Cross-thread callbacks will throw an error to catch accidental nondeterminism.
raw docstring

next-eventclj/s

(next-event sched)

Returns info about what would execute next, or nil if idle.

Useful for stepwise debugging and agent introspection.

Returns:

  • {:type :microtask :id ... :kind ... :label ... :lane ...} when a microtask is ready to run
  • {:type :timer :id ... :kind ... :label ... :at-ms ... :lane ...} when no microtasks but a timer is pending
  • nil when scheduler is idle (no work pending)

Note: This reflects FIFO order. Actual selection may differ if a seed is provided (random ordering) or an explicit micro-schedule is configured.

Returns info about what would execute next, or nil if idle.

Useful for stepwise debugging and agent introspection.

Returns:
- {:type :microtask :id ... :kind ... :label ... :lane ...}
  when a microtask is ready to run
- {:type :timer :id ... :kind ... :label ... :at-ms ... :lane ...}
  when no microtasks but a timer is pending
- nil when scheduler is idle (no work pending)

Note: This reflects FIFO order. Actual selection may differ if a seed is
provided (random ordering) or an explicit micro-schedule is configured.
raw docstring

next-tasksclj/s

(next-tasks sched)

Returns vector of available microtasks that can be selected for execution.

Use this for manual stepping to see which tasks are available and their IDs. Each task map contains :id :kind :label :lane keys.

Usage for manual stepping: (mt/start! sched task) (let [tasks (mt/next-tasks sched)] (println "Available:" (mapv :id tasks)) (mt/step! sched (-> tasks first :id))) ; step specific task

Returns empty vector if no microtasks are ready (check timers with next-event).

Returns vector of available microtasks that can be selected for execution.

Use this for manual stepping to see which tasks are available and their IDs.
Each task map contains :id :kind :label :lane keys.

Usage for manual stepping:
  (mt/start! sched task)
  (let [tasks (mt/next-tasks sched)]
    (println "Available:" (mapv :id tasks))
    (mt/step! sched (-> tasks first :id)))  ; step specific task

Returns empty vector if no microtasks are ready (check timers with next-event).
raw docstring

now-msclj/s

(now-ms sched)

Current virtual time in milliseconds.

Current virtual time in milliseconds.
raw docstring

off-scheduler-callbackclj/s


pendingclj/s

(pending sched)

Stable, printable data describing queued microtasks and timers.

Stable, printable data describing queued microtasks and timers.
raw docstring

replayclj/s

(replay task-fn failure)
(replay task-fn failure opts)

Replay a failure from check-interleaving.

IMPORTANT: Must be called inside a with-determinism body.

Takes a failure bundle (from check-interleaving) and a task factory, re-runs the task with the same schedule that caused the failure.

Usage: (let [result (mt/check-interleaving make-task {:seed 42 :property valid?})] (when-not (:ok? result) (mt/replay make-task result)))

Options (merged with failure bundle):

  • :trace? - whether to record trace (default true)
  • :max-steps - max scheduler steps (default 100000)
  • :max-time-ms - max virtual time (default 60000)

Returns the task result (same as replay-schedule).

Replay a failure from check-interleaving.

IMPORTANT: Must be called inside a with-determinism body.

Takes a failure bundle (from check-interleaving) and a task factory,
re-runs the task with the same schedule that caused the failure.

Usage:
  (let [result (mt/check-interleaving make-task {:seed 42 :property valid?})]
    (when-not (:ok? result)
      (mt/replay make-task result)))

Options (merged with failure bundle):
- :trace?      - whether to record trace (default true)
- :max-steps   - max scheduler steps (default 100000)
- :max-time-ms - max virtual time (default 60000)

Returns the task result (same as replay-schedule).
raw docstring

replay-scheduleclj/s

(replay-schedule task schedule)
(replay-schedule task
                 schedule
                 {:keys [trace? max-steps max-time-ms]
                  :or {trace? true max-steps 100000 max-time-ms 60000}})

Run a task with the exact schedule from a previous trace. Returns the task result.

IMPORTANT: Must be called inside a with-determinism body.

Usage: (def original-trace (mt/trace sched)) (with-determinism (mt/replay-schedule (make-task) (mt/trace->schedule original-trace)))

Run a task with the exact schedule from a previous trace.
Returns the task result.

IMPORTANT: Must be called inside a with-determinism body.

Usage:
  (def original-trace (mt/trace sched))
  (with-determinism
    (mt/replay-schedule (make-task) (mt/trace->schedule original-trace)))
raw docstring

resultclj/s

(result job)

Returns job value, throws job failure, or ::pending.

Returns job value, throws job failure, or ::pending.
raw docstring

runclj/s

(run sched task)
(run sched task opts)

Run task deterministically to completion (or throw).

Automatically binds scheduler to sched for the duration of execution, so m/via with m/cpu or m/blk works correctly.

JVM: returns value or throws. CLJS: returns a js/Promise that resolves/rejects.

Run task deterministically to completion (or throw).

Automatically binds *scheduler* to sched for the duration of execution,
so m/via with m/cpu or m/blk works correctly.

JVM: returns value or throws.
CLJS: returns a js/Promise that resolves/rejects.
raw docstring

schedule-exhaustedclj/s


scheduled-flowclj/s

(scheduled-flow sched flow)
(scheduled-flow sched flow {:keys [label]})

Wrap a flow to marshal readiness/termination signals through the scheduler.

(mt/scheduled-flow sched flow {:label ...})

Wrap a flow to marshal readiness/termination signals through the scheduler.

(mt/scheduled-flow sched flow {:label ...})
raw docstring

sleepclj/s

(sleep ms)
(sleep ms x)

Virtual sleep task.

(mt/sleep ms) (mt/sleep ms x)

Semantics:

  • completes after delay with x (or nil)
  • cancelling fails immediately with missionary.Cancelled
Virtual sleep task.

(mt/sleep ms)
(mt/sleep ms x)

Semantics:
- completes after delay with x (or nil)
- cancelling fails immediately with missionary.Cancelled
raw docstring

start!clj/s

(start! sched task)
(start! sched task {:keys [label] :as _opts})

Start a Missionary task under the scheduler and return a Job handle.

Automatically binds scheduler to sched for the task invocation.

(def job (mt/start! sched task {:label "optional"}))

Start a Missionary task under the scheduler and return a Job handle.

Automatically binds *scheduler* to sched for the task invocation.

(def job (mt/start! sched task {:label "optional"}))
raw docstring

step!clj/s

(step! sched)
(step! sched task-id)

Run exactly 1 microtask. Returns ::idle if no microtasks.

(step! sched) - select next task per schedule/FIFO/random (step! sched task-id) - run specific task by ID (for manual stepping)

Binds scheduler to sched for the duration of execution.

Run exactly 1 microtask. Returns ::idle if no microtasks.

(step! sched)        - select next task per schedule/FIFO/random
(step! sched task-id) - run specific task by ID (for manual stepping)

Binds *scheduler* to sched for the duration of execution.
raw docstring

task-id-not-foundclj/s


tick!clj/s

(tick! sched)

Drain all microtasks at current virtual time. Returns number of microtasks executed.

Binds scheduler to sched for the duration of execution.

Drain all microtasks at current virtual time. Returns number of microtasks executed.

Binds *scheduler* to sched for the duration of execution.
raw docstring

timeoutclj/s

(timeout task ms)
(timeout task ms x)

Virtual timeout wrapper task.

(mt/timeout task ms) (mt/timeout task ms x)

Semantics:

  • if input completes before ms, propagate success/failure
  • else, cancel input task and succeed with x (default nil)
  • cancelling the timeout task fails with missionary.Cancelled (and cancels input).
Virtual timeout wrapper task.

(mt/timeout task ms)
(mt/timeout task ms x)

Semantics:
- if input completes before ms, propagate success/failure
- else, cancel input task and succeed with x (default nil)
- cancelling the timeout task fails with missionary.Cancelled (and cancels input).
raw docstring

traceclj/s

(trace sched)

Vector of trace events if enabled, else nil.

Vector of trace events if enabled, else nil.
raw docstring

trace->scheduleclj/s

(trace->schedule trace)

Extract the sequence of task IDs from a trace for replay. Returns a vector of task IDs [id1 id2 id3 ...] that can be used to replay the exact same execution order.

Usage: (def schedule (mt/trace->schedule (mt/trace sched))) ;; => [2 4 3] ; bare task IDs ;; User can inspect/modify: [2 3 4] ; different order (mt/replay-schedule (make-task) schedule)

Extract the sequence of task IDs from a trace for replay.
Returns a vector of task IDs [id1 id2 id3 ...] that can be used to replay
the exact same execution order.

Usage:
  (def schedule (mt/trace->schedule (mt/trace sched)))
  ;; => [2 4 3]  ; bare task IDs
  ;; User can inspect/modify: [2 3 4]  ; different order
  (mt/replay-schedule (make-task) schedule)
raw docstring

unknown-decisionclj/s


with-determinismcljmacro

(with-determinism & body)

Scope deterministic behavior to a test body by rebinding/redefining Missionary vars.

IMPORTANT: This macro is the entry point to deterministic behavior. All flows and tasks under test MUST be created inside the macro body (or by factory functions called from within the body). Tasks/flows created BEFORE or OUTSIDE the macro will capture the real (non-virtual) primitives and will NOT be deterministic.

Usage: (with-determinism (let [sched (mt/make-scheduler)] (mt/run sched (m/sp (m/? (m/sleep 100)) :done))))

Also correct (factory function called inside): (defn make-task [] (m/sp (m/? (m/sleep 100)) :done)) (with-determinism (let [sched (mt/make-scheduler)] (mt/run sched (make-task))))

WRONG (task created outside - will use real time!): (def my-task (m/sp (m/? (m/sleep 100)) :done)) ; WRONG: created outside (with-determinism (let [sched (mt/make-scheduler)] (mt/run sched my-task))) ; m/sleep was NOT rebound when task was created

Effects:

  • Sets is-deterministic to true
  • missionary.core/sleep -> mt/sleep
  • missionary.core/timeout -> mt/timeout
  • missionary.core/cpu -> deterministic executor (JVM only)
  • missionary.core/blk -> deterministic executor (JVM only)

NOTE: m/via with m/cpu or m/blk works correctly because these executors are rebound to run work as scheduler microtasks. Do NOT use real executors (e.g., Executors/newFixedThreadPool) inside with-determinism.

NOTE: mt/run and mt/start! automatically bind scheduler to sched, so you don't need any explicit binding - just pass the scheduler as an argument.

INTERRUPT BEHAVIOR: When a via task is cancelled before its microtask executes, the via body will run with Thread.interrupted() returning true. Blocking calls in the via body will throw InterruptedException. The interrupt flag is cleared after the via body completes, so the scheduler remains usable.

CONCURRENCY: Uses reference counting to make var rebinding safe for parallel test runs. Multiple tests can run concurrently - first acquires the rebindings, last restores originals.

Scope deterministic behavior to a test body by rebinding/redefining Missionary vars.

IMPORTANT: This macro is the entry point to deterministic behavior. All flows
and tasks under test MUST be created inside the macro body (or by factory
functions called from within the body). Tasks/flows created BEFORE or OUTSIDE
the macro will capture the real (non-virtual) primitives and will NOT be
deterministic.

Usage:
  (with-determinism
    (let [sched (mt/make-scheduler)]
      (mt/run sched
        (m/sp (m/? (m/sleep 100)) :done))))

Also correct (factory function called inside):
  (defn make-task [] (m/sp (m/? (m/sleep 100)) :done))
  (with-determinism
    (let [sched (mt/make-scheduler)]
      (mt/run sched (make-task))))

WRONG (task created outside - will use real time!):
  (def my-task (m/sp (m/? (m/sleep 100)) :done))  ; WRONG: created outside
  (with-determinism
    (let [sched (mt/make-scheduler)]
      (mt/run sched my-task)))  ; m/sleep was NOT rebound when task was created

Effects:
- Sets *is-deterministic* to true
- missionary.core/sleep    -> mt/sleep
- missionary.core/timeout  -> mt/timeout
- missionary.core/cpu      -> deterministic executor (JVM only)
- missionary.core/blk      -> deterministic executor (JVM only)

NOTE: m/via with m/cpu or m/blk works correctly because these executors
are rebound to run work as scheduler microtasks. Do NOT use real executors
(e.g., Executors/newFixedThreadPool) inside with-determinism.

NOTE: mt/run and mt/start! automatically bind *scheduler* to sched, so you
don't need any explicit binding - just pass the scheduler as an argument.

INTERRUPT BEHAVIOR: When a via task is cancelled before its microtask executes,
the via body will run with Thread.interrupted() returning true. Blocking calls
in the via body will throw InterruptedException. The interrupt flag is cleared
after the via body completes, so the scheduler remains usable.

CONCURRENCY: Uses reference counting to make var rebinding safe for parallel test runs.
Multiple tests can run concurrently - first acquires the rebindings, last restores originals.
raw docstring

yieldclj/s

(yield)
(yield x)

Yield point task for testing interleavings.

(mt/yield) (mt/yield x)

In production (outside with-determinism): completes immediately with x (or nil). In test mode (inside with-determinism): creates a scheduling point that allows other concurrent tasks to interleave, then completes with x.

This is useful for:

  • Testing concurrent code under different task orderings
  • Creating explicit interleaving points without time delays
  • Simulating cooperative multitasking yield points

Example: ;; In production, this just returns :done immediately (m/? (mt/yield :done))

;; In tests with check-interleaving, different orderings are explored (mt/check-interleaving (fn [] (let [result (atom [])] (m/sp (m/? (m/join vector (m/sp (swap! result conj :a) (m/? (mt/yield)) (swap! result conj :a2)) (m/sp (swap! result conj :b) (m/? (mt/yield)) (swap! result conj :b2)))) @result))) {:property (fn [r] (= 4 (count r)))})

Yield point task for testing interleavings.

(mt/yield)
(mt/yield x)

In production (outside with-determinism): completes immediately with x (or nil).
In test mode (inside with-determinism): creates a scheduling point that allows
other concurrent tasks to interleave, then completes with x.

This is useful for:
- Testing concurrent code under different task orderings
- Creating explicit interleaving points without time delays
- Simulating cooperative multitasking yield points

Example:
  ;; In production, this just returns :done immediately
  (m/? (mt/yield :done))

  ;; In tests with check-interleaving, different orderings are explored
  (mt/check-interleaving
    (fn []
      (let [result (atom [])]
        (m/sp
          (m/? (m/join vector
                 (m/sp (swap! result conj :a) (m/? (mt/yield)) (swap! result conj :a2))
                 (m/sp (swap! result conj :b) (m/? (mt/yield)) (swap! result conj :b2))))
          @result)))
    {:property (fn [r] (= 4 (count r)))})
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close