//! Integration Tests for BSP dungeon generation #[cfg(test)] mod tests { use dungeon::{ bsp, map::{self, TILE_COUNT}, pos::Pos, }; use rand::{Rng, SeedableRng, rngs::SmallRng}; /// Generate a set of test seeds for reproducibility with a seeded RNG fn generate_test_seeds(seed: u64) -> Vec { let mut rng = SmallRng::seed_from_u64(seed); // Generate 100 random u64 seeds (0..100).map(|_| rng.random_range(0..u64::MAX)).collect() } /// Basic integration test for BSP generation #[test] fn test_bsp_integration() { let test_seeds = generate_test_seeds(123456); for seed in test_seeds { let mut rng = SmallRng::seed_from_u64(seed); let floor = bsp::generate(&mut rng); // Basic integration test: ensure we get valid data assert!(!floor.tiles().is_empty()); } } /// Test that BSP-generated floors have a valid player start #[test] fn test_bsp_player_start() { let test_seeds = generate_test_seeds(654321); for seed in test_seeds { let mut rng = SmallRng::seed_from_u64(seed); let floor = bsp::generate(&mut rng); // Ensure player start is a room tile let start = floor.player_start(); assert_eq!(floor.get(start), map::Tile::Room); } } /// Test that BSP-generated floors have at least one room tile #[test] fn test_bsp_2_or_more_rooms() { let test_seeds = generate_test_seeds(111222); for seed in test_seeds { let mut rng = SmallRng::seed_from_u64(seed); let floor = bsp::generate(&mut rng); // Ensure we have at least one room tile let room_count = floor .tiles() .iter() .filter(|&&tile| tile == map::Tile::Room) .count(); assert!( room_count >= 1, "Expected at least one room tile, found {room_count}" ); } } /// Test that BSP-generated floors have walls on the borders #[test] fn test_bsp_walls_on_borders() { let test_seeds = generate_test_seeds(777888); for seed in test_seeds { let mut rng = SmallRng::seed_from_u64(seed); let floor = bsp::generate(&mut rng); // Go through all tiles, and ensure border tiles are walls for pos in Pos::values() { if pos.is_border() { assert_eq!( floor.get(pos), map::Tile::Wall, "Expected wall at border position {pos:?}" ); } } } } // Test that BSP-generated floors are reproducible with the same seed #[test] fn test_bsp_reproducibility() { let test_seeds = generate_test_seeds(111111); for seed in test_seeds { let mut rng1 = SmallRng::seed_from_u64(seed); let mut rng2 = SmallRng::seed_from_u64(seed); let floor1 = bsp::generate(&mut rng1); let floor2 = bsp::generate(&mut rng2); assert_eq!( floor1.tiles(), floor2.tiles(), "Tiles differ for same seed {seed}" ); assert_eq!( floor1.player_start(), floor2.player_start(), "Player starts differ for same seed {seed}" ); } } // Test that all `air` tiles (`Tile::Room` and `Tile::Hallway`) are reachable from the player start #[test] fn test_bsp_all_air_tiles_reachable() { let test_seeds = generate_test_seeds(333444); for seed in test_seeds { check_air_tiles_reachable(seed); } } // Helper function to check that all air tiles are reachable from player start fn check_air_tiles_reachable(seed: u64) { let mut rng = SmallRng::seed_from_u64(seed); let floor = bsp::generate(&mut rng); // BFS to find all reachable air tiles let mut visited = vec![false; TILE_COUNT]; let mut queue = vec![floor.player_start()]; visited[floor.player_start().idx()] = true; while let Some(pos) = queue.pop() { for neighbor in pos.neighbors() { let idx = neighbor.idx(); if !visited[idx] && floor.get(neighbor) != map::Tile::Wall { visited[idx] = true; queue.push(neighbor); } } } for (i, &tile) in floor.tiles().iter().enumerate() { if tile == map::Tile::Room || tile == map::Tile::Hallway { assert!(visited[i], "Unreachable air tile at index {i}"); } } } }