mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-07 18:07:42 -06:00
test: add tests for item systems & movement types
This commit is contained in:
159
tests/item.rs
Normal file
159
tests/item.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
use pacman::systems::components::EntityType;
|
||||
|
||||
// Helper functions that extract the core scoring logic from item_system
|
||||
// This allows us to test the business rules without ECS complexity
|
||||
|
||||
fn calculate_score_for_item(entity_type: EntityType) -> Option<u32> {
|
||||
match entity_type {
|
||||
EntityType::Pellet => Some(10),
|
||||
EntityType::PowerPellet => Some(50),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_collectible_item(entity_type: EntityType) -> bool {
|
||||
matches!(entity_type, EntityType::Pellet | EntityType::PowerPellet)
|
||||
}
|
||||
|
||||
fn should_trigger_audio_on_collection(entity_type: EntityType) -> bool {
|
||||
is_collectible_item(entity_type)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pellet_scoring() {
|
||||
assert_eq!(calculate_score_for_item(EntityType::Pellet), Some(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_power_pellet_scoring() {
|
||||
assert_eq!(calculate_score_for_item(EntityType::PowerPellet), Some(50));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_collectible_items_no_score() {
|
||||
assert_eq!(calculate_score_for_item(EntityType::Player), None);
|
||||
assert_eq!(calculate_score_for_item(EntityType::Ghost), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_collectible_item_detection() {
|
||||
assert!(is_collectible_item(EntityType::Pellet));
|
||||
assert!(is_collectible_item(EntityType::PowerPellet));
|
||||
assert!(!is_collectible_item(EntityType::Player));
|
||||
assert!(!is_collectible_item(EntityType::Ghost));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_audio_trigger_for_collectibles() {
|
||||
assert!(should_trigger_audio_on_collection(EntityType::Pellet));
|
||||
assert!(should_trigger_audio_on_collection(EntityType::PowerPellet));
|
||||
assert!(!should_trigger_audio_on_collection(EntityType::Player));
|
||||
assert!(!should_trigger_audio_on_collection(EntityType::Ghost));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_score_progression() {
|
||||
// Test that power pellets are worth more than regular pellets
|
||||
let pellet_score = calculate_score_for_item(EntityType::Pellet).unwrap();
|
||||
let power_pellet_score = calculate_score_for_item(EntityType::PowerPellet).unwrap();
|
||||
|
||||
assert!(power_pellet_score > pellet_score);
|
||||
assert_eq!(power_pellet_score / pellet_score, 5); // Power pellets are worth 5x regular pellets
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entity_type_variants() {
|
||||
// Test all EntityType variants to ensure they're handled appropriately
|
||||
let all_types = vec![
|
||||
EntityType::Player,
|
||||
EntityType::Ghost,
|
||||
EntityType::Pellet,
|
||||
EntityType::PowerPellet,
|
||||
];
|
||||
|
||||
let mut collectible_count = 0;
|
||||
let mut non_collectible_count = 0;
|
||||
|
||||
for entity_type in all_types {
|
||||
if is_collectible_item(entity_type) {
|
||||
collectible_count += 1;
|
||||
// All collectible items should have a score
|
||||
assert!(calculate_score_for_item(entity_type).is_some());
|
||||
} else {
|
||||
non_collectible_count += 1;
|
||||
// Non-collectible items should not have a score
|
||||
assert!(calculate_score_for_item(entity_type).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
// Verify we have the expected number of each type
|
||||
assert_eq!(collectible_count, 2); // Pellet and PowerPellet
|
||||
assert_eq!(non_collectible_count, 2); // Player and Ghost
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_score_accumulation() {
|
||||
// Test score accumulation logic (simulating multiple collections)
|
||||
let mut total_score = 0u32;
|
||||
|
||||
// Collect some items
|
||||
let collected_items = vec![
|
||||
EntityType::Pellet,
|
||||
EntityType::Pellet,
|
||||
EntityType::PowerPellet,
|
||||
EntityType::Pellet,
|
||||
EntityType::PowerPellet,
|
||||
];
|
||||
|
||||
for item in collected_items {
|
||||
if let Some(score) = calculate_score_for_item(item) {
|
||||
total_score += score;
|
||||
}
|
||||
}
|
||||
|
||||
// Expected: 3 pellets (30) + 2 power pellets (100) = 130
|
||||
assert_eq!(total_score, 130);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_collision_filtering_logic() {
|
||||
// Test the logic for determining valid collision pairs
|
||||
// This mirrors the logic in item_system that checks entity types
|
||||
|
||||
let test_cases = vec![
|
||||
(EntityType::Player, EntityType::Pellet, true),
|
||||
(EntityType::Player, EntityType::PowerPellet, true),
|
||||
(EntityType::Player, EntityType::Ghost, false), // Not handled by item system
|
||||
(EntityType::Player, EntityType::Player, false), // Not a valid collision
|
||||
(EntityType::Ghost, EntityType::Pellet, false), // Ghosts don't collect items
|
||||
(EntityType::Pellet, EntityType::PowerPellet, false), // Items don't interact
|
||||
];
|
||||
|
||||
for (entity1, entity2, should_be_valid) in test_cases {
|
||||
let is_valid_item_collision = (entity1 == EntityType::Player && is_collectible_item(entity2))
|
||||
|| (entity2 == EntityType::Player && is_collectible_item(entity1));
|
||||
|
||||
assert_eq!(
|
||||
is_valid_item_collision, should_be_valid,
|
||||
"Failed for collision between {:?} and {:?}",
|
||||
entity1, entity2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_item_collection_side_effects() {
|
||||
// Test that collecting items should trigger the expected side effects
|
||||
let collectible_items = vec![EntityType::Pellet, EntityType::PowerPellet];
|
||||
|
||||
for item in collectible_items {
|
||||
// Should provide score
|
||||
assert!(calculate_score_for_item(item).is_some());
|
||||
|
||||
// Should trigger audio
|
||||
assert!(should_trigger_audio_on_collection(item));
|
||||
|
||||
// Should be marked as collectible
|
||||
assert!(is_collectible_item(item));
|
||||
}
|
||||
}
|
||||
195
tests/movement.rs
Normal file
195
tests/movement.rs
Normal file
@@ -0,0 +1,195 @@
|
||||
use glam::Vec2;
|
||||
use pacman::map::direction::Direction;
|
||||
use pacman::map::graph::{Graph, Node};
|
||||
use pacman::systems::movement::{BufferedDirection, Position, Velocity};
|
||||
|
||||
fn create_test_graph() -> Graph {
|
||||
let mut graph = Graph::new();
|
||||
|
||||
// Add a few test nodes
|
||||
let node0 = graph.add_node(Node {
|
||||
position: Vec2::new(0.0, 0.0),
|
||||
});
|
||||
let node1 = graph.add_node(Node {
|
||||
position: Vec2::new(16.0, 0.0),
|
||||
});
|
||||
let node2 = graph.add_node(Node {
|
||||
position: Vec2::new(0.0, 16.0),
|
||||
});
|
||||
|
||||
// Connect them
|
||||
graph.connect(node0, node1, false, None, Direction::Right).unwrap();
|
||||
graph.connect(node0, node2, false, None, Direction::Down).unwrap();
|
||||
|
||||
graph
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_position_is_at_node() {
|
||||
let stopped_pos = Position::Stopped { node: 0 };
|
||||
let moving_pos = Position::Moving {
|
||||
from: 0,
|
||||
to: 1,
|
||||
remaining_distance: 8.0,
|
||||
};
|
||||
|
||||
assert!(stopped_pos.is_at_node());
|
||||
assert!(!moving_pos.is_at_node());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_position_current_node() {
|
||||
let stopped_pos = Position::Stopped { node: 5 };
|
||||
let moving_pos = Position::Moving {
|
||||
from: 3,
|
||||
to: 7,
|
||||
remaining_distance: 12.0,
|
||||
};
|
||||
|
||||
assert_eq!(stopped_pos.current_node(), 5);
|
||||
assert_eq!(moving_pos.current_node(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_position_tick_no_movement_when_stopped() {
|
||||
let mut pos = Position::Stopped { node: 0 };
|
||||
let result = pos.tick(5.0);
|
||||
|
||||
assert!(result.is_none());
|
||||
assert_eq!(pos, Position::Stopped { node: 0 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_position_tick_no_movement_when_zero_distance() {
|
||||
let mut pos = Position::Moving {
|
||||
from: 0,
|
||||
to: 1,
|
||||
remaining_distance: 10.0,
|
||||
};
|
||||
let result = pos.tick(0.0);
|
||||
|
||||
assert!(result.is_none());
|
||||
assert_eq!(
|
||||
pos,
|
||||
Position::Moving {
|
||||
from: 0,
|
||||
to: 1,
|
||||
remaining_distance: 10.0,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_position_tick_partial_movement() {
|
||||
let mut pos = Position::Moving {
|
||||
from: 0,
|
||||
to: 1,
|
||||
remaining_distance: 10.0,
|
||||
};
|
||||
let result = pos.tick(3.0);
|
||||
|
||||
assert!(result.is_none());
|
||||
assert_eq!(
|
||||
pos,
|
||||
Position::Moving {
|
||||
from: 0,
|
||||
to: 1,
|
||||
remaining_distance: 7.0,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_position_tick_exact_arrival() {
|
||||
let mut pos = Position::Moving {
|
||||
from: 0,
|
||||
to: 1,
|
||||
remaining_distance: 5.0,
|
||||
};
|
||||
let result = pos.tick(5.0);
|
||||
|
||||
assert!(result.is_none());
|
||||
assert_eq!(pos, Position::Stopped { node: 1 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_position_tick_overshoot_with_overflow() {
|
||||
let mut pos = Position::Moving {
|
||||
from: 0,
|
||||
to: 1,
|
||||
remaining_distance: 3.0,
|
||||
};
|
||||
let result = pos.tick(8.0);
|
||||
|
||||
assert_eq!(result, Some(5.0));
|
||||
assert_eq!(pos, Position::Stopped { node: 1 });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_position_get_pixel_position_stopped() {
|
||||
let graph = create_test_graph();
|
||||
let pos = Position::Stopped { node: 0 };
|
||||
|
||||
let pixel_pos = pos.get_pixel_position(&graph).unwrap();
|
||||
let expected = Vec2::new(
|
||||
0.0 + pacman::constants::BOARD_PIXEL_OFFSET.x as f32,
|
||||
0.0 + pacman::constants::BOARD_PIXEL_OFFSET.y as f32,
|
||||
);
|
||||
|
||||
assert_eq!(pixel_pos, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_position_get_pixel_position_moving() {
|
||||
let graph = create_test_graph();
|
||||
let pos = Position::Moving {
|
||||
from: 0,
|
||||
to: 1,
|
||||
remaining_distance: 8.0, // Halfway through a 16-unit edge
|
||||
};
|
||||
|
||||
let pixel_pos = pos.get_pixel_position(&graph).unwrap();
|
||||
// Should be halfway between (0,0) and (16,0), so at (8,0) plus offset
|
||||
let expected = Vec2::new(
|
||||
8.0 + pacman::constants::BOARD_PIXEL_OFFSET.x as f32,
|
||||
0.0 + pacman::constants::BOARD_PIXEL_OFFSET.y as f32,
|
||||
);
|
||||
|
||||
assert_eq!(pixel_pos, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_velocity_basic_properties() {
|
||||
let velocity = Velocity {
|
||||
speed: 2.5,
|
||||
direction: Direction::Up,
|
||||
};
|
||||
|
||||
assert_eq!(velocity.speed, 2.5);
|
||||
assert_eq!(velocity.direction, Direction::Up);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffered_direction_none() {
|
||||
let buffered = BufferedDirection::None;
|
||||
assert_eq!(buffered, BufferedDirection::None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_buffered_direction_some() {
|
||||
let buffered = BufferedDirection::Some {
|
||||
direction: Direction::Left,
|
||||
remaining_time: 0.5,
|
||||
};
|
||||
|
||||
if let BufferedDirection::Some {
|
||||
direction,
|
||||
remaining_time,
|
||||
} = buffered
|
||||
{
|
||||
assert_eq!(direction, Direction::Left);
|
||||
assert_eq!(remaining_time, 0.5);
|
||||
} else {
|
||||
panic!("Expected BufferedDirection::Some");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user