test(pf-encode): guard TrackedEncoder's forwarding completeness (WP7.7, cheap half)

A defaulted Encoder method that TrackedEncoder doesn't forward silently
no-ops through the wrapper — the host loop only ever holds the wrapped
box, so the feature dies for every session with nothing in the logs. The
trap has bitten three times (set_wire_chunking's §4.4 chunking probe,
set_pipelined's LN3 escalation, applied_bitrate_bps's ABR truth), and
every Phase 7 consolidation that adds a trait method re-arms it.

Source-text parse of the trait and the forwarding impl (both top-level
rustfmt items: block ends at the first column-0 brace, method names sit
on 'fn '-prefixed lines), then set equality — the reverse direction is
already a compile error, so equality == completeness. Mutation-verified:
removing set_wire_chunking's forward fails naming exactly that method.
16/16 today.

Limit stated in the test doc: name-set equality only — a forward whose
body delegates to the WRONG inner method still passes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-26 00:08:03 +02:00
co-authored by Claude Opus 5
parent 94b818da28
commit d28fb1282b
+60
View File
@@ -1610,4 +1610,64 @@ mod tests {
};
assert_eq!(none.wire_mask(), None);
}
/// WP7.7 guard (the cheap half): every `Encoder` trait method must be explicitly forwarded by
/// `TrackedEncoder`. A defaulted trait method that isn't forwarded silently no-ops through the
/// wrapper — the trap has bitten three times (`set_wire_chunking`'s §4.4 chunking probe,
/// `set_pipelined`'s LN3 escalation, `applied_bitrate_bps`'s ABR truth), and every Phase 7
/// consolidation that adds a trait method re-arms it. Source-text parse, not reflection: both
/// blocks are top-level rustfmt items, so each ends at the first column-0 `}` and every method
/// name sits on a line starting `fn ` (a wrapped signature still carries the name there).
#[test]
fn tracked_encoder_forwards_every_trait_method() {
fn item_block<'a>(src: &'a str, marker: &str) -> &'a str {
let start = src
.find(marker)
.unwrap_or_else(|| panic!("marker {marker:?} not found — update this guard"));
let body = &src[start..];
let end = body
.find("\n}")
.unwrap_or_else(|| panic!("no column-0 close brace after {marker:?}"));
&body[..end]
}
fn fn_names(block: &str) -> std::collections::BTreeSet<&str> {
block
.lines()
.map(str::trim_start)
.filter(|l| !l.starts_with("//"))
.filter_map(|l| l.strip_prefix("fn "))
.map(|rest| {
rest.split(|c: char| !(c.is_ascii_alphanumeric() || c == '_'))
.next()
.expect("split yields at least one item")
})
.collect()
}
// `find` takes the FIRST occurrence: the real impl precedes this test's own copy of the
// marker string in the included source.
let trait_fns = fn_names(item_block(
include_str!("enc/codec.rs"),
"pub trait Encoder: Send {",
));
let impl_fns = fn_names(item_block(
include_str!("lib.rs"),
"impl Encoder for TrackedEncoder {",
));
assert!(
trait_fns.len() >= 12,
"only {} trait methods parsed — the extraction markers have rotted, fix the parse \
before trusting this guard",
trait_fns.len()
);
let missing: Vec<_> = trait_fns.difference(&impl_fns).collect();
assert!(
missing.is_empty(),
"Encoder methods NOT forwarded by TrackedEncoder: {missing:?} — the host loop only \
ever holds the wrapped box, so an unforwarded default silently disables the feature \
for every session. Forward each one in `impl Encoder for TrackedEncoder`."
);
// The reverse direction (impl fn absent from the trait) is a compile error, so equality
// here is pure belt-and-braces against a parse regression.
assert_eq!(trait_fns, impl_fns);
}
}