generated from GarandPLG/rust-flake-template
- Adjust rustfmt.toml to set tab_spaces = 4. - Reformat imports, struct definitions, function bodies, and match arms across the project to match the new style (aligned fields, added spacing, etc.). - Ensure all code complies with the updated rustfmt settings.
535 lines
19 KiB
Rust
535 lines
19 KiB
Rust
use clap::Parser;
|
|
use ratatui::{
|
|
Terminal,
|
|
backend::TestBackend,
|
|
buffer::Buffer,
|
|
crossterm::event::{KeyCode, KeyEvent, KeyModifiers},
|
|
layout::Rect,
|
|
widgets::Widget,
|
|
};
|
|
use std::sync::mpsc::{Receiver, Sender, channel};
|
|
use war_in_tunnels::{
|
|
app::{
|
|
App, GameStates, View, handle_keybindings,
|
|
states::{
|
|
SkirmishState,
|
|
skirmish_states::{
|
|
BoardState, CellSizes, MoveFocusedCell, Players, ZoomLevel,
|
|
structures::{Structure, Structures, Tunnel},
|
|
tasks::{DiggingTask, MovingTask, Task, Tasks},
|
|
units::{MinerUnit, Unit, Units},
|
|
},
|
|
},
|
|
threads::{AudioCmd, TICK_MS},
|
|
},
|
|
cli::Cli,
|
|
};
|
|
|
|
fn game() -> SkirmishState {
|
|
let args = Cli::try_parse_from(["game", "--disable-audio"]).unwrap();
|
|
GameStates::new(&args, &Rect::new(0, 0, 120, 40)).skirmish
|
|
}
|
|
|
|
fn queue(board: &mut BoardState, origin: (usize, usize), path: &[(usize, usize)]) {
|
|
let unit = board.cells[origin.0][origin.1]
|
|
.get_mut_option_unit()
|
|
.as_mut()
|
|
.unwrap();
|
|
let power = unit.get_digging_power();
|
|
unit.set_task(Tasks::Digging(DiggingTask::new(
|
|
path.iter().copied().collect(),
|
|
power,
|
|
)));
|
|
}
|
|
|
|
fn tunnel(board: &mut BoardState, row: usize, col: usize) {
|
|
board.cells[row][col].set_structure(Structures::Tunnel(Tunnel::new()));
|
|
}
|
|
|
|
fn unit(board: &BoardState, row: usize, col: usize) -> &Units {
|
|
board.cells[row][col]
|
|
.get_ref_option_unit()
|
|
.as_ref()
|
|
.unwrap()
|
|
}
|
|
|
|
fn travel_ticks() -> u16 {
|
|
3000_u16.div_ceil(u16::from(TICK_MS))
|
|
}
|
|
|
|
fn advance(game: &mut SkirmishState, ticks: u16) {
|
|
for _ in 0..ticks {
|
|
game.tick_update();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn tick_damages_stone_before_moving_and_finishes_entire_route() {
|
|
let mut game = game();
|
|
queue(&mut game.board, (5, 2), &[(5, 3), (5, 4)]);
|
|
game.tick_update();
|
|
assert_eq!(game.turn_counter, 1);
|
|
let durability = game.board.cells[5][3].get_structure().get_durability();
|
|
assert!(durability < 1000 && durability > 0);
|
|
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
|
assert!(game.board.cells[5][3].get_ref_option_unit().is_none());
|
|
for _ in 0..500 {
|
|
game.tick_update();
|
|
}
|
|
for col in [3, 4] {
|
|
assert!(matches!(
|
|
game.board.cells[5][col].get_structure(),
|
|
Structures::Tunnel(_)
|
|
));
|
|
}
|
|
assert!(matches!(
|
|
game.board.cells[5][2].get_structure(),
|
|
Structures::Base(_)
|
|
));
|
|
assert_eq!(unit(&game.board, 5, 4).get_coords(), (5, 4));
|
|
assert!(unit(&game.board, 5, 4).get_ref_tasks().is_empty());
|
|
assert!(game.board.cells[5][2].get_ref_option_unit().is_none());
|
|
assert!(game.board.cells[5][3].get_ref_option_unit().is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn each_tunnel_step_takes_three_seconds_in_both_directions() {
|
|
for path in [[(5, 3), (5, 4)], [(4, 2), (3, 2)]] {
|
|
let mut game = game();
|
|
for (row, col) in path {
|
|
tunnel(&mut game.board, row, col);
|
|
}
|
|
queue(&mut game.board, (5, 2), &path);
|
|
advance(&mut game, travel_ticks() - 1);
|
|
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
|
game.tick_update();
|
|
assert_eq!(
|
|
unit(&game.board, path[0].0, path[0].1).get_coords(),
|
|
path[0]
|
|
);
|
|
assert_eq!(
|
|
unit(&game.board, path[0].0, path[0].1).get_ref_tasks()[0]
|
|
.get_path()
|
|
.len(),
|
|
1
|
|
);
|
|
advance(&mut game, travel_ticks() - 1);
|
|
assert_eq!(
|
|
unit(&game.board, path[0].0, path[0].1).get_coords(),
|
|
path[0]
|
|
);
|
|
game.tick_update();
|
|
assert!(
|
|
unit(&game.board, path[1].0, path[1].1)
|
|
.get_ref_tasks()
|
|
.is_empty()
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn queued_tasks_execute_in_order_and_keep_unit_coordinates_current() {
|
|
let mut game = game();
|
|
tunnel(&mut game.board, 5, 3);
|
|
tunnel(&mut game.board, 5, 4);
|
|
queue(&mut game.board, (5, 2), &[(5, 3)]);
|
|
queue(&mut game.board, (5, 2), &[(5, 4)]);
|
|
advance(&mut game, travel_ticks());
|
|
assert_eq!(unit(&game.board, 5, 3).get_ref_tasks().len(), 1);
|
|
advance(&mut game, travel_ticks());
|
|
assert_eq!(unit(&game.board, 5, 4).get_coords(), (5, 4));
|
|
assert!(unit(&game.board, 5, 4).get_ref_tasks().is_empty());
|
|
queue(&mut game.board, (5, 4), &[(5, 3), (5, 2)]);
|
|
advance(&mut game, 2 * travel_ticks() - 1);
|
|
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
|
game.tick_update();
|
|
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
|
assert!(matches!(
|
|
game.board.cells[5][2].get_structure(),
|
|
Structures::Base(_)
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn occupied_tunnel_waits_without_losing_units_or_task() {
|
|
let mut game = game();
|
|
tunnel(&mut game.board, 5, 3);
|
|
*game.board.cells[5][3].get_mut_option_unit() =
|
|
Some(Units::Miner(MinerUnit::new(Players::Player, (5, 3))));
|
|
queue(&mut game.board, (5, 2), &[(5, 3)]);
|
|
for _ in 0..3 {
|
|
game.tick_update();
|
|
}
|
|
assert_eq!(unit(&game.board, 5, 2).get_ref_tasks().len(), 1);
|
|
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
|
game.board.cells[5][3].get_mut_option_unit().take();
|
|
advance(&mut game, travel_ticks() - 1);
|
|
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
|
game.tick_update();
|
|
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
|
assert!(unit(&game.board, 5, 3).get_ref_tasks().is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn ore_is_not_destroyed_by_digging_task() {
|
|
let mut game = game();
|
|
let ore = game.board.cells[5][1].get_structure();
|
|
queue(&mut game.board, (5, 2), &[(5, 1)]);
|
|
for _ in 0..200 {
|
|
game.tick_update();
|
|
}
|
|
assert_eq!(game.board.cells[5][1].get_structure(), ore);
|
|
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
|
}
|
|
|
|
#[test]
|
|
fn invalid_or_empty_routes_do_not_teleport_or_remove_miner() {
|
|
for path in [vec![], vec![(usize::MAX, usize::MAX)], vec![(0, 0)]] {
|
|
let mut game = game();
|
|
queue(&mut game.board, (5, 2), &path);
|
|
game.tick_update();
|
|
assert_eq!(unit(&game.board, 5, 2).get_coords(), (5, 2));
|
|
assert!(unit(&game.board, 5, 2).get_ref_tasks().is_empty());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn only_player_miner_can_start_drawing_route() {
|
|
let mut game = game();
|
|
game.board.toggle_marking();
|
|
assert!(!game.board.marked_cells.marking_cells);
|
|
for _ in 0..36 {
|
|
game.board.change_focused_cell(MoveFocusedCell::Right);
|
|
}
|
|
assert_eq!(game.board.get_focused_cell().get_coords(), (5, 39));
|
|
game.board.toggle_marking();
|
|
assert!(!game.board.marked_cells.marking_cells);
|
|
assert!(game.board.get_marked_cells().is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn drawing_route_pauses_selected_miner_and_boundary_moves_do_not_repeat_cells() {
|
|
let mut game = game();
|
|
game.board.change_focused_cell(MoveFocusedCell::Left);
|
|
queue(&mut game.board, (5, 2), &[(5, 3)]);
|
|
game.board.toggle_marking();
|
|
game.tick_update();
|
|
assert_eq!(
|
|
game.board.cells[5][3].get_structure().get_durability(),
|
|
1000
|
|
);
|
|
for _ in 0..10 {
|
|
game.board.change_focused_cell(MoveFocusedCell::Up);
|
|
}
|
|
let coords: Vec<_> = game.board.get_marked_cells().iter().copied().collect();
|
|
assert!(coords.windows(2).all(|pair| pair[0] != pair[1]));
|
|
game.board.end_marking_cells(true);
|
|
game.tick_update();
|
|
assert!(game.board.cells[5][3].get_structure().get_durability() < 1000);
|
|
}
|
|
|
|
#[test]
|
|
fn drawing_another_task_extends_the_end_of_queued_work() {
|
|
let mut game = game();
|
|
for col in [3, 4, 5] {
|
|
tunnel(&mut game.board, 5, col);
|
|
}
|
|
game.board.change_focused_cell(MoveFocusedCell::Left);
|
|
game.board.toggle_marking();
|
|
game.board.change_focused_cell(MoveFocusedCell::Right);
|
|
game.board.change_focused_cell(MoveFocusedCell::Right);
|
|
game.board.toggle_marking();
|
|
game.board.change_focused_cell(MoveFocusedCell::Left);
|
|
game.board.change_focused_cell(MoveFocusedCell::Left);
|
|
game.board.toggle_marking();
|
|
assert_eq!(game.board.get_focused_cell().get_coords(), (5, 4));
|
|
game.board.change_focused_cell(MoveFocusedCell::Right);
|
|
game.board.toggle_marking();
|
|
assert_eq!(unit(&game.board, 5, 2).get_ref_tasks().len(), 2);
|
|
advance(&mut game, 3 * travel_ticks());
|
|
assert_eq!(unit(&game.board, 5, 5).get_coords(), (5, 5));
|
|
assert!(unit(&game.board, 5, 5).get_ref_tasks().is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn freshly_dug_cell_is_entered_immediately_then_tunnel_travel_takes_three_seconds() {
|
|
let mut game = game();
|
|
tunnel(&mut game.board, 5, 4);
|
|
queue(&mut game.board, (5, 2), &[(5, 3), (5, 4)]);
|
|
for _ in 0..200 {
|
|
game.tick_update();
|
|
if matches!(
|
|
game.board.cells[5][3].get_structure(),
|
|
Structures::Tunnel(_)
|
|
) {
|
|
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
|
advance(&mut game, travel_ticks() - 1);
|
|
assert_eq!(unit(&game.board, 5, 3).get_coords(), (5, 3));
|
|
game.tick_update();
|
|
assert!(unit(&game.board, 5, 4).get_ref_tasks().is_empty());
|
|
return;
|
|
}
|
|
}
|
|
panic!("miner did not finish digging the stone");
|
|
}
|
|
|
|
#[test]
|
|
fn miner_symbol_in_tunnel_uses_owner_color_even_when_selected_or_marked() {
|
|
for owner in [Players::Player, Players::Enemy] {
|
|
for zoom in [ZoomLevel::Default, ZoomLevel::ZoomedIn] {
|
|
for (selected, marked) in [(false, false), (true, false), (false, true), (true, true)] {
|
|
let mut game = game();
|
|
let cell = &mut game.board.cells[5][3];
|
|
cell.set_structure(Structures::Tunnel(Tunnel::new()));
|
|
cell.set_selected(selected)
|
|
.set_marked(marked)
|
|
.set_zoom_level(zoom);
|
|
*cell.get_mut_option_unit() = Some(Units::Miner(MinerUnit::new(owner, (5, 3))));
|
|
let area = Rect::new(
|
|
0,
|
|
0,
|
|
zoom.get_cell_size(CellSizes::Width) as u16,
|
|
zoom.get_cell_size(CellSizes::Height) as u16,
|
|
);
|
|
let mut buffer = Buffer::empty(area);
|
|
cell.clone().render(area, &mut buffer);
|
|
let marker = buffer
|
|
.content
|
|
.iter()
|
|
.find(|cell| cell.symbol() == "M")
|
|
.unwrap();
|
|
assert_eq!(marker.fg, owner.get_color(), "{owner:?} {zoom:?}");
|
|
assert!(buffer.content.iter().any(|cell| cell.symbol() == "T"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn tui_shows_movement_percentage_and_clears_it_after_each_step() {
|
|
for zoom in [
|
|
ZoomLevel::ZoomedIn,
|
|
ZoomLevel::Default,
|
|
ZoomLevel::ZoomedOut,
|
|
] {
|
|
let mut game = game();
|
|
for col in [2, 3, 4] {
|
|
tunnel(&mut game.board, 5, col);
|
|
}
|
|
queue(&mut game.board, (5, 2), &[(5, 3), (5, 4)]);
|
|
let render = |board: &BoardState, col: usize| {
|
|
let area = Rect::new(
|
|
0,
|
|
0,
|
|
zoom.get_cell_size(CellSizes::Width) as u16,
|
|
zoom.get_cell_size(CellSizes::Height) as u16,
|
|
);
|
|
let mut buffer = Buffer::empty(area);
|
|
let mut cell = board.get_ref_cell(5, col).clone();
|
|
cell.set_zoom_level(zoom);
|
|
cell.render(area, &mut buffer);
|
|
buffer
|
|
};
|
|
let text = |buffer: &Buffer| -> String {
|
|
buffer.content.iter().map(|cell| cell.symbol()).collect()
|
|
};
|
|
assert!(!text(&render(&game.board, 2)).contains('%'));
|
|
advance(&mut game, 46);
|
|
let buffer = render(&game.board, 2);
|
|
assert!(text(&buffer).contains("50%"), "{zoom:?}");
|
|
if zoom != ZoomLevel::ZoomedOut {
|
|
let marker = buffer
|
|
.content
|
|
.iter()
|
|
.find(|cell| cell.symbol() == "M")
|
|
.unwrap();
|
|
assert_eq!(marker.fg, Players::Player.get_color());
|
|
}
|
|
advance(&mut game, travel_ticks() - 46);
|
|
assert!(!text(&render(&game.board, 2)).contains('%'));
|
|
assert!(!text(&render(&game.board, 3)).contains('%'));
|
|
game.tick_update();
|
|
assert!(text(&render(&game.board, 3)).contains("1%"));
|
|
advance(&mut game, travel_ticks() - 1);
|
|
assert!(!text(&render(&game.board, 4)).contains('%'));
|
|
assert!(unit(&game.board, 5, 4).get_ref_tasks().is_empty());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn confirmed_route_splits_into_moving_and_digging_segments() {
|
|
let mut game = game();
|
|
for col in [3, 4, 7] {
|
|
tunnel(&mut game.board, 5, col);
|
|
}
|
|
game.board.change_focused_cell(MoveFocusedCell::Left);
|
|
game.board.toggle_marking();
|
|
for _ in 0..5 {
|
|
game.board.change_focused_cell(MoveFocusedCell::Right);
|
|
}
|
|
game.board.toggle_marking();
|
|
let tasks = unit(&game.board, 5, 2).get_ref_tasks();
|
|
assert_eq!(tasks.len(), 3);
|
|
assert!(matches!(tasks[0], Tasks::Moving(_)));
|
|
assert_eq!(
|
|
tasks[0].get_path().iter().copied().collect::<Vec<_>>(),
|
|
[(5, 3), (5, 4)]
|
|
);
|
|
assert!(matches!(tasks[1], Tasks::Digging(_)));
|
|
assert_eq!(
|
|
tasks[1].get_path().iter().copied().collect::<Vec<_>>(),
|
|
[(5, 5), (5, 6)]
|
|
);
|
|
assert!(matches!(tasks[2], Tasks::Moving(_)));
|
|
assert_eq!(tasks[0].get_icon(), '→');
|
|
assert_eq!(tasks[1].get_icon(), '⛏');
|
|
advance(&mut game, 2 * travel_ticks());
|
|
assert!(matches!(
|
|
unit(&game.board, 5, 4).get_ref_tasks()[0],
|
|
Tasks::Digging(_)
|
|
));
|
|
advance(&mut game, 2 * 182);
|
|
assert!(matches!(
|
|
unit(&game.board, 5, 6).get_ref_tasks()[0],
|
|
Tasks::Moving(_)
|
|
));
|
|
advance(&mut game, travel_ticks());
|
|
assert!(unit(&game.board, 5, 7).get_ref_tasks().is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn moving_task_does_not_damage_walkable_structure() {
|
|
let mut game = game();
|
|
tunnel(&mut game.board, 5, 3);
|
|
let structure = game.board.cells[5][3].get_structure();
|
|
game.board.cells[5][2]
|
|
.get_mut_option_unit()
|
|
.set_task(Tasks::Moving(MovingTask::new([(5, 3)].into())));
|
|
advance(&mut game, travel_ticks());
|
|
assert_eq!(game.board.cells[5][3].get_structure(), structure);
|
|
assert!(unit(&game.board, 5, 3).get_ref_tasks().is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn previously_dug_route_becomes_moving_without_instant_steps() {
|
|
let mut game = game();
|
|
queue(&mut game.board, (5, 2), &[(5, 3), (5, 4), (5, 3)]);
|
|
advance(&mut game, 2 * 182);
|
|
assert_eq!(unit(&game.board, 5, 4).get_coords(), (5, 4));
|
|
game.tick_update();
|
|
assert!(matches!(
|
|
unit(&game.board, 5, 4).get_ref_tasks()[0],
|
|
Tasks::Moving(_)
|
|
));
|
|
advance(&mut game, travel_ticks() - 2);
|
|
assert_eq!(unit(&game.board, 5, 4).get_coords(), (5, 4));
|
|
game.tick_update();
|
|
assert!(unit(&game.board, 5, 3).get_ref_tasks().is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn route_reclassifies_as_digging_if_target_is_stone() {
|
|
let mut game = game();
|
|
game.board.cells[5][2]
|
|
.get_mut_option_unit()
|
|
.set_task(Tasks::Moving(MovingTask::new([(5, 3)].into())));
|
|
game.tick_update();
|
|
assert!(matches!(
|
|
unit(&game.board, 5, 2).get_ref_tasks()[0],
|
|
Tasks::Digging(_)
|
|
));
|
|
assert!(game.board.cells[5][3].get_structure().get_durability() < 1000);
|
|
advance(&mut game, 181);
|
|
assert!(unit(&game.board, 5, 3).get_ref_tasks().is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn tab_panel_shows_current_step_and_total_for_active_and_pending_tasks() -> () {
|
|
let args: Cli = Cli::try_parse_from(["game", "--disable-audio", "--view", "skirmish"]).unwrap();
|
|
let channels: (Sender<AudioCmd>, Receiver<AudioCmd>) = channel();
|
|
let mut app: App = App::new(args, channels.0);
|
|
app.window_area = Rect::new(0, 0, 120, 60);
|
|
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
|
let state: &mut SkirmishState = &mut app.states_mut().unwrap().skirmish;
|
|
tunnel(&mut state.board, 5, 3);
|
|
tunnel(&mut state.board, 5, 4);
|
|
state.board.cells[5][2]
|
|
.get_mut_option_unit()
|
|
.set_task(Tasks::Moving(MovingTask::new([(5, 3), (5, 4)].into())));
|
|
state.board.cells[5][2]
|
|
.get_mut_option_unit()
|
|
.set_task(Tasks::Digging(DiggingTask::new([(5, 5)].into(), 0.55)));
|
|
advance(state, travel_ticks() + 46);
|
|
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE));
|
|
let mut terminal: Terminal<TestBackend> = Terminal::new(TestBackend::new(120, 60)).unwrap();
|
|
terminal
|
|
.draw(|frame: &mut ratatui::Frame<'_>| -> () {
|
|
frame.render_widget(&mut app, frame.area());
|
|
})
|
|
.unwrap();
|
|
let text: String = terminal
|
|
.backend()
|
|
.buffer()
|
|
.content
|
|
.iter()
|
|
.map(|cell: &ratatui::buffer::Cell| -> &str { cell.symbol() })
|
|
.collect();
|
|
assert!(text.contains("Step: 50% | Total: 75%"));
|
|
assert!(text.contains("Step: 0% | Total: 0%"));
|
|
}
|
|
|
|
#[test]
|
|
fn space_route_tick_and_tui_show_remaining_durability_at_every_zoom() {
|
|
let args = Cli::try_parse_from(["game", "--disable-audio", "--view", "skirmish"]).unwrap();
|
|
let (tx, _) = channel();
|
|
let mut app = App::new(args, tx);
|
|
app.window_area = Rect::new(0, 0, 120, 40);
|
|
app.states = Some(GameStates::new(&app.args, &app.window_area));
|
|
for code in [
|
|
KeyCode::Left,
|
|
KeyCode::Char(' '),
|
|
KeyCode::Right,
|
|
KeyCode::Char(' '),
|
|
] {
|
|
handle_keybindings(&mut app, KeyEvent::new(code, KeyModifiers::NONE));
|
|
}
|
|
app.states_mut().unwrap().skirmish.tick_update();
|
|
let durability = app.states_ref().unwrap().skirmish.board.cells[5][3]
|
|
.get_structure()
|
|
.get_durability();
|
|
let mut terminal = Terminal::new(TestBackend::new(120, 40)).unwrap();
|
|
for zoom in [
|
|
ZoomLevel::ZoomedIn,
|
|
ZoomLevel::Default,
|
|
ZoomLevel::ZoomedOut,
|
|
] {
|
|
let state = &mut app.states_mut().unwrap().skirmish;
|
|
state.board.change_zoom(zoom);
|
|
state.board.vertical_offset.set_value(5);
|
|
terminal
|
|
.draw(|frame| frame.render_widget(&mut app, frame.area()))
|
|
.unwrap();
|
|
let text: String = terminal
|
|
.backend()
|
|
.buffer()
|
|
.content
|
|
.iter()
|
|
.map(|cell| cell.symbol())
|
|
.collect();
|
|
assert!(text.contains("99%"), "{zoom:?}");
|
|
}
|
|
handle_keybindings(&mut app, KeyEvent::new(KeyCode::Tab, KeyModifiers::NONE));
|
|
terminal
|
|
.draw(|frame| frame.render_widget(&mut app, frame.area()))
|
|
.unwrap();
|
|
let text: String = terminal
|
|
.backend()
|
|
.buffer()
|
|
.content
|
|
.iter()
|
|
.map(|cell| cell.symbol())
|
|
.collect();
|
|
assert!(text.contains(&format!("{durability}/1000")));
|
|
assert_eq!(app.view, View::Skirmish);
|
|
}
|