Moved tests

This commit is contained in:
2026-06-07 00:46:38 -05:00
parent 5ea7e4ec4e
commit 9c2212520c
13 changed files with 1015 additions and 1031 deletions
+139
View File
@@ -0,0 +1,139 @@
use std::time::Duration;
use crate::board::tests::open_board;
use crate::game::GameState;
use crate::script::Direction;
use super::{board_with_object, scripted_object, log_texts};
#[test]
fn init_runs_only_on_run_init_not_at_construction() {
let board = board_with_object(
Some("greet"),
&[("greet", r#"fn init() { log("hello"); }"#)],
);
let mut game = GameState::new(board);
// init must not fire during construction / deserialization.
assert!(game.log.is_empty());
game.run_init();
assert_eq!(log_texts(&game), vec!["hello"]);
}
#[test]
fn tick_calls_script_tick_with_elapsed_seconds() {
let board = board_with_object(
Some("t"),
&[("t", r#"fn tick(dt) { log(dt.to_string()); }"#)],
);
let mut game = GameState::new(board);
game.run_init();
assert!(game.log.is_empty());
game.tick(Duration::from_millis(500));
assert_eq!(game.log.len(), 1);
let logged: f64 = log_texts(&game)[0].parse().unwrap();
assert!((logged - 0.5).abs() < 1e-9);
}
#[test]
fn missing_hooks_and_no_script_are_noops() {
// Object with no script: nothing happens.
let mut game = GameState::new(board_with_object(None, &[]));
game.run_init();
game.tick(Duration::from_millis(33));
assert!(game.log.is_empty());
// Script defines neither init nor tick: also a no-op.
let mut game = GameState::new(board_with_object(
Some("e"),
&[("e", "fn other() { log(\"x\"); }")],
));
game.run_init();
game.tick(Duration::from_millis(33));
assert!(game.log.is_empty());
}
#[test]
fn compile_and_unknown_script_errors_are_logged() {
// A reference to a script name that isn't in the table.
let game = GameState::new(board_with_object(Some("ghost"), &[]));
assert!(log_texts(&game)[0].contains("unknown script 'ghost'"));
// A script that fails to compile is reported at construction time.
let game = GameState::new(board_with_object(Some("bad"), &[("bad", "fn init( {")]));
assert!(log_texts(&game)[0].contains("failed to compile"));
}
#[test]
fn script_reads_board_through_view() {
let board = open_board(
5,
3,
(3, 1),
vec![scripted_object(2, 1, "r")],
&[("r", "fn init() { log(Board.player_x.to_string()); }")],
);
let mut game = GameState::new(board);
game.run_init();
assert_eq!(log_texts(&game), vec!["3"]);
}
#[test]
fn commands_are_routed_to_their_own_source_object() {
// Two objects with different scripts move in opposite directions; each
// must affect only itself (the per-call tag routes the command source).
let board = open_board(
5,
3,
(0, 0),
vec![scripted_object(0, 1, "e"), scripted_object(4, 1, "w")],
&[
("e", "fn init() { move(East); }"),
("w", "fn init() { move(West); }"),
],
);
let mut game = GameState::new(board);
game.run_init();
let b = game.board();
assert_eq!(b.objects[&1].x, 1); // moved east from 0
assert_eq!(b.objects[&2].x, 3); // moved west from 4
}
#[test]
fn start_map_greeter_runs_init() {
// End-to-end against the shipped example map: load it, run init, and
// confirm the greeter read the board (interpolated message) and wrote to
// itself (set_tile). Also guards the example from drifting out of sync.
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/../maps/start.toml");
let board = crate::map_file::load(path).expect("load start.toml");
let mut game = GameState::new(board);
game.run_init();
assert!(
log_texts(&game).iter().any(|t| t.contains("hello from object")),
"greeter init should log a greeting"
);
assert!(
game.board().objects.values().any(|o| o.glyph.tile == 2),
"greeter set_tile(2) should change its glyph"
);
}
// Ensure try_move from the player side also triggers scripted bump
#[test]
fn player_bump_fires_with_negative_one() {
// The player walks into a solid scripted object; the object is bumped with -1
// and the player does not move onto it.
let board = open_board(
3,
1,
(0, 0),
vec![scripted_object(1, 0, "b")],
&[("b", "fn bump(id) { log(`bumped by ${id}`); }")],
);
let mut game = GameState::new(board);
game.run_init();
game.try_move(Direction::East);
assert_eq!((game.board().player.x, game.board().player.y), (0, 0)); // blocked
// bump's log is emitted into the object's queue; a tick flushes it.
game.tick(Duration::from_millis(16));
assert!(log_texts(&game).iter().any(|t| t == "bumped by -1"));
}