def setup(self):
     self._game_cycle_control_service = GameCycleControlService(self._state_factory, self._world_map_service_mock_instance, self._pathfinding_service_mock_instance, self._motion_service_mock_instance,
                                                                self._secret_code_decoding_service_mock_instance, self._treasure_handling_service_mock_instance, self._hardware_status_collector_service_mock_instance, self._local_world_map_service_mock_instance)
class TestGameCycleControlService(object):

    SAMPLE_PATH_TO_CHARGING_STATION = object()
    SAMPLE_PATH_TO_TREASURE = object()
    SAMPLE_PATH_TO_ISLAND = object()
    SAMPLE_LOW_CAPACITOR_ENERGY = 1
    SAMPLE_HIGH_CAPACITOR_ENERGY = 6
    SAMPLE_LOCATION = Vector2(7, 5)
    SAMPLE_LABEL = "label"

    CYCLE_COMPLETION_CHECK_WAIT_TIME = 0.1
    CYCLE_COMPLETION_TIMEOUT = 10

    @classmethod
    @mock.patch('domain.coordinate.CoordinateFactory.CoordinateFactory', autospec = False)
    @mock.patch('domain.world_objects.island.Island.Island', autospec = False)
    @mock.patch('services.virtual_mapping.LocalWorldMapService.LocalWorldMapService', autospec = False)
    @mock.patch('services.hardware_status.HardwareStatusCollectorService.HardwareStatusCollectorService', autospec = False)
    @mock.patch('services.treasure_handling.TreasureHandlingService.TreasureHandlingService', autospec = False)
    @mock.patch('services.secret_code_decoding.SecretCodeDecodingService.SecretCodeDecodingService', autospec = False)
    @mock.patch('services.motion.MotionService.MotionService', autospec = False)
    @mock.patch('services.pathfinding.PathfindingService.PathfindingService', autospec = False)
    @mock.patch('services.virtual_mapping.WorldMapService.WorldMapService', autospec = False)
    def setup_class(cls, world_map_service_mock, pathfinding_service_mock, motion_service_mock, secret_code_decoding_service_mock, 
                    treasure_handling_service_mock, hardware_status_collector_service_mock, local_world_map_service_mock, island_mock, coordinate_factory_mock):
        cls._island_mock_instance = island_mock.return_value
        cls._island_mock_instance.get_center.return_value = cls.SAMPLE_LOCATION
        cls._coordinate_factory_instance = coordinate_factory_mock.return_value
        cls._coordinate_factory_instance.get.return_value = cls.SAMPLE_LOCATION
        cls._world_map_service_mock_instance = world_map_service_mock.return_value
        cls._world_map_service_mock_instance.wait_for_detection.return_value = True
        cls._pathfinding_service_mock_instance = pathfinding_service_mock.return_value
        cls._pathfinding_service_mock_instance.find_path_to_charging_station.return_value = cls.SAMPLE_PATH_TO_CHARGING_STATION, cls.SAMPLE_LOCATION
        cls._sample_treasure = Treasure(cls._coordinate_factory_instance, cls.SAMPLE_LABEL)
        cls._pathfinding_service_mock_instance.find_path_to_treasure.return_value = cls.SAMPLE_PATH_TO_TREASURE, cls._sample_treasure
        cls._pathfinding_service_mock_instance.find_path_to_island.return_value = cls.SAMPLE_PATH_TO_ISLAND, cls._island_mock_instance
        cls._motion_service_mock_instance = motion_service_mock.return_value
        cls._secret_code_decoding_service_mock_instance = secret_code_decoding_service_mock.return_value
        cls._secret_code_decoding_service_mock_instance.get_target_island.return_value = IslandDescriptor(IslandType.ANY, IslandColor.RED)
        cls._treasure_handling_service_mock_instance = treasure_handling_service_mock.return_value
        cls._hardware_status_collector_service_mock_instance = hardware_status_collector_service_mock.return_value
        cls._hardware_status_collector_service_mock_instance.get_remaining_capacitor_energy.side_effect = [CapacitorChargingState.MIN_CAPACITOR_CHARGE_JOULES, CapacitorChargingState.TARGET_CAPACITOR_CHARGE_JOULES]
        cls._local_world_map_service_mock_instance = local_world_map_service_mock.return_value
        cls._local_world_map_service_mock_instance.wait_for_detection.return_value = True
        cls._setup_state_factory(cls)

    def _setup_state_factory(cls):
        cls._state_factory = StateFactory()
        cls._state_factory.inject_dependency(cls._world_map_service_mock_instance, WorldMapService.__name__)
        cls._state_factory.inject_dependency(cls._pathfinding_service_mock_instance, PathfindingService.__name__)
        cls._state_factory.inject_dependency(cls._motion_service_mock_instance, MotionService.__name__)
        cls._state_factory.inject_dependency(cls._secret_code_decoding_service_mock_instance, SecretCodeDecodingService.__name__)
        cls._state_factory.inject_dependency(cls._treasure_handling_service_mock_instance, TreasureHandlingService.__name__)
        cls._state_factory.inject_dependency(cls._hardware_status_collector_service_mock_instance, HardwareStatusCollectorService.__name__)
        cls._state_factory.inject_dependency(cls._local_world_map_service_mock_instance, LocalWorldMapService.__name__)

    def setup(self):
        self._game_cycle_control_service = GameCycleControlService(self._state_factory, self._world_map_service_mock_instance, self._pathfinding_service_mock_instance, self._motion_service_mock_instance,
                                                                   self._secret_code_decoding_service_mock_instance, self._treasure_handling_service_mock_instance, self._hardware_status_collector_service_mock_instance, self._local_world_map_service_mock_instance)

    def test_when_starting_cycle_then_cycle_is_completed_correctly(self):
        self._game_cycle_control_service.start_cycle()
        self._wait_for_cycle_completion()
        self._assert_cycle_calls()

    def _wait_for_cycle_completion(self):
        self._thread = Thread(target = self._cycle_completion_check_thread, daemon = True)
        self._thread.start()
        self._thread.join(self.CYCLE_COMPLETION_TIMEOUT)
        if self._thread.is_alive():
            assert False, "Game cycle completion timeout reached."

    def _cycle_completion_check_thread(self):
        while not isinstance(self._game_cycle_control_service.state, GameCycleCompletedState):
            time.sleep(self.CYCLE_COMPLETION_CHECK_WAIT_TIME)

    def _assert_cycle_calls(self):
        self._world_map_service_mock_instance.assert_has_calls(self._world_map_service_mock_instance.wait_for_detection)
        self._hardware_status_collector_service_mock_instance.get_remaining_capacitor_energy.assert_called_with()
        self._local_world_map_service_mock_instance.assert_has_calls(self._local_world_map_service_mock_instance.wait_for_detection)
        self._world_map_service_mock_instance.remove_treasure.assert_called_once_with(self._sample_treasure)
        self._treasure_handling_service_mock_instance.assert_has_calls(self._treasure_handling_service_mock_instance.treasure_pickup)
        self._treasure_handling_service_mock_instance.treasure_drop.assert_called_once_with()
        self._motion_service_mock_instance.navigate_path.assert_any_call(self.SAMPLE_PATH_TO_CHARGING_STATION, self.SAMPLE_LOCATION, RobotSide.LEFT, True)
        self._motion_service_mock_instance.navigate_path.assert_any_call(self.SAMPLE_PATH_TO_TREASURE, self.SAMPLE_LOCATION, RobotSide.BACK, True)
        self._motion_service_mock_instance.navigate_path.assert_any_call(self.SAMPLE_PATH_TO_ISLAND, self.SAMPLE_LOCATION, RobotSide.BACK, False)