pub struct Harness<G: GameData> { /* private fields */ }Expand description
Drives a Game with simulated key presses and time, as the platform
would: in an App, on the HeadlessPlatform.
Implementations§
Source§impl<G: GameData> Harness<G>
impl<G: GameData> Harness<G>
Sourcepub fn on(game: Game<G>, platform: HeadlessPlatform) -> Self
pub fn on(game: Game<G>, platform: HeadlessPlatform) -> Self
Drives game from where it is on platform (one with a system
keyboard, say). Its screens get only the actions they declare
(Game::guard_actions), so a screen reacting to one it didn’t
declare fails its test (undeclared says which
were held back).
Sourcepub fn start(start: Start<G>) -> Result<Self, String>
pub fn start(start: Start<G>) -> Result<Self, String>
Runs the game from its real start, as a device runs it: the card’s
frame at 0 ms and the load’s one FRAME_DT later run now, and the
load’s errors are the Err. Its frames go through App::frame, in
whole milliseconds. As on a device, its screens get the actions they
didn’t declare too (on holds those back), and a game
waits for its first press unless its start says not
(Setup::key_prompt). The scripts work as
on any Harness.
Sourcepub fn start_recording(start: Start<G>) -> Result<Self, String>
pub fn start_recording(start: Start<G>) -> Result<Self, String>
start, recording from the first frame:
session gives the record so far.
A record holds what the platform gives the game, so what a test
changes around the platform isn’t in it, and plays differently or
not at all: act, ctx_mut,
screen_mut. Press the keys instead. Nor does it
hold a with_system_hook hook, so give the
hook in the Start, to both runs (Start::on_system).
Sourcepub fn session(&self) -> Option<&SessionRecord>
pub fn session(&self) -> Option<&SessionRecord>
The record so far, of a start_recording.
Sourcepub fn from_session(
start: Start<G>,
record: &SessionRecord,
stamps: Stamps,
) -> Result<Self, SessionError>
pub fn from_session( start: Start<G>, record: &SessionRecord, stamps: Stamps, ) -> Result<Self, SessionError>
Makes ready to play record from the game’s real start, on the
display, the facts and the store it recorded
(HeadlessPlatform::from_session). It plays nothing yet:
play_to does. A record of another version is
refused, and of another build as stamps says
(session::prepare).
Sourcepub fn play_to(&mut self, frame: u32) -> &Played
pub fn play_to(&mut self, frame: u32) -> &Played
Plays the record’s frames up to frame, counted from 1, or to its
end, and stops at the first frame whose hash differs, leaving the game
on that frame; after that it plays nothing more. On a frame before the
load, app, screens and
screen_log can be read, while what needs the
game panics. A Harness with no record plays nothing.
A script’s frame ends the playing, wherever it has got to: the game
goes on with the script, its clock from the last frame played
(Playback::now) and its music from the headless
Speaker, and Played::limits says so.
Sourcepub fn with_system_hook(self, hook: SystemHook<G>) -> Self
pub fn with_system_hook(self, hook: SystemHook<G>) -> Self
Gives the game hook for the system’s events
(system).
Sourcepub fn at_prompt(self) -> Self
pub fn at_prompt(self) -> Self
Waits for a key or button press, as a platform’s first screen may
(KeyPrompt::Waiting), and redraws.
Sourcepub fn as_target(self, profile: &TargetProfile) -> Self
pub fn as_target(self, profile: &TargetProfile) -> Self
Runs the game as the target profile describes: its facts
(PlatformFacts::from_profile) go into the context
(Ctx::platform), and the screen redraws. Call it before the
screen under test opens: a screen may keep what it read as it
opened (the key bindings screen keeps the keyboard).
Sourcepub fn music_load_delay(&mut self, seconds: f32) -> &mut Self
pub fn music_load_delay(&mut self, seconds: f32) -> &mut Self
Makes music take seconds to load: a track starts sounding, and
Ctx::music_clock starts counting, that long after the game
starts it (at once by default). Negative or NaN counts as 0.
Sourcepub fn voice_length(&mut self, seconds: f32) -> &mut Self
pub fn voice_length(&mut self, seconds: f32) -> &mut Self
Makes every voice clip last seconds: Ctx::voice_playing names
its line from the frame after it was asked for until that long has
passed. Until this is called a clip sounds until it is stopped or
replaced. Negative or NaN counts as 0.
Sourcepub fn without_music(&mut self) -> &mut Self
pub fn without_music(&mut self) -> &mut Self
Makes no music ever sound, as when the music files are missing:
Ctx::music_clock stays None.
Sourcepub fn music_clock(&self) -> Option<&MusicClock>
pub fn music_clock(&self) -> Option<&MusicClock>
Where the music is in its track, as the screens saw it in the last
frame (Ctx::music_clock).
Sourcepub fn change_file(
&mut self,
layer: Layer,
path: &str,
bytes: Vec<u8>,
) -> &mut Self
pub fn change_file( &mut self, layer: Layer, path: &str, bytes: Vec<u8>, ) -> &mut Self
Changes the file at path in layer (one handed in at run time),
as a file changing on disk does: the next frame’s modules hear of it
before the update (HeadlessPlatform::change_file).
Sourcepub fn keys(&mut self, script: &str) -> &mut Self
pub fn keys(&mut self, script: &str) -> &mut Self
Presses and releases each whitespace-separated chord in turn, e.g.
"Down Down f" or "Shift+Space Enter": a frame with the press,
then a frame with the release, each FRAME_DT long.
§Panics
On a chord Chord::parse rejects.
Sourcepub fn type_text(&mut self, text: &str) -> &mut Self
pub fn type_text(&mut self, text: &str) -> &mut Self
Types text one character per frame as the platform reports
typing: the key’s press (Shift for capitals) with the character,
then its release. A character without a key of its own is typed
alone.
Sourcepub fn hold(&mut self, chord: &str, seconds: f32) -> &mut Self
pub fn hold(&mut self, chord: &str, seconds: f32) -> &mut Self
Holds chord for seconds of frames (so held keys repeat), then
releases it in one more frame.
§Panics
On a chord Chord::parse rejects.
Sourcepub fn pad(&mut self, script: &str) -> &mut Self
pub fn pad(&mut self, script: &str) -> &mut Self
Presses and releases each whitespace-separated controller button in
turn, e.g. "DpadDown South", like keys. The names
are button positions, as in a keymap file. Buttons joined by +
("South+East", "East+DpadRight") go down together in one frame,
in the order written, and up together in the next.
§Panics
On a button Button::parse rejects.
Sourcepub fn set_crank(&mut self, def: CrankDef) -> &mut Self
pub fn set_crank(&mut self, def: CrankDef) -> &mut Self
Notches of def for crank (the game’s keymap
file’s crank; 45° until set).
Sourcepub fn crank(&mut self, degrees: f32) -> &mut Self
pub fn crank(&mut self, degrees: f32) -> &mut Self
Winds the crank degrees in one frame of FRAME_DT (forwards is
positive, backwards negative), as the Playdate’s front end reports
it: a press and a release of a crank button for each whole notch,
the rest of a notch kept for the next call (ticket 1035).
Sourcepub fn show_debug(&mut self, channels: &str) -> &mut Self
pub fn show_debug(&mut self, channels: &str) -> &mut Self
Turns debug tools on, and the debug view’s channels of the
comma-separated ids channels ("all": every channel, offered or
not; ADR-0085), and draws the frame again with them: no frame
passes. A script’s Step::Debug.
Sourcepub fn system(&mut self, event: SystemEvent) -> &mut Self
pub fn system(&mut self, event: SystemEvent) -> &mut Self
The system tells the game event (a suspend, a resume, a lost
focus), in a frame of FRAME_DT with no input.
Sourcepub fn hold_pad(&mut self, button: &str, seconds: f32) -> &mut Self
pub fn hold_pad(&mut self, button: &str, seconds: f32) -> &mut Self
Holds the controller button button for seconds of frames (so a
held direction repeats), then releases it in one more frame.
§Panics
On a button Button::parse rejects.
Sourcepub fn wait(&mut self, seconds: f32) -> &mut Self
pub fn wait(&mut self, seconds: f32) -> &mut Self
Lets seconds pass with no input, in frames of at most FRAME_DT.
Sourcepub fn steps(&mut self, count: u32) -> &mut Self
pub fn steps(&mut self, count: u32) -> &mut Self
Runs frames with no input until exactly count fixed simulation
steps were due in them (App::ticks), so a tick stage steps its
rules count times: frames of FRAME_DT, which run at most two
steps, and a half one for the last step, which runs at most one.
§Panics
If that takes more than MAX_FRAMES frames: the game runs no
steps (it is suspended, or hasn’t loaded).
Sourcepub fn frame(&mut self, events: &[RawInputEvent], dt: f32) -> &mut Self
pub fn frame(&mut self, events: &[RawInputEvent], dt: f32) -> &mut Self
Runs one frame of dt seconds with events, as a platform does:
the events go to the headless backend, which the App reads in
its frame (the music sounding, the update, the audio played, the
frame presented); then the audio is recorded. The scripts above
are made of these.
Sourcepub fn frame_count(&self) -> u32
pub fn frame_count(&self) -> u32
How many frames have run since the Harness started.
Sourcepub fn screen_log(&self) -> &[(u32, Vec<&'static str>)]
pub fn screen_log(&self) -> &[(u32, Vec<&'static str>)]
The screens open before the first frame (frame 0), then after each frame that changed them: the frame, and their names, bottom first.
Sourcepub fn quit_frame(&self) -> Option<u32>
pub fn quit_frame(&self) -> Option<u32>
The frame the game asked to quit in, if it has (frames count from 1).
Sourcepub fn audio_log(&self) -> Vec<(u32, &AudioRequest<G::Voice>)>
pub fn audio_log(&self) -> Vec<(u32, &AudioRequest<G::Voice>)>
Every audio request since the last clear_audio,
in order, each with the frame that asked for it.
Sourcepub fn music_log(&self) -> Vec<(u32, &MusicCommand)>
pub fn music_log(&self) -> Vec<(u32, &MusicCommand)>
Every music command since the last
clear_audio, in order, each with the frame
that gave it.
Sourcepub fn audio_requests(&self) -> Vec<AudioRequest<G::Voice>>
pub fn audio_requests(&self) -> Vec<AudioRequest<G::Voice>>
Every audio request of the run so far, in order.
Sourcepub fn sounds(&self) -> Vec<String>
pub fn sounds(&self) -> Vec<String>
The sound cues (not music) played or started so far, in order.
Sourcepub fn sounds_playing(&self) -> &[PlayingSound]
pub fn sounds_playing(&self) -> &[PlayingSound]
The sounds started with a handle that are still playing, as the
headless backend’s Speaker holds them,
in id order.
Sourcepub fn sounds_added(&self) -> Vec<&str>
pub fn sounds_added(&self) -> Vec<&str>
The cues of the sounds the game added at run time
(AudioQueue::add_sound)
and hasn’t removed, as the headless backend’s speaker holds them, in
order.
Sourcepub fn added_plays(&self) -> &[AddedPlay]
pub fn added_plays(&self) -> &[AddedPlay]
Every play and start of a sound the game added, in order, with the samples it played, as the headless backend’s speaker heard them.
Sourcepub fn voices(&self) -> Vec<String>
pub fn voices(&self) -> Vec<String>
The lines whose voice clip was asked for so far, in order, as line ids.
Sourcepub fn last_frame_audio(&self) -> &[AudioRequest<G::Voice>]
pub fn last_frame_audio(&self) -> &[AudioRequest<G::Voice>]
The audio requests of the last frame run (a key press runs two frames, press and release; this is the release).
Sourcepub fn volumes(&self) -> (f32, f32)
pub fn volumes(&self) -> (f32, f32)
The volumes the last frame told the platform to play at, 0–1: the
music’s and the sounds’ (AudioFrame).
Sourcepub fn voice_volume(&self) -> f32
pub fn voice_volume(&self) -> f32
The voice volume the last frame told the platform to play at, 0–1
(AudioFrame).
Sourcepub fn fullscreen(&self) -> bool
pub fn fullscreen(&self) -> bool
Whether the last frame told the platform to fill the screen.
Sourcepub fn music_commands(&self) -> &[MusicCommand]
pub fn music_commands(&self) -> &[MusicCommand]
Every music command of the run so far, in order.
Sourcepub fn clear_audio(&mut self) -> &mut Self
pub fn clear_audio(&mut self) -> &mut Self
Forgets the audio recorded so far, so the next checks see only what comes after.
Sourcepub fn top_screen(&self) -> &'static str
pub fn top_screen(&self) -> &'static str
Name of the top screen; empty once every screen has closed, and while the game hasn’t loaded.
Sourcepub fn screens(&self) -> Vec<&'static str>
pub fn screens(&self) -> Vec<&'static str>
Names of the open screens, bottom first: none while the game hasn’t loaded.
Sourcepub fn screen<T: Any>(&self) -> Option<&T>
pub fn screen<T: Any>(&self) -> Option<&T>
The top-most screen of type T on the stack, if it opts in
(Game::screen).
Sourcepub fn screen_mut<T: Any>(&mut self) -> Option<&mut T>
pub fn screen_mut<T: Any>(&mut self) -> Option<&mut T>
screen, mutable, to drive it with scripted
commands.
Sourcepub fn snapshot(&self) -> String
pub fn snapshot(&self) -> String
The current frame in the snapshot format of
shuyi_ui::frame::snapshot. The second cell of a wide character
(Japanese, Chinese) prints as nothing, so a row of wide text reads
as it is drawn; its colour row still has a key a cell.
Sourcepub fn snapshot_as(&mut self, device: Device) -> String
pub fn snapshot_as(&mut self, device: Device) -> String
The current frame as it would look had the player pressed device
last: the same screen with that device’s key or button names. For
comparing play on a controller with the same play on the keyboard.
Sourcepub fn undeclared(&self) -> &[(&'static str, G::Action)]
pub fn undeclared(&self) -> &[(&'static str, G::Action)]
Every action held back from a screen that doesn’t declare it
(Screen::actions), oldest first, with
the screen’s name: why a screen didn’t react to a key.
Sourcepub fn quit_requested(&self) -> bool
pub fn quit_requested(&self) -> bool
Whether the game has asked to quit.
Sourcepub fn app_mut(&mut self) -> &mut App<G>
pub fn app_mut(&mut self) -> &mut App<G>
The App running the game, mutable, to take what it hands a
backend (App::take_session_save).
Sourcepub fn log(&self) -> &LogRing
pub fn log(&self) -> &LogRing
The game’s log, kept by its App: every line from frame 0 (what
its context logged before the Harness started), each with the frame
it was given in.
Sourcepub fn set_clock(&mut self, clock: Option<PerfClock>) -> &mut Self
pub fn set_clock(&mut self, clock: Option<PerfClock>) -> &mut Self
Measures frames with clock from the next frame, or stops (None):
the headless backend’s clock, and the game’s context’s, since the
game runs. A fake clock (PerfClock::fake) gives the same records
on every run.
Sourcepub fn set_scopes(&mut self, scopes: Option<MemoryScopes>) -> &mut Self
pub fn set_scopes(&mut self, scopes: Option<MemoryScopes>) -> &mut Self
Tags what the game allocates with scopes from the next frame, or
stops (None): the headless backend’s memory tags, and the game’s
context’s, since the game runs (MemoryScopes).
Sourcepub fn set_memory(&mut self, memory: Option<fn() -> HeapCounts>) -> &mut Self
pub fn set_memory(&mut self, memory: Option<fn() -> HeapCounts>) -> &mut Self
Reads the heap with memory (a real counter’s reading) at the end
of each frame measured from now on, or reads none (None): each
record’s heap. A fake memory
that moves by fixed amounts is the platform’s
(HeadlessPlatform::with_fake_memory, with Harness::on).
Sourcepub fn set_heap_snapshots(
&mut self,
snapshot: Option<fn() -> HeapSnapshot>,
) -> &mut Self
pub fn set_heap_snapshots( &mut self, snapshot: Option<fn() -> HeapSnapshot>, ) -> &mut Self
Takes heap snapshots with snapshot (a real counter’s) from now
on, or none (None). A fake memory takes its own
(HeadlessPlatform::with_fake_memory).
Sourcepub fn set_present_hook(&mut self, hook: Option<PresentHook>) -> &mut Self
pub fn set_present_hook(&mut self, hook: Option<PresentHook>) -> &mut Self
Runs hook on each frame presented from now on, in the headless
backend’s present, or runs none (None): the work a device’s
backend does on each frame, such as a one-bit display’s conversion
(to_one_bit), so shuyi bench counts it in the frame
(HeadlessPlatform::set_present_hook).
Sourcepub fn heap_snapshot(&mut self) -> Option<HeapSnapshot>
pub fn heap_snapshot(&mut self) -> Option<HeapSnapshot>
What holds the heap now, by memory tag, stamped with the frames run
(App::heap_snapshot): a fake memory’s, or a counter’s set with
set_heap_snapshots; None without
either, or with debug tools off. A test takes two and compares them
(HeapSnapshot::diff).
Sourcepub fn platform(&self) -> &HeadlessPlatform
pub fn platform(&self) -> &HeadlessPlatform
The headless backend it runs on: the frames presented, the audio played.
Sourcepub fn ctx_mut(&mut self) -> &mut Ctx<G>
pub fn ctx_mut(&mut self) -> &mut Ctx<G>
The shared context, to change it between key presses, e.g.
h.ctx_mut().set_layout_bindings(…) to rebind keys as a key
bindings screen would. The next frame uses the new keys.
Sourcepub fn into_storage(self) -> Box<dyn Storage>
pub fn into_storage(self) -> Box<dyn Storage>
Ends the run, handing back its storage, to start the next launch with it.