|
| 1 | +import pytest |
| 2 | +from unittest.mock import Mock |
| 3 | + |
| 4 | +from aco_routing.ant import Ant |
| 5 | +from aco_routing.graph_api import GraphApi |
| 6 | + |
| 7 | + |
| 8 | +@pytest.fixture |
| 9 | +def mock_graph_api(): |
| 10 | + graph_api = Mock(spec=GraphApi) |
| 11 | + # Set up a simple graph for testing |
| 12 | + graph_api.get_neighbors.return_value = ["B", "C"] |
| 13 | + graph_api.get_edge_pheromones.return_value = 1.0 |
| 14 | + graph_api.get_edge_cost.return_value = 1.0 |
| 15 | + return graph_api |
| 16 | + |
| 17 | + |
| 18 | +def test_ant_initialization(mock_graph_api): |
| 19 | + ant = Ant(mock_graph_api, source="A", destination="D") |
| 20 | + assert ant.source == "A" |
| 21 | + assert ant.destination == "D" |
| 22 | + assert ant.current_node == "A" |
| 23 | + assert ant.path == ["A"] |
| 24 | + assert ant.path_cost == 0.0 |
| 25 | + assert not ant.is_fit |
| 26 | + assert not ant.is_solution_ant |
| 27 | + assert len(ant.visited_nodes) == 0 |
| 28 | + |
| 29 | + |
| 30 | +def test_reached_destination(mock_graph_api): |
| 31 | + ant = Ant(mock_graph_api, source="A", destination="D") |
| 32 | + assert not ant.reached_destination() |
| 33 | + ant.current_node = "D" |
| 34 | + assert ant.reached_destination() |
| 35 | + |
| 36 | + |
| 37 | +def test_get_unvisited_neighbors(mock_graph_api): |
| 38 | + ant = Ant(mock_graph_api, source="A", destination="D") |
| 39 | + unvisited = ant._get_unvisited_neighbors() |
| 40 | + assert set(unvisited) == {"B", "C"} |
| 41 | + |
| 42 | + # Test with some visited nodes |
| 43 | + ant.visited_nodes.add("B") |
| 44 | + unvisited = ant._get_unvisited_neighbors() |
| 45 | + assert unvisited == ["C"] |
| 46 | + |
| 47 | + |
| 48 | +def test_take_step_normal_case(mock_graph_api): |
| 49 | + ant = Ant(mock_graph_api, source="A", destination="D") |
| 50 | + ant.take_step() |
| 51 | + assert ant.current_node in ["B", "C"] # Should move to one of the neighbors |
| 52 | + assert len(ant.path) == 2 # Should have two nodes in path [A, (B or C)] |
| 53 | + assert ant.path_cost == 1.0 # With our mock returning cost=1.0 |
| 54 | + assert len(ant.visited_nodes) == 1 # Should have marked A as visited |
| 55 | + |
| 56 | + |
| 57 | +def test_solution_ant_behavior(mock_graph_api): |
| 58 | + ant = Ant(mock_graph_api, source="A", destination="D", is_solution_ant=True) |
| 59 | + # Mock edge pheromones to make B more desirable |
| 60 | + mock_graph_api.get_edge_pheromones.side_effect = lambda src, dst: ( |
| 61 | + 2.0 if dst == "B" else 1.0 |
| 62 | + ) |
| 63 | + |
| 64 | + ant.take_step() |
| 65 | + assert ant.current_node == "B" # Should always choose B as it has higher pheromone |
| 66 | + assert ant.path == ["A", "B"] |
| 67 | + |
| 68 | + |
| 69 | +def test_deposit_pheromones(mock_graph_api): |
| 70 | + ant = Ant(mock_graph_api, source="A", destination="D") |
| 71 | + ant.path = ["A", "B", "C"] # Manually set path for testing |
| 72 | + ant.path_cost = 2.0 |
| 73 | + |
| 74 | + ant.deposit_pheromones_on_path() |
| 75 | + |
| 76 | + # Should deposit pheromones on A->B and B->C |
| 77 | + mock_graph_api.deposit_pheromones.assert_any_call("A", "B", 0.5) |
| 78 | + mock_graph_api.deposit_pheromones.assert_any_call("B", "C", 0.5) |
| 79 | + assert mock_graph_api.deposit_pheromones.call_count == 2 |
0 commit comments