def test_available_actions_corner(game, board, hero): up_position = Position(0, 1) right_position = Position(1, 0) down_position = Position(0, -1) left_position = Position(-1, 0) valid_positions = [up_position, right_position] def is_valid_side_effect(pos): return pos in valid_positions board.is_valid.side_effect = is_valid_side_effect board.has_enemies_in_range.return_value = False expected_actions = [GameActions.MOVE_UP, GameActions.MOVE_RIGHT] available_actions = game.available_actions() assert len(available_actions) == len(expected_actions), f"There should be {len(expected_actions)} available actions, but there are {len(available_actions)}" for e in expected_actions: assert e in available_actions, f"{e} should be an available action!" board.is_valid.assert_any_call(up_position) board.is_valid.assert_any_call(down_position) board.is_valid.assert_any_call(right_position) board.is_valid.assert_any_call(left_position) board.has_enemies_in_range.assert_called_once_with(hero.position, 1)
def test_another_distance(): from_pos = Position(5, 4) to_pos = Position(1, 2) expected_distance = 6 distance = from_pos.distance(to=to_pos) assert distance == expected_distance, \ f"Distance from {from_pos} to {to_pos} was be {distance}, but should be {expected_distance}"
def test_distance(): from_pos = Position(0, 0) to_pos = Position(1, 1) expected_distance = 2 distance = from_pos.distance(to=to_pos) assert distance == expected_distance, \ f"Distance from {from_pos} to {to_pos} was be {distance}, but should be {expected_distance}"
def test_move_when_in_not_range(capsys, unit): destination = Position(10, 0) expected_msg = f"Target position {destination} is at a distance of 10 and my speed is 1, I can't reach it!" result = unit.move(destination) out, _ = capsys.readouterr() assert result == False, "Result should be False when unit can't move to the position" assert unit.position == Position( 0, 0), f"Position should still be the same after a failed attempt to move" assert expected_msg in out
def test_is_enemy_with_enemy(board, enemy): position = Position(0, 0) board.place(enemy, position) is_enemy = board.is_enemy(position) assert is_enemy == True, f"Game element at {position} is an enemy"
def test_empty_slots(board): expected_positions = [ Position(x, y) for x in range(board.width) for y in range(board.height) ] assert sorted(board.empty_slots) == sorted(expected_positions), \ f"The board doesn't have all the expected empty slots"
def hero(): hero = mock.MagicMock(spec=Hero) hero.name = "TestHero" hero.speed = 1 hero.position = Position(0, 0) return hero
def test_attack_enemy_not_killed_and_hero_killed(capsys, game, hero, board): hero.is_alive = False action = GameActions.ATTACK target = Position(2, 2) board.has_enemies_in_range.return_value = True board.is_enemy.return_value = True enemy = mock.MagicMock() enemy.is_alive = True enemy.name = "Foo" board.get.return_value = enemy msgs = [ f"Enemy {enemy.name} is retaliating against the hero!", "You died, game over!", ] expected_msgs = [f"[{game.__class__.__name__}] {msg}" for msg in msgs] result = game.do(action, target=target) out, _ = capsys.readouterr() assert result == True, "game.do should return True when attacking but not killing an enemy" assert game.status == GameStatus.GAME_OVER, "Game status should be GAME_OVER when the hero is killed" hero.attack.assert_called_once_with(enemy) enemy.attack.assert_called_once_with(hero) for expected_msg in expected_msgs: assert expected_msg.lower() in out.lower()
def test_is_enemy_with_game_element(board, game_element): position = Position(0, 0) board.place(game_element, position) is_enemy = board.is_enemy(position) assert is_enemy == False, f"Game element at {position} is not an enemy"
def test_clear(board, unit): position = Position(1, 1) board.place(unit, destination=position) result = board.clear(position=position) assert result == True, f"board.clear({position}) should return True"
def test_create(hero, board): width = board.width height = board.height board.place(hero, Position(0, 0)) num_enemies = 3 min_level = 1 max_level = 5 # We'll be creating a board of the same width and height, so initially the empty slots will be the same empty_slots = board.empty_slots expected_enemy_positions = [] enemies = [] def create_enemy(level): enemy = mock.MagicMock(spec=Enemy) enemy.name = f"Enemy {len(enemies) + 1}" # Add the expected position and enemy to the lists, so that we can check later expected_enemy_positions.append(empty_slots.pop(0)) enemies.append(enemy) return enemy with mock.patch("questing.Enemy.random_enemy", side_effect=create_enemy) as rand_enemy: # For every enemy, there are two calls to np.random.randint # The first one is to determine the empty slot index, while # the second one is to determine the level of the enemy created. with mock.patch("numpy.random.randint", side_effect=[0, 1, 0, 2, 0, 3]): generated_board = Board.create(hero=hero, width=width, height=height, num_enemies=num_enemies, min_level=min_level, max_level=max_level) assert generated_board.width == width, f"Generated board should have a width of {width}" assert generated_board.height == height, f"Generated board should have a height of {height}" assert generated_board.get(Position( 0, 0)) == hero, f"Hero should be placed in position (0, 0) in the board" rand_enemy.assert_has_calls([mock.call(1), mock.call(2), mock.call(3)]) for idx, pos in enumerate(expected_enemy_positions): assert generated_board.is_enemy( pos), f"There should be an enemy at position {pos}" assert generated_board.get(pos) == enemies[ idx], f"Enemy at position {pos} should be {enemies[idx].name}"
def test_position_can_be_compared_to_tuple(): x = 1 y = 5 expected = (x, y) pos = Position(x=x, y=y) assert pos == expected, f"Position {pos} should be equal to tuple {expected}"
def test_position_is_set_on_all_units(army_units): pos = Position(3, 5) army = Army(units=army_units, position=pos) assert army.position == pos, f"Army position should be {pos}, but it was {army.position}" for u in army_units: assert u.position == pos, f"Position for unit {u.name} should be {pos}, but it was {u.position}"
def test_getitem(board): element = mock.MagicMock() position = Position(0, 0) board.place(element, destination=position) assert board[ position] == element, f"board[{position}] did not contain the expected element"
def test_available_actions_with_move_up_not_valid(game, board, hero): up_position = Position(0, 1) board.is_valid.return_value = False available_actions = game.available_actions() assert GameActions.MOVE_UP not in available_actions, f"GameActions.MOVE_UP should be in the available actions" board.is_valid.assert_any_call(up_position)
def test_get(board): element = mock.MagicMock() position = Position(0, 0) board.place(element, destination=position) assert board.get( position ) == element, f"board.get({position}) did not contain the expected element"
def test_move_with_0_speed(capsys, unit): unit.speed = 0 expected_msg = "My speed is 0, I can't move!" result = unit.move(destination=Position(1, 1)) out, _ = capsys.readouterr() assert result == False, "unit.move should be False if the unit can't move" assert expected_msg in out
def test_move_to_invalid_position(capsys, board, unit): destination = Position(board.width + 1, 0) expected_msg = f"Target position {destination} is not valid!" result = board.move(unit, destination=destination) out, _ = capsys.readouterr() assert result == False, f"Moving to invalid position {destination} should return false" assert expected_msg in out
def test_place_at_invalid_position(capsys, board, unit): destination = Position(board.width + 1, 0) expected_msg = f"Target position {destination} is not valid!" result = board.place(unit, destination=destination) out, _ = capsys.readouterr() assert result == False, f"Placing an element at an invalid position should return false" assert expected_msg in out
def test_clear_invalid_position(capsys, board): position = Position(10, 10) expected_msg = f"Can't clear position {position}, it's not valid!" result = board.clear(position=position) out, _ = capsys.readouterr() assert result == False, f"board.clear with invalid position {position} should return False" assert expected_msg in out
def test_move_when_in_range(capsys, unit): destination = Position(0, 1) expected_msg = f"Moving to new position ({destination})" result = unit.move(destination) out, _ = capsys.readouterr() assert result == True, "Result should be true when unit can move to the position" assert unit.position == destination, f"New position after moving should be {destination}" assert expected_msg in out
def test_available_actions_occupied_slot(game, board): up_position = Position(0, 1) board.is_valid.return_value = True board.is_empty.return_value = False available_actions = game.available_actions() assert GameActions.MOVE_UP not in available_actions, f"GameActions.MOVE_UP should not be in the available actions" board.is_valid.assert_any_call(up_position)
def test_move_when_move_fails(capsys, board, unit): unit.move.return_value = False destination = Position(0, 0) expected_msg = f"Failed to move {unit.name} to position {destination}" result = board.move(unit, destination=destination) out, _ = capsys.readouterr() assert result == False, f"board.move should return False if the unit.move fails" assert expected_msg in out unit.move.assert_called_once_with(destination)
def _get_full_board(width, height): b = Board(width=width, height=height) for x in range(width): for y in range(height): enemy = mock.MagicMock(spec=Enemy) enemy.name = f"Enemy x={width}, y={height}" pos = Position(x, y) b.place(enemy, pos) return b
def test_place_at_non_empty_position(capsys, board, unit): other_unit = mock.MagicMock() destination = Position(1, 0) board.place(other_unit, destination) expected_msg = f"Can't place element {unit.name} at position {destination}, it's not empty!" result = board.place(unit, destination=destination) out, _ = capsys.readouterr() assert result == False, f"Placing an element in a occupied position should return false" assert expected_msg in out
def test_move_to_non_empty_position(capsys, board, unit): other_unit = mock.MagicMock() destination = Position(1, 0) board.place(other_unit, destination) expected_msg = f"Can't move {unit.name} to position {destination}, it's not empty!" result = board.move(unit, destination=destination) out, _ = capsys.readouterr() assert result == False, f"Moving to occupied position {destination} should return false" assert expected_msg in out
def test_attack_with_target_position_empty(capsys, game, hero, board): action = GameActions.ATTACK target = Position(2, 2) board.has_enemies_in_range.return_value = True board.is_enemy.return_value = False msg = f"Action {action.value} requires an enemy in the target position!" expected_msg = f"[{game.__class__.__name__}] {msg}" result = game.do(action, target=target) out, _ = capsys.readouterr() assert result == False, f"game.do should return False when attacking a non-enemy position" assert expected_msg.lower() in out.lower()
def test_move(capsys, board, unit): unit.move.return_value = True origin = unit.position destination = Position(1, 1) expected_msg = f"Moved {unit.name} from {origin} to position {destination}" result = board.move(unit, destination=destination) out, _ = capsys.readouterr() assert result == True, f"board.move should return True if the unit.move succeeds" assert board.get( origin ) == None, f"Origin position {origin} should be empty after moving" assert board.get( destination ) == unit, f"Unit should be in the destination {destination}" assert expected_msg in out unit.move.assert_called_once_with(destination)
def test_attack_enemy_killed(capsys, game, hero, board): hero.is_alive = True hero.heal = mock.MagicMock() action = GameActions.ATTACK target = Position(2, 2) board.has_enemies_in_range.return_value = True board.is_enemy.return_value = True enemy = mock.MagicMock() enemy.is_alive = False enemy.name = "Foo" board.get.return_value = enemy board.move.return_value = True result = game.do(action, target=target) assert result == True, "game.do should return True when attacking and killing an enemy" hero.attack.assert_called_once_with(enemy) hero.heal.assert_called_once() enemy.attack.assert_not_called() board.clear.assert_called_once_with(target) board.move.assert_called_once_with(unit=hero, destination=target)
def test_attack_enemy_not_killed(capsys, game, hero, board): hero.is_alive = True action = GameActions.ATTACK target = Position(2, 2) board.has_enemies_in_range.return_value = True board.is_enemy.return_value = True enemy = mock.MagicMock() enemy.is_alive = True enemy.name = "Foo" board.get.return_value = enemy msg = f"Enemy {enemy.name} is retaliating against the hero!" expected_msg = f"[{game.__class__.__name__}] {msg}" result = game.do(action, target=target) out, _ = capsys.readouterr() assert result == True, "game.do should return True when attacking but not killing an enemy" hero.attack.assert_called_once_with(enemy) enemy.attack.assert_called_once_with(hero) assert expected_msg.lower() in out.lower()