diff --git a/test/src/emulator.rs b/test/src/emulator.rs index 0bd653d7b4..89d6e2613e 100644 --- a/test/src/emulator.rs +++ b/test/src/emulator.rs @@ -61,6 +61,12 @@ enum Action_ { CountDown, } +enum Initializer<'a, P: Program + 'a> { + Boot, + Preset(&'a program::Preset), + State(P::State), +} + impl Emulator

{ /// Creates a new [`Emulator`] of the [`Program`] with the given [`Mode`] and [`Size`]. /// @@ -73,7 +79,21 @@ impl Emulator

{ mode: Mode, size: Size, ) -> Emulator

{ - Self::with_preset(sender, program, mode, size, None) + Self::initialize(sender, program, mode, size, Initializer::Boot) + } + + /// Creates a new [`Emulator`] analogously to [`new`](Self::new), but it also takes + /// an existing [`Program::State`] as the initial state. + /// + /// When the [`Emulator`] has finished booting, an [`Event::Ready`] will be produced. + pub fn with_state( + sender: mpsc::Sender>, + program: &P, + mode: Mode, + size: Size, + state: P::State, + ) -> Emulator

{ + Self::initialize(sender, program, mode, size, Initializer::State(state)) } /// Creates a new [`Emulator`] analogously to [`new`](Self::new), but it also takes a @@ -86,6 +106,26 @@ impl Emulator

{ mode: Mode, size: Size, preset: Option<&program::Preset>, + ) -> Emulator

{ + Self::initialize( + sender, + program, + mode, + size, + preset.map(Initializer::Preset).unwrap_or(Initializer::Boot), + ) + } + + /// Creates a new [`Emulator`] analogously to [`new`](Self::new), but it also takes a + /// [`program::Preset`] that will be used as the initial state. + /// + /// When the [`Emulator`] has finished booting, an [`Event::Ready`] will be produced. + fn initialize( + sender: mpsc::Sender>, + program: &P, + mode: Mode, + size: Size, + initializer: Initializer<'_, P>, ) -> Emulator

{ use renderer::Headless; @@ -104,12 +144,10 @@ impl Emulator

{ let runtime = Runtime::new(executor, sender); - let (state, task) = runtime.enter(|| { - if let Some(preset) = preset { - preset.boot() - } else { - program.boot() - } + let (state, task) = runtime.enter(|| match initializer { + Initializer::Boot => program.boot(), + Initializer::Preset(preset) => preset.boot(), + Initializer::State(state) => (state, Task::none()), }); let mut emulator = Self { diff --git a/tester/src/lib.rs b/tester/src/lib.rs index 334f17bcdb..958d5c04ec 100644 --- a/tester/src/lib.rs +++ b/tester/src/lib.rs @@ -158,6 +158,7 @@ enum Event { Play, Import, Export, + Reset, Imported(Result), Edit, Edited(text_editor::Action), @@ -232,17 +233,29 @@ impl Tester

{ } Event::Record => { self.edit = None; - self.instructions.clear(); let (sender, receiver) = mpsc::channel(1); - let emulator = Emulator::with_preset( - sender, - program, - self.mode, - self.viewport, - self.preset(program), - ); + let emulator = if let State::Asserting { state, .. } = + std::mem::replace(&mut self.state, State::Empty) + { + Emulator::with_state( + sender, + program, + self.mode, + self.viewport, + state, + ) + } else { + self.instructions.clear(); + Emulator::with_preset( + sender, + program, + self.mode, + self.viewport, + self.preset(program), + ) + }; self.state = State::Recording { emulator }; @@ -342,6 +355,19 @@ impl Tester

{ }) .discard() } + Event::Reset => { + self.edit = None; + self.instructions.clear(); + + let (state, _) = self + .preset(program) + .map(program::Preset::boot) + .unwrap_or_else(|| program.boot()); + + self.state = State::Idle { state }; + + Task::none() + } Event::Imported(Ok(ice)) => { self.viewport = ice.viewport; self.mode = ice.mode; @@ -488,25 +514,33 @@ impl Tester

{ let mut interaction = Some(interaction); while let Some(new_interaction) = interaction.take() { - if let Some(Instruction::Interact(last_interaction)) = - self.instructions.pop() - { - let (merged_interaction, new_interaction) = - last_interaction.merge(new_interaction); - - if let Some(new_interaction) = new_interaction { - self.instructions.push(Instruction::Interact( - merged_interaction, - )); - - self.instructions - .push(Instruction::Interact(new_interaction)); + if let Some(last_instruction) = self.instructions.pop() { + if let Instruction::Interact(last_interaction) = + last_instruction + { + let (merged_interaction, new_interaction) = + last_interaction.merge(new_interaction); + + if let Some(new_interaction) = new_interaction { + self.instructions.push(Instruction::Interact( + merged_interaction, + )); + + self.instructions.push(Instruction::Interact( + new_interaction, + )); + } else { + interaction = Some(merged_interaction); + } } else { - interaction = Some(merged_interaction); + self.instructions.extend([ + last_instruction, + Instruction::Interact(new_interaction), + ]); } } else { self.instructions - .push(Instruction::Interact(new_interaction)); + .push(Instruction::Interact(new_interaction)) } } @@ -807,6 +841,10 @@ impl Tester

{ .style(button::danger) }; + let reset = control(icon::cancel()) + .on_press(Event::Reset) + .style(button::danger); + let import = control(icon::folder()) .on_press_maybe((!self.is_busy()).then_some(Event::Import)) .style(button::secondary); @@ -819,8 +857,9 @@ impl Tester

{ ) .style(button::success); - let controls = - row![import, export, play, record].height(30).spacing(10); + let controls = row![import, export, play, record, reset] + .height(30) + .spacing(10); column![instructions, controls].spacing(10).align_x(Center) };