Skip to main content

Module conformance

Module conformance 

Source
Expand description

Cross-backend conformance tests for Output, Cursor, and Input (retroglyph#763).

Each of the five backends in this workspace answers the same handful of obligations (Output::clear/Output::resize resetting internal state, out-of-range DrawCell positions, cursor tracking staying in sync with external writes, Input::push_event coalescing consecutive Mouse(Moved) events) independently. This module is what holds those independent answers to a single agreed contract: assert_output_contract, assert_cursor_contract, and assert_input_contract each drive a backend through one facet’s obligations and panic on the first violation, so a backend crate wires one of them into a #[test] and gets every future regression in that facet for free.

§Why not B: Backend

GlRenderer deliberately implements neither Input nor Cursor (a GPU/pixel surface has no text cursor and never receives external input): a single B: Backend bound would make the harness itself impossible to use, since a bound including Input + Cursor could never be satisfied by every backend that wants only assert_output_contract. The three entry points stay separate so a backend opts into exactly the facets it implements.

§The Observable hook, and why it must be a delta

Output/Cursor have no shared way to read back “what would actually appear”: a terminal backend has emitted bytes, a pixel backend has a framebuffer, Headless has a Grid. Observable::snapshot is the one method a backend implements to bridge that gap, and every assertion below only ever compares two calls to it for equality, never interpreting the u64 any other way.

That equality only means what it should if snapshot returns what changed since the previous call, not the backend’s whole history or its whole current state. The assertions compare two independently-built action sequences that a real user could not tell apart from this point forward; if snapshot hashed everything ever produced (a terminal backend’s whole emitted byte log, say), two sequences of different lengths could never compare equal even when both are correct, and the assertions would fail on every backend, always, for a reason that has nothing to do with the obligation under test. A backend whose only observable output is an appended log implements this by hashing the slice appended since the last call (and advancing a remembered offset past it). A framebuffer-shaped backend implements it by hashing the positions that differ from the previous call’s content (and remembering the new content for next time) rather than the whole buffer. Either way, snapshot needs its own “since last call” bookkeeping the production backend has no other reason to carry, which is usually easiest to add via a small test-only wrapper around the real backend rather than on the backend type itself; see the tests module below for a worked example over Headless.

§What this does not cover

Output::needs_full_frame only takes effect through Terminal::present when a backend also returns true from Output::composites_layers (see that method’s docs); a bare Output impl has no diffing of its own to exercise, so that combination is instead pinned by a Terminal-level test rather than by this module.

Traits§

Observable
A backend that can report a digest of what changed since the last call.

Functions§

assert_cursor_contract
Drives B through Cursor’s tracked-cursor obligation.
assert_cursor_style_contract
Drives B through Cursor::set_cursor_style’s obligation: each CursorStyle variant must have its own distinct, observable effect (retroglyph#920).
assert_input_contract
Drives B through Input’s coalescing obligation.
assert_output_contract
Drives B through Output’s obligations: make must return a fresh backend sized to the requested Size, with no cells drawn yet.
fnv1a
Hashes bytes with FNV-1a (64-bit).