pub struct AssertResult {
pub outcomes: Vec<Outcome>,
pub passes: Vec<PassDetail>,
pub stats: ScenarioStats,
pub measurements: BTreeMap<String, NoteValue>,
pub info_notes: Vec<InfoNote>,
}Expand description
Verdict for a single test scenario.
§Reading the verdict
Inspect the terminal verdict via Self::outcome (returns the
folded Outcome enum) or the convenience accessors
Self::is_pass / Self::is_fail / Self::is_inconclusive /
Self::is_skip. Iterate the per-variant payloads via
Self::failure_details (all Outcome::Fail payloads),
Self::inconclusive_details (all Outcome::Inconclusive
payloads), and Self::skip_details (all Outcome::Skip
payloads). All four bool accessors mirror
Outcome::is_pass / Outcome::is_fail /
Outcome::is_inconclusive / Outcome::is_skip.
§Recording outcomes
Producers use the atomic mutators Self::record_fail /
Self::record_skip / Self::record_inconclusive /
Self::record_pass (each pushes a single Outcome variant
onto Self::outcomes) and the escape hatch
Self::record_outcome for pre-folded values. Constructors
Self::pass / Self::skip / Self::fail seed the
outcomes vec with the corresponding variant; Self::pass is
zero-allocation (empty vec; the Pass identity element).
Wire-format stability: this struct is postcard-serialized as
part of the in-VM MSG_TYPE_TEST_RESULT payload and as
sidecar artifacts under ~/.cache/ktstr. The wire format is
not stable across crate versions — pre-1.0, fields can be
added, removed, or reshaped at any time, and old sidecars must
be regenerated after upgrades (re-running the affected tests
produces a fresh sidecar). Per the project’s pre-1.0 no-compat
stance, no #[serde(default)] shims are added for old payloads.
Fields§
§outcomes: Vec<Outcome>Recorded terminal verdicts in emission order, one entry per
check that explicitly called Self::record_pass,
Self::record_skip, Self::record_inconclusive, or
Self::record_fail (plus the single entry seeded by
Self::skip / Self::fail constructors).
Empty outcomes is the Pass identity — Self::pass
constructs with outcomes: vec![], and Self::outcome
resolves an empty outcomes to Outcome::Pass via the
ordered Fail > Inconclusive > Pass > Skip precedence (NOT a
fold via Outcome::merge; see Self::outcome), so a
never-touched accumulator naturally resolves to Pass without
any allocation. record_pass() is
for the rare case where a test explicitly records a passing
check (e.g. per-check helpers that document what passed);
pass() is the zero-state “nothing failed so far”
constructor.
The folded terminal verdict is computed by Self::outcome
per the precedence Fail > Inconclusive > Pass > Skip. Use
Self::is_pass / Self::is_fail /
Self::is_inconclusive / Self::is_skip for bool
checks; use Self::failure_details /
Self::inconclusive_details / Self::skip_details to
iterate the per-variant AssertDetail payloads.
passes: Vec<PassDetail>Structured records of every passing claim. Counterpart to
Self::outcomes: where outcomes carries terminal-verdict
records (Fail/Skip/Pass per-check), passes carries the
positive confirmations every comparator’s pass arm emits via
Verdict’s record_pass_unary / record_pass_binary
helpers.
Empty in tests that don’t exercise the structured-pass path
(the no-claim base case), populated whenever a Verdict
records claims. The auto-repro renderer iterates both vecs
to compose the bracketed phase-grouped output that surfaces
passing context alongside failing assertions.
Bounded by MAX_RECORDED_PASSES — past that count,
further pushes drop on the floor and a single sentinel
record named PASSES_TRUNCATION_SENTINEL_NAME appears at
the tail. Use the sentinel-name check (not len()
arithmetic) to detect truncation.
Test-author convention: do NOT pin result.passes shape
or contents in test assertions unless the test exists
specifically to verify the structured-pass surface (e.g.
the auto-repro renderer’s own coverage tests). The field
exists for the renderer’s consumption; pinning it
elsewhere makes the test surface viral — every new
comparator that fires under the test starts churning the
pin. Pin outcome(), failure_details(), and measurements for
scenario verification.
stats: ScenarioStatsAggregated stats from all workers in this scenario.
measurements: BTreeMap<String, NoteValue>Structured measurements attached via Self::note_value /
Verdict::note_value. Distinct from Self::outcomes —
outcomes carry typed verdict variants with AssertDetail
payloads for operator triage, measurements carries typed
(key, NoteValue) pairs for programmatic consumption (sidecar
parsers, perf-delta, regression dashboards).
info_notes: Vec<InfoNote>Informational annotations attached via Self::note /
Verdict::note. Structurally separated from Self::outcomes
so the failure stream stays purely failure-shaped: sidecar
consumers iterating details count real failures without
the “forgot to filter notes” silent-miscount class of bug
that the prior DetailKind::Note variant on AssertDetail
invited. The auto-repro renderer surfaces these alongside the
failure summary so the operator still sees them on a failing
run.
Implementations§
Source§impl AssertResult
impl AssertResult
Sourcepub fn pass() -> Self
pub fn pass() -> Self
Empty passing result with no outcomes and default stats. Use
when a scenario completed successfully with nothing interesting
to report. Zero-allocation: outcomes is an empty Vec and
Self::outcome folds it to Outcome::Pass via the
merge identity.
Sourcepub fn skip(reason: impl Into<String>) -> Self
pub fn skip(reason: impl Into<String>) -> Self
Pass result with a skip reason. Used when a scenario cannot run
under the current topology or flag combination but is not a failure.
Seeds Self::outcomes with a single Outcome::Skip carrying
the reason.
Skip is not Pass: a skipped result reports is_pass() == false
(the outcomes vec contains a non-Pass entry). Callers that want
“not a failure” gate semantics must test
r.is_pass() || r.is_skip() rather than bare r.is_pass() —
otherwise skipped runs count as failures.
Sourcepub fn fail(detail: AssertDetail) -> Self
pub fn fail(detail: AssertDetail) -> Self
Failing result carrying a single AssertDetail. Mirrors
Self::pass / Self::skip for the failure axis so callers
don’t hand-roll the struct-literal shape at every diagnostic-only
failure site. Seeds Self::outcomes with a single
Outcome::Fail carrying the detail.
Sourcepub fn fail_msg(msg: impl Into<String>) -> Self
pub fn fail_msg(msg: impl Into<String>) -> Self
Failing result carrying a single diagnostic message with
DetailKind::Other. Shortcut for the common nesting
AssertResult::fail(AssertDetail::new(DetailKind::Other, msg))
at call sites where the failure is a diagnostic message and
the kind is always Other. Named fail_msg rather than
fail_other so the call site reads “failing result with a
message” without leaking the DetailKind variant name into
the API surface; external callers that do want a specific
kind still reach for AssertResult::fail +
AssertDetail::new(kind, msg).
Sourcepub fn inconclusive(detail: AssertDetail) -> Self
pub fn inconclusive(detail: AssertDetail) -> Self
Inconclusive result carrying a single AssertDetail.
Mirrors Self::pass / Self::skip / Self::fail for
the inconclusive axis so callers don’t hand-roll the struct-
literal shape at sites that need to construct a fresh
“couldn’t evaluate” envelope (the symmetric peer of
Self::fail for INSTRUMENT-derived zero-denominator
gates). Seeds Self::outcomes with a single
Outcome::Inconclusive carrying the detail. For mutating
an existing accumulator in place, use
Self::record_inconclusive.
Sourcepub fn inconclusive_msg(msg: impl Into<String>) -> Self
pub fn inconclusive_msg(msg: impl Into<String>) -> Self
Inconclusive result carrying a single message-only diagnostic.
Shorthand for AssertResult::inconclusive(AssertDetail::new( DetailKind::Other, msg)) — mirrors Self::fail_msg for the
inconclusive axis at call sites where the operator hint is a
flat string and the structured DetailKind would always be
Other. Callers that need a specific kind still reach for
AssertResult::inconclusive + AssertDetail::new(kind, msg).
Sourcepub fn record_fail(&mut self, detail: AssertDetail) -> &mut Self
pub fn record_fail(&mut self, detail: AssertDetail) -> &mut Self
Atomically record a Fail outcome carrying detail. Replaces
the legacy two-step pattern r.passed = false; r.details.push(d);
— collapses the producer-defect window where the discriminant
flipped without a corresponding diagnostic. Returns &mut Self
for chaining.
See Self::record_inconclusive for ratio gates whose
denominator legitimately reached zero — neither Pass nor
Fail is truthful there, and Fail-coding a “couldn’t evaluate”
run loses signal in CI triage.
Sourcepub fn record_skip(&mut self, reason: impl Into<String>) -> &mut Self
pub fn record_skip(&mut self, reason: impl Into<String>) -> &mut Self
Atomically record a Skip outcome carrying reason. Replaces
the legacy two-step pattern r.skipped = true; r.details.push(AssertDetail::new(DetailKind::Skip, reason));.
Returns &mut Self for chaining.
Boundary with Self::record_inconclusive: Skip = scenario
precondition unmet (the check doesn’t apply — e.g. host lacks
the topology the test needs); Inconclusive = precondition met
and the check applied, but the signal was absent and the gate
couldn’t conclude (e.g. a ratio with a zero denominator).
Mis-coding an Inconclusive case as Skip drops it from the
“ran but couldn’t evaluate” bucket CI gates need for triage.
Sourcepub fn record_inconclusive(&mut self, detail: AssertDetail) -> &mut Self
pub fn record_inconclusive(&mut self, detail: AssertDetail) -> &mut Self
Atomically record an Inconclusive outcome carrying detail.
Signature mirrors Self::record_fail (takes the full
AssertDetail so the producer’s DetailKind flows into
the inconclusive record for filterable diagnostics — a
zero-iteration max_migration_ratio site emits
DetailKind::Migration, not a flat string). Use for ratio
gates whose INSTRUMENT-derived denominator (iteration count,
sample count, wall-clock interval) reached zero: the gate
has no signal to evaluate, neither Pass nor Fail is a
truthful verdict. Returns &mut Self for chaining.
Boundary with Self::record_skip: Inconclusive = the gate
applied (preconditions met) but the signal was absent;
Skip = the gate’s precondition was unmet (e.g. host lacks
the required topology) so the check did NOT apply. Boundary
with Self::record_fail: Inconclusive = denominator is
INSTRUMENT-derived (a measurement count that happened to be
zero); Fail = denominator is POLICY-derived (a configured
expectation that must hold — see the Outcome doc’s
MemPolicy::Bind carve-out for the canonical example).
Sourcepub fn record_pass(&mut self) -> &mut Self
pub fn record_pass(&mut self) -> &mut Self
Explicitly record a Pass marker. Rare — the zero-state
AssertResult::pass() already folds to Outcome::Pass via
the merge identity over an empty vec. Use when a test helper
wants the outcome stream to carry an explicit pass record for
per-check accounting (e.g. “this specific check ran and
passed” vs “no check ran”). Returns &mut Self for chaining.
Sourcepub fn record_outcome(&mut self, outcome: Outcome) -> &mut Self
pub fn record_outcome(&mut self, outcome: Outcome) -> &mut Self
Escape hatch: push a pre-folded Outcome onto the stream.
Used by helpers that compute a verdict externally (e.g.
“this branch returned Outcome::Fail(d)”) and want to fold
it into the running Self::outcomes without re-deriving
the variant. Returns &mut Self for chaining.
Sourcepub fn is_pass(&self) -> bool
pub fn is_pass(&self) -> bool
True iff the scenario completed without failure or
inconclusive verdict and actually ran (i.e. wasn’t all-Skip).
An empty outcomes stream (the Self::pass zero-state,
which is the merge identity element) satisfies this; any
stream containing at least one real Pass marker alongside no
Fail / Inconclusive also satisfies it; an all-Skip stream
returns false (a skipped scenario didn’t pass, it didn’t
run); any Inconclusive returns false (a zero-denominator
ratio gate didn’t pass, it couldn’t evaluate).
Mechanically: !self.is_fail() && !self.is_inconclusive() && !self.is_skip(). The three conjuncts capture “no failure
recorded”, “no inconclusive verdict recorded”, AND “not
vacuously satisfied by all-skip”.
Part of the is_pass / is_fail / is_inconclusive /
is_skip vocabulary uniform across the verdict surfaces:
AssertResult::is_pass /
crate::test_support::SidecarResult::is_pass /
Outcome::is_pass / MonitorVerdict::is_pass (in the
monitor module, which is pub(crate)) / Verdict::is_pass
(re-exported at crate::assert::Verdict) /
GauntletRow::is_pass (in the stats module, which is
pub(crate)).
Sourcepub fn is_fail(&self) -> bool
pub fn is_fail(&self) -> bool
True iff any recorded outcome is Outcome::Fail. Any fail
in the stream dominates per Fail > Inconclusive > Pass > Skip
precedence.
Sourcepub fn is_skip(&self) -> bool
pub fn is_skip(&self) -> bool
True iff outcomes is non-empty AND every entry is
Outcome::Skip. Empty outcomes is the Pass identity,
NOT a vacuous Skip — is_skip() returns false on empty.
Sourcepub fn is_inconclusive(&self) -> bool
pub fn is_inconclusive(&self) -> bool
True iff any recorded outcome is Outcome::Inconclusive
AND no Outcome::Fail dominates it. Mirrors the precedence
Fail > Inconclusive > Pass > Skip: a Fail-plus-Inconclusive
stream is is_fail() == true and is_inconclusive() == false
(the Fail wins; Inconclusive is dominated). Used by CI gates
that want to surface “couldn’t evaluate” verdicts distinctly
from passes and failures.
Sourcepub fn failure_details(&self) -> impl Iterator<Item = &AssertDetail>
pub fn failure_details(&self) -> impl Iterator<Item = &AssertDetail>
Iterate every Outcome::Fail’s payload. Use to extract
failure diagnostics for rendering or stats roll-up. Does NOT
include Outcome::Inconclusive payloads —
Self::inconclusive_details is the sibling iterator for
those, and Self::into_anyhow_or_log bails only on Fail
so folding Inconclusive into this iterator would break the
“couldn’t evaluate doesn’t fail the run” semantic.
Sourcepub fn skip_details(&self) -> impl Iterator<Item = &AssertDetail>
pub fn skip_details(&self) -> impl Iterator<Item = &AssertDetail>
Iterate every Outcome::Skip’s payload. Use to extract
skip reasons when triaging “scenario didn’t run” outcomes.
The _details suffix mirrors Self::failure_details /
Self::inconclusive_details — all three yield
&AssertDetail payloads.
Sourcepub fn inconclusive_details(&self) -> impl Iterator<Item = &AssertDetail>
pub fn inconclusive_details(&self) -> impl Iterator<Item = &AssertDetail>
Iterate every Outcome::Inconclusive’s payload. Use to
extract diagnostic context for zero-denominator ratio gates
or other “couldn’t evaluate” verdicts when triaging.
Symmetric with Self::failure_details /
Self::skip_details; not folded into either so the
failure / skip / inconclusive surfaces remain separately
addressable. The _details suffix mirrors
Self::failure_details — both yield &AssertDetail
payloads that drive triage of material verdicts.
Sourcepub fn into_anyhow_or_log(self) -> Result<()>
pub fn into_anyhow_or_log(self) -> Result<()>
Terminal post_vm-callback helper: route every
Self::info_notes entry through tracing::info! (so
--nocapture + RUST_LOG=ktstr=info users see them, but
default-noise-level runs stay quiet) and bail on any
accumulated failure OR inconclusive verdict. Returns Ok(())
only on the pass / pure-skip path — idiomatic post_vm usage
chains ? to propagate the verdict or continue.
§Failure behavior
Per the precedence Fail > Inconclusive > Pass > Skip, Fail
dominates Inconclusive: when any Outcome::Fail is recorded
the helper bails with the failure narrative, regardless of
any sibling Inconclusive outcomes. Every entry from
Self::failure_details is concatenated into the returned
anyhow::Error message — all failures surface, the helper
does NOT drop N-1 details when multiple claims failed.
§Inconclusive behavior
When no failure is present but at least one Inconclusive is
recorded (a zero-denominator ratio gate that couldn’t
evaluate), the helper bails with a distinct preamble
"N inconclusive verdict(s):" carrying every
Self::inconclusive_details payload. This prevents the
silent-pass class of bug where a CI gate keying off
into_anyhow_or_log().is_ok() would treat an Inconclusive
run as green (the is_pass()-keyed invariant fails on
Inconclusive, so the bail surface must match). The
"inconclusive verdict(s)" preamble distinguishes the bail
narrative from the failure preamble "N assertion failures:"
so an operator triaging the log can immediately tell whether
the run failed claims or merely lacked signal to evaluate them.
§Note ordering
Info notes are logged BEFORE the verdict check fires, so on a failed or inconclusive run the operator sees the diagnostic observations that led to the verdict ALONGSIDE the bail message in their log feed (rather than the bail terminating before the notes surface).
§tracing vs println!
Notes are emitted via tracing::info! with target
"ktstr::assert" — the parent namespace of the comparator
pass-arm’s more specific "ktstr::assert::claim" target at
crate::assert::claim. Operators set
RUST_LOG=ktstr::assert=info (or broader) to surface them;
println! would bypass the tracing subscriber and bake in
stdout-only visibility.
§Composability
crate::assert::Verdict::into_anyhow_or_log is a thin
wrapper for callers that hold a Verdict directly.
Sourcepub fn note(&mut self, msg: impl Into<String>) -> &mut Self
pub fn note(&mut self, msg: impl Into<String>) -> &mut Self
Append an informational annotation to Self::info_notes.
Does NOT alter the terminal verdict (Self::outcome is unaffected) — a note
is context, not a verdict. Use to surface observed values
alongside a passing or failing result so the sidecar carries
the diagnostic context an operator needs without forcing every
test to hand-format a format! and push onto details
directly. Notes live on the structurally-separate
Self::info_notes field — sidecar consumers iterating
details see only failures, eliminating the prior
“forgot to filter kind == Note” silent-miscount class of bug.
Sourcepub fn with_note(self, msg: impl Into<String>) -> Self
pub fn with_note(self, msg: impl Into<String>) -> Self
Builder-style sibling of Self::note returning the
owned result so a scenario can chain
AssertResult::pass().with_note("max_wchar=12345") at
the return site. Equivalent to calling
Self::note on a mutable binding.
Sourcepub fn outcome(&self) -> Outcome
pub fn outcome(&self) -> Outcome
Terminal verdict as a single Outcome value, aligned with
Self::is_pass / Self::is_fail /
Self::is_inconclusive / Self::is_skip:
- any
Outcome::Failin the stream →Outcome::Failcarrying the first Fail’s payload (the LEFT operand wins perOutcome::merge’s payload-tie semantics). - else any
Outcome::Inconclusivein the stream →Outcome::Inconclusivecarrying the first Inconclusive’s payload (the gate ran but couldn’t evaluate; perFail > Inconclusive > Pass > Skipthis sits below Fail but above Pass and Skip). - else non-empty all-
Outcome::Skip→Outcome::Skipcarrying the first Skip’s payload (a scenario whose only recorded gates were skips didn’t run — the terminal verdict is Skip, not Pass). - else (empty stream OR at least one explicit
Outcome::Passmarker alongside no Fail / Inconclusive) →Outcome::Pass(the zero-allocation pass identity also lands here).
Diverges from the naive Outcome::merge fold over the
identity element Outcome::Pass: that fold would treat
[Skip(d)] as Pass.merge(Skip(d)) = Pass per the
Fail > Inconclusive > Pass > Skip precedence, contradicting
the all-Skip branch of Self::is_skip. This accessor
encodes the “empty Pass identity” / “real Pass beats Skip” /
“all-Skip is Skip terminal” distinctions the boolean
accessors enforce.
Use Self::outcome_ref when the caller only needs to
inspect the verdict shape/payload without taking ownership —
avoids the per-call AssertDetail::clone this accessor
performs on the Skip / Fail arms.
Sourcepub fn outcome_ref(&self) -> OutcomeRef<'_>
pub fn outcome_ref(&self) -> OutcomeRef<'_>
Borrow the terminal verdict as an OutcomeRef. Same fold
semantics as Self::outcome —
Fail > Inconclusive > Pass > Skip precedence, empty-vec /
non-empty-all-Skip / mixed-Pass-plus-Skip branches all match
— but the Skip(_) / Inconclusive(_) / Fail(_) arms
borrow the source AssertDetail from self.outcomes
instead of cloning. Use when the caller holds the source
AssertResult and wants the verdict payload without the
per-call clone (formatter / sidecar emit / debug-render paths).
Drift guard: assert_result_outcome_ref_matches_owned_outcome_shape
in tests_assert.rs pins the lockstep with Self::outcome;
any divergence (e.g. a future refactor that adds a new
terminal arm here but forgets the owned accessor, or vice
versa) trips the test.
Sourcepub fn merge(&mut self, other: AssertResult)
pub fn merge(&mut self, other: AssertResult)
Fold other into self. The four parallel vecs/maps —
Self::outcomes, Self::passes, Self::info_notes,
Self::measurements — all extend with other’s contents
(the three vecs concatenate; measurements is a BTreeMap
merged with plain last-write-wins on key collision, i.e.
other’s value overwrites self’s for shared keys).
Aggregate stats adopt the worst-case value per dimension
so the merged result represents the union of all checks
applied. The polarity-aware per-key min/max selection for
extensible scheduler metrics is a separate mechanism that
applies inside stats.ext_metrics only — see the loop at
the bottom of this body for the polarity registry path; the
result-level measurements map deliberately does not consult
the registry (it is a producer-attached typed annotation map,
not a roll-up aggregation surface).
Terminal-verdict semantics fall out automatically per the
precedence Fail > Inconclusive > Pass > Skip: appending
other.outcomes keeps every Fail in the stream so
Self::outcome’s fold surfaces them; absent any Fail, any
Inconclusive in either side dominates Pass/Skip so a
zero-denominator gate in one branch survives the fold;
Skip survives only when both inputs were Skip-only because a
Pass or Inconclusive entry in either side beats Skip.
Sourcepub fn note_value(
&mut self,
key: impl Into<String>,
value: impl Into<NoteValue>,
) -> &mut Self
pub fn note_value( &mut self, key: impl Into<String>, value: impl Into<NoteValue>, ) -> &mut Self
Attach a structured (key, value) measurement to the result.
Writes into Self::measurements without altering
the terminal verdict (Self::outcome) —
pure context for stats tooling.
Distinct from Self::note: note carries a free-form
String for operator triage; note_value carries a typed
(key, NoteValue) pair for programmatic consumption (sidecar
parsers, perf-delta regression dashboards). Producers
commonly call BOTH — they occupy independent buffers and
neither overwrites the other.
Key collision policy: a second write with the same key
overwrites the first. The intended call site shape is “one
producer per key” (one site computes max_wchar, one site
computes psi_some_total_usec); accidental key collision
indicates a producer bug. The test
note_value_overwrites_on_duplicate_key pins this last-
write-wins semantics.
let mut r = AssertResult::pass();
r.note_value("max_wchar", 12345i64);
r.note_value("psi_available", true);
assert_eq!(r.measurements["max_wchar"], NoteValue::Int(12345));
assert_eq!(r.measurements["psi_available"], NoteValue::Bool(true));Sourcepub fn with_note_value(
self,
key: impl Into<String>,
value: impl Into<NoteValue>,
) -> Self
pub fn with_note_value( self, key: impl Into<String>, value: impl Into<NoteValue>, ) -> Self
Builder-style sibling of Self::note_value returning the
owned result so a scenario can chain
AssertResult::pass().with_note_value("max_wchar", 12345u64)
at the return site. Equivalent to calling Self::note_value
on a mutable binding. Mirrors Self::with_note.
Sourcepub fn any_of(branches: impl IntoIterator<Item = AssertResult>) -> AssertResult
pub fn any_of(branches: impl IntoIterator<Item = AssertResult>) -> AssertResult
Fold a sequence of AssertResults with OR semantics: the
returned result passes iff at least one branch passes. Use
when a test author expresses “either of these two checks
suffices” — a kernel-version-fork case where one path is
expected on 6.16 and another on 7.1, or a topology probe
where any of several detection methods landing is enough.
Outcomes:
- At least one branch passes: returned result is passing.
info_notescarries the union of every passing branch’s info_notes, each prefix-stamped withany_of[<branch-idx>]:so an operator can attribute every note to the emitting branch. The synthesized “any_of: branch N satisfied the disjunction” arbiter annotation is appended last, bare (it’s not from any branch — it IS the disposition). Failed-branch details and info_notes are dropped (they would only confuse the operator with messages from the not-taken paths).statsadopts the first passing branch’sstats.measurementsunion all passing branches’ measurements (last write wins on key collision, matchingmerge).outcomesfollows the first passing branch (typically empty per the Pass identity). - No branch passes; at least one fails: returned result
is failing. Every branch’s recorded outcomes are re-emitted
with each payload’s message prefixed by
"any_of[<branch-idx>]: "so an operator can identify which branch produced which outcome.statsandmeasurementsadopt the FIRST branch’s values (an arbitrary choice but deterministic). A synthesized summary record is appended last carrying the per-disposition counts(F failed, I inconclusive, S skipped of N branches). - No branch passes or fails; at least one is Inconclusive:
returned result is Inconclusive (the disjunction itself
could not be evaluated — every branch either was inconclusive
or skipped, and per the lattice
Fail > Inconclusive > Pass > SkipInconclusive dominates Skip). Same re-emission + summary shape as the fail case, but the synthesized record isOutcome::Inconclusive. Critical: without this arm, all-zero-denominator branches would silently MISCLASSIFY as Fail, defeating the Inconclusive primitive’s purpose of preserving “couldn’t evaluate” signal. - All branches skipped: returned result is Skip. Same
re-emission + summary shape, with
Outcome::Skipas the synthesized record (every alternative check’s precondition was unmet — the disjunction itself didn’t run). - Empty input: returned result is failing with a single
Fail outcome explaining the empty
any_of. An empty disjunction is logically false; this surfaces a producer bug as a nameable failure rather than a vacuous pass.
Doc: a trivial two-branch test with the second branch passing and the first branch failing — pinning that the verdict chooses the passer.
let r = AssertResult::any_of([
{
let mut a = AssertResult::pass();
a.record_fail(AssertDetail::new(DetailKind::Other, "branch 0 boom"));
a
},
AssertResult::pass(),
]);
assert!(r.is_pass());Sourcepub fn all_of(branches: impl IntoIterator<Item = AssertResult>) -> AssertResult
pub fn all_of(branches: impl IntoIterator<Item = AssertResult>) -> AssertResult
Fold a sequence of AssertResults with AND semantics:
equivalent to branches.into_iter().fold(pass(), |acc, b| { acc.merge(b); acc }). Returns a passing result iff
every branch passes.
Distinct from Self::merge in API shape only: merge
folds one external result into an existing accumulator;
all_of folds an iterator of branches into a fresh result.
Same semantics for outcomes (concatenated; Fail dominates
per Outcome::merge precedence), stats (worst-per-dimension),
measurements (union with last-write-wins). An empty input
yields the passing identity (AssertResult::pass()) — the
AND of an empty set is logically true, mirroring
Iterator::all.
Use when the test reads more naturally as “every check
must hold” than as a merge chain — e.g. when the checks
are dynamically generated from a slice and the call site
would otherwise need an explicit for loop with merge.
let r = AssertResult::all_of([
AssertResult::pass(),
AssertResult::pass(),
]);
assert!(r.is_pass());
let r = AssertResult::all_of([
AssertResult::pass(),
AssertResult::fail(AssertDetail::new(DetailKind::Other, "boom")),
]);
assert!(r.is_fail());Trait Implementations§
Source§impl Clone for AssertResult
impl Clone for AssertResult
Source§fn clone(&self) -> AssertResult
fn clone(&self) -> AssertResult
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for AssertResult
impl Debug for AssertResult
Source§impl<'de> Deserialize<'de> for AssertResult
impl<'de> Deserialize<'de> for AssertResult
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for AssertResult
impl RefUnwindSafe for AssertResult
impl Send for AssertResult
impl Sync for AssertResult
impl Unpin for AssertResult
impl UnwindSafe for AssertResult
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more