Exemplo n.º 1
0
class Communicator(Singleton):
    '''Interface between listeners and the application.'''

    def _initialize(self):
        self._database = MemcachedDatabase()
        self._action_manager = ActionManager()
        self._population_control = PopulationControl()
        self._admin_handler = AdminHandler()

    def execute_command(self, password, command, args):
        '''Execute client's command.'''

        try:
            if command in ["born", "give_birth"]:
                result = self._population_control.execute_command(password, command, args)
            elif command == "map_data":
                result = self._admin_handler.execute_command(password, command, args)
            else:
                result = self._action_manager.do_action(password, command, args)

            # Committing or rollbacking all changes after the completion of execution.
            self._database.commit()
            return result

        except Exception:
            self._database.rollback()
            raise
    def test_for_update(self):
        '''Tests of for_update flag works correctly.'''
        database = MemcachedDatabase()

        robot_id = "test_for_update_18762"
        robot = Robot(robot_id, "123")
        database.add_robot(robot, (10, 0))
        database.commit()

        new_robot = database.get_robot(robot_id, for_update=True)

        # Testing lock
        with self.assertRaises(LockAlreadyAquiredError):
            database.get_robot(robot_id, for_update=True)

        # Testing commit.
        new_robot.set_alive(False)

        # It shouldn't be changed yet.
        new_robot_2 = database.get_robot(robot_id)
        self.assertNotEqual(new_robot.get_alive(), new_robot_2.get_alive())

        # Actually committing changes.
        database.commit()
        new_robot_2 = database.get_robot(robot_id)
        self.assertEqual(new_robot.get_alive(), new_robot_2.get_alive())

        # Lock should be freed.
        database.get_robot(robot_id, for_update=True)
        database.rollback()
Exemplo n.º 3
0
    def test_for_update(self):
        '''Tests for_update flag of get_square method.'''
        database = MemcachedDatabase()

        square = database.get_square((6, 1), for_update=True)

        # Testing the lock.
        with self.assertRaises(LockAlreadyAquiredError):
            database.get_square((6, 1), for_update=True)

        # Testing commit.
        square.set_robot_id("ujhqi981762yhdg67")

        # It shouldn't be changed yet.
        new_square = database.get_square((6, 1))
        self.assertNotEqual(square.get_robot_id(), new_square.get_robot_id())

        # Committing changes.
        database.commit()
        new_square = database.get_square((6, 1))
        self.assertEqual(square.get_robot_id(), new_square.get_robot_id())

        # Lock should be freed.
        new_square = database.get_square((6, 1), for_update=True)
        database.rollback()
    def test_for_update(self):
        '''Tests of for_update flag works correctly.'''
        database = MemcachedDatabase()

        robot_id = "test_for_update_18762"
        robot = Robot(robot_id, "123")
        database.add_robot(robot, (10, 0))
        database.commit()

        new_robot = database.get_robot(robot_id, for_update=True)

        # Testing lock
        with self.assertRaises(LockAlreadyAquiredError):
            database.get_robot(robot_id, for_update=True)

        # Testing commit.
        new_robot.set_alive(False)

        # It shouldn't be changed yet.
        new_robot_2 = database.get_robot(robot_id)
        self.assertNotEqual(new_robot.get_alive(), new_robot_2.get_alive())

        # Actually committing changes.
        database.commit()
        new_robot_2 = database.get_robot(robot_id)
        self.assertEqual(new_robot.get_alive(), new_robot_2.get_alive())

        # Lock should be freed.
        database.get_robot(robot_id, for_update=True)
        database.rollback()
    def test_no_plant(self):
        '''Tests when there's no plant to eat.'''
        action_manager = ActionManager()
        database = MemcachedDatabase()

        with self.assertRaises(NoPlantToEat):
            action_manager.do_action("123", "eat", [TestEatAction.ROBOT_ID])
        database.rollback()
    def test_no_plant(self):
        '''Tests when there's no plant to eat.'''
        action_manager = ActionManager()
        database = MemcachedDatabase()

        with self.assertRaises(NoPlantToEat):
            action_manager.do_action("123", "eat", [TestEatAction.ROBOT_ID])
        database.rollback()
    def test_locked(self):
        '''Tests with a locked square.'''
        action_manager = ActionManager()
        database = MemcachedDatabase()

        database.get_square(TestEatAction.LOCATION, for_update=True)

        with self.assertRaises(LockAlreadyAquiredError):
            action_manager.do_action("123", "eat", [TestEatAction.ROBOT_ID])
        database.rollback()
    def test_locked_robot(self):
        '''Tests with a locked robot.'''
        population_control = PopulationControl()
        database = MemcachedDatabase()
        database.get_robot(TestGiveBirth.ROBOT_ID, for_update=True)

        with self.assertRaises(LockAlreadyAquiredError):
            population_control.execute_command("123", "give_birth", [TestGiveBirth.ROBOT_ID])

        database.rollback()
    def test_locked(self):
        '''Tests with a locked square.'''
        action_manager = ActionManager()
        database = MemcachedDatabase()

        database.get_square(TestEatAction.LOCATION, for_update=True)

        with self.assertRaises(LockAlreadyAquiredError):
            action_manager.do_action("123", "eat", [TestEatAction.ROBOT_ID])
        database.rollback()
Exemplo n.º 10
0
    def test_locked_robot(self):
        '''Tests with a locked robot.'''
        population_control = PopulationControl()
        database = MemcachedDatabase()
        database.get_robot(TestGiveBirth.ROBOT_ID, for_update=True)

        with self.assertRaises(LockAlreadyAquiredError):
            population_control.execute_command("123", "give_birth",
                                               [TestGiveBirth.ROBOT_ID])

        database.rollback()
Exemplo n.º 11
0
    def test_bad_argument(self):
        '''Tests when sending bad arguments to the action.'''
        action_manager = ActionManager()
        database = MemcachedDatabase()

        with self.assertRaises(InvalidArgumentsError):
            action_manager.do_action("123", "eat", [TestEatAction.ROBOT_ID, None])
        database.rollback()

        with self.assertRaises(InvalidArgumentsError):
            action_manager.do_action("123", "eat", [TestEatAction.ROBOT_ID, "", 9])
        database.rollback()
Exemplo n.º 12
0
    def test_rollback(self):
        '''Tests if calling rollback works correctly.'''
        database = MemcachedDatabase()

        new_robot = Robot("test_rollback_87162", "123")
        database.add_robot(new_robot, (1, 1))

        database.rollback()
        database.commit()

        with self.assertRaises(RobotNotFoundError):
            database.get_robot("test_rollback_87162")
Exemplo n.º 13
0
    def test_rollback(self):
        '''Tests if database rolls back the changes correctly.'''
        database = MemcachedDatabase()

        square = database.get_square((6, 2), for_update=True)
        square.set_robot_id("iuwuyehdmn990198283")

        database.rollback()
        database.commit()

        new_square = database.get_square((6, 2))
        self.assertNotEqual(square.get_robot_id(), new_square.get_robot_id())
Exemplo n.º 14
0
    def test_with_parent(self):
        '''Tests borning a robot with a parent.'''
        population_control = PopulationControl()
        database = MemcachedDatabase()

        database.add_password("oijdnnh76153WEd")
        robot = Robot("test_with_parent_18873", "123")
        database.add_robot(robot, (14, 1))
        database.commit()

        population_control.execute_command("oijdnnh76153WEd", "born", ["test_with_parent_18873", "My Child"])

        database.rollback()
Exemplo n.º 15
0
    def test_with_parent(self):
        '''Tests borning a robot with a parent.'''
        population_control = PopulationControl()
        database = MemcachedDatabase()

        database.add_password("oijdnnh76153WEd")
        robot = Robot("test_with_parent_18873", "123")
        database.add_robot(robot, (14, 1))
        database.commit()

        population_control.execute_command(
            "oijdnnh76153WEd", "born", ["test_with_parent_18873", "My Child"])

        database.rollback()
Exemplo n.º 16
0
    def test_bad_name(self):
        '''Tries to born a robot with an invalid name. Should be fail.'''
        population_control = PopulationControl()
        database = MemcachedDatabase()

        database.add_password("OIkdj981HJDJHcnm_1")
        database.add_password("OIkdj981HJDJHcnm_2")
        database.add_password("OIkdj981HJDJHcnm_3")
        database.add_password("OIkdj981HJDJHcnm_4")
        database.commit()

        long_name = "n" * (MAX_ROBOT_NAME + 1)
        with self.assertRaises(LongRobotNameError):
            population_control.execute_command("OIkdj981HJDJHcnm_1", "born", [None, long_name])
        database.rollback()

        with self.assertRaises(LongRobotNameError):
            population_control.execute_command("OIkdj981HJDJHcnm_2", "born", [None, None])
        database.rollback()

        with self.assertRaises(LongRobotNameError):
            population_control.execute_command("OIkdj981HJDJHcnm_3", "born", [None, b"some bytes"])
        database.rollback()

        with self.assertRaises(LongRobotNameError):
            population_control.execute_command("OIkdj981HJDJHcnm_4", "born", [None, database])
        database.rollback()
Exemplo n.º 17
0
    def test_not_enough_honor(self):
        '''Tests a robot with few honors, trying to give birth.'''
        population_control = PopulationControl()
        database = MemcachedDatabase()
        configs = Configs()

        robot = database.get_robot(TestGiveBirth.ROBOT_ID, for_update=True)
        robot.set_honor(configs.get_robots_birth_required_honor() - 1)
        database.commit()

        with self.assertRaises(NotEnoughHonorError):
            population_control.execute_command("123", "give_birth", [TestGiveBirth.ROBOT_ID])

        database.rollback()
    def test_bad_direction(self):
        '''Sends an invalid direction as arguments.'''
        robot_id = "test_bad_direction_18445"
        robot = Robot(robot_id, "123")
        world = World()
        action_manager = ActionManager()
        database = MemcachedDatabase()

        world.add_robot(robot, (12, 6))
        database.commit()

        with self.assertRaises(InvalidArgumentsError):
            action_manager.do_action("123", "move", [robot_id, "U"])

        database.rollback()

        with self.assertRaises(InvalidArgumentsError):
            action_manager.do_action("123", "move", [robot_id, 988])

        database.rollback()

        with self.assertRaises(InvalidArgumentsError):
            action_manager.do_action("123", "move", [robot_id, None])

        database.rollback()

        with self.assertRaises(InvalidArgumentsError):
            action_manager.do_action("123", "move", [robot_id])

        database.rollback()
Exemplo n.º 19
0
    def test_bad_name(self):
        '''Tries to born a robot with an invalid name. Should be fail.'''
        population_control = PopulationControl()
        database = MemcachedDatabase()

        database.add_password("OIkdj981HJDJHcnm_1")
        database.add_password("OIkdj981HJDJHcnm_2")
        database.add_password("OIkdj981HJDJHcnm_3")
        database.add_password("OIkdj981HJDJHcnm_4")
        database.commit()

        long_name = "n" * (MAX_ROBOT_NAME + 1)
        with self.assertRaises(LongRobotNameError):
            population_control.execute_command("OIkdj981HJDJHcnm_1", "born",
                                               [None, long_name])
        database.rollback()

        with self.assertRaises(LongRobotNameError):
            population_control.execute_command("OIkdj981HJDJHcnm_2", "born",
                                               [None, None])
        database.rollback()

        with self.assertRaises(LongRobotNameError):
            population_control.execute_command("OIkdj981HJDJHcnm_3", "born",
                                               [None, b"some bytes"])
        database.rollback()

        with self.assertRaises(LongRobotNameError):
            population_control.execute_command("OIkdj981HJDJHcnm_4", "born",
                                               [None, database])
        database.rollback()
Exemplo n.º 20
0
    def test_bad_argument(self):
        '''Tests when sending bad arguments to the action.'''
        action_manager = ActionManager()
        database = MemcachedDatabase()

        with self.assertRaises(InvalidArgumentsError):
            action_manager.do_action("123", "eat",
                                     [TestEatAction.ROBOT_ID, None])
        database.rollback()

        with self.assertRaises(InvalidArgumentsError):
            action_manager.do_action("123", "eat",
                                     [TestEatAction.ROBOT_ID, "", 9])
        database.rollback()
Exemplo n.º 21
0
    def test_not_enough_honor(self):
        '''Tests a robot with few honors, trying to give birth.'''
        population_control = PopulationControl()
        database = MemcachedDatabase()
        configs = Configs()

        robot = database.get_robot(TestGiveBirth.ROBOT_ID, for_update=True)
        robot.set_honor(configs.get_robots_birth_required_honor() - 1)
        database.commit()

        with self.assertRaises(NotEnoughHonorError):
            population_control.execute_command("123", "give_birth",
                                               [TestGiveBirth.ROBOT_ID])

        database.rollback()
    def test_moving_outside(self):
        '''Tests moving a robot to outside of the world.'''
        robot_id = "test_moving_outside_981165"
        robot = Robot(robot_id, "123")
        world = World()
        action_manager = ActionManager()
        database = MemcachedDatabase()

        world.add_robot(robot, (14, 2))
        database.commit()

        with self.assertRaises(InvalidLocationError):
            action_manager.do_action("123", "move", [robot_id, "E"])

        database.rollback()
    def test_bad_arguments(self):
        '''Calls the action with invalid arguments.'''
        action = InfoAction()
        database = MemcachedDatabase()
        robot = Robot("test_info_action_1293", "123")

        with self.assertRaises(InvalidArgumentsError):
            action.do_action(robot, [robot.get_id(), None])
        database.rollback()

        with self.assertRaises(InvalidArgumentsError):
            action.do_action(robot, [robot.get_id(), "", "09"])
        database.rollback()

        with self.assertRaises(InvalidArgumentsError):
            action.do_action(robot, [robot.get_id(), []])
Exemplo n.º 24
0
    def test_bad_arguments(self):
        '''Calls the action with invalid arguments.'''
        action = InfoAction()
        database = MemcachedDatabase()
        robot = Robot("test_info_action_1293", "123")

        with self.assertRaises(InvalidArgumentsError):
            action.do_action(robot, [robot.get_id(), None])
        database.rollback()

        with self.assertRaises(InvalidArgumentsError):
            action.do_action(robot, [robot.get_id(), "", "09"])
        database.rollback()

        with self.assertRaises(InvalidArgumentsError):
            action.do_action(robot, [robot.get_id(), []])
Exemplo n.º 25
0
    def test_locked_square(self):
        '''Tests with a already-locked square.'''
        database = MemcachedDatabase()
        robot = Robot("oi981872yuweu.9887", "123")
        robot.set_location((5, 0))
        robot.set_has_water(True)

        database.get_square((5, 0), for_update=True)

        action = WaterAction()

        with self.assertRaises(LockAlreadyAquiredError):
            action.do_action(robot, ["oi981872yuweu.9887"])

        # Freeing lock.
        database.rollback()
    def test_locked_square(self):
        '''Tests with a already-locked square.'''
        database = MemcachedDatabase()
        robot = Robot("oi981872yuweu.9887", "123")
        robot.set_location((5, 0))
        robot.set_has_water(True)

        database.get_square((5, 0), for_update=True)

        action = WaterAction()

        with self.assertRaises(LockAlreadyAquiredError):
            action.do_action(robot, ["oi981872yuweu.9887"])

        # Freeing lock.
        database.rollback()
    def test_lock(self):
        '''Tests if location is locked.'''
        robot_id = "test_move_lock_76120"
        robot = Robot(robot_id, "123")
        world = World()
        action_manager = ActionManager()
        database = MemcachedDatabase()

        world.add_robot(robot, (13, 6))
        database.commit()

        database.get_square((13, 7), for_update=True)

        with self.assertRaises(LockAlreadyAquiredError):
            action_manager.do_action("123", "move", [robot_id, "S"])

        database.rollback()
    def test_no_plant_square(self):
        '''Tests watering a square without any plant.'''
        database = MemcachedDatabase()

        robot = Robot("098kk.ski87.99", "123")
        robot.set_location((6, 0))
        robot.set_has_water(True)

        action = WaterAction()

        action.do_action(robot, ["098kk.ski87.99"])

        self.assertFalse(robot.get_has_water())
        # Honor shouldn't increase because this robot didn't really watered a plant.
        self.assertEqual(robot.get_honor(), 0)

        database.rollback()
Exemplo n.º 29
0
    def test_rollback(self):
        '''Tests if changes rolls back correctly.'''
        database = MemcachedDatabase()

        robot_id = "test_rollback_18098"
        robot = Robot(robot_id, "123")
        database.add_robot(robot, (11, 0))
        database.commit()

        new_robot = database.get_robot(robot_id, for_update=True)
        new_robot.set_alive(False)

        database.rollback()
        database.commit()

        new_robot_2 = database.get_robot(robot_id)
        self.assertNotEqual(new_robot.get_alive(), new_robot_2.get_alive())
Exemplo n.º 30
0
    def test_rollback(self):
        '''Tests if changes rolls back correctly.'''
        database = MemcachedDatabase()

        robot_id = "test_rollback_18098"
        robot = Robot(robot_id, "123")
        database.add_robot(robot, (11, 0))
        database.commit()

        new_robot = database.get_robot(robot_id, for_update=True)
        new_robot.set_alive(False)

        database.rollback()
        database.commit()

        new_robot_2 = database.get_robot(robot_id)
        self.assertNotEqual(new_robot.get_alive(), new_robot_2.get_alive())
Exemplo n.º 31
0
    def test_no_plant_square(self):
        '''Tests watering a square without any plant.'''
        database = MemcachedDatabase()

        robot = Robot("098kk.ski87.99", "123")
        robot.set_location((6, 0))
        robot.set_has_water(True)

        action = WaterAction()

        action.do_action(robot, ["098kk.ski87.99"])

        self.assertFalse(robot.get_has_water())
        # Honor shouldn't increase because this robot didn't really watered a plant.
        self.assertEqual(robot.get_honor(), 0)

        database.rollback()
Exemplo n.º 32
0
 def tearDown(cls):
     database = MemcachedDatabase()
     database.rollback()
class TestActionManager(unittest.TestCase):
    def __init__(self, *args, **kwargs):
        super(TestActionManager, self).__init__(*args, **kwargs)

        self._action_manager = ActionManager()
        self._database = MemcachedDatabase()

    def tearDown(self):
        # Rolling back any remaining thing.
        self._database.rollback()

    def test_not_exist_robot(self):
        '''Test if ActionManager handles not-existed robot.'''
        with self.assertRaises(RobotNotFoundError):
            self._action_manager.do_action("123", "move",
                                           ["not_existed_robot_id"])

    def test_bad_robot_id(self):
        '''Test invalid robot IDs.'''
        # Note that listeners checks if `args' is a list, so action manager won't receive another type.

        with self.assertRaises(actions.exceptions.InvalidArgumentsError):
            self._action_manager.do_action("123", "move", [])

        with self.assertRaises(actions.exceptions.InvalidArgumentsError):
            self._action_manager.do_action("123", "move", [None])

    def test_invalid_password(self):
        '''Test if ActionManager authenticate passwords correctly.'''
        robot = Robot("test_invalid_password_95312", "andhue-ifue876-fkdnpw-1")
        self._database.add_robot(robot, (3, 1))
        self._database.commit()

        with self.assertRaises(AuthenticationFailedError):
            self._action_manager.do_action("ieukjdf-ioquiwe-751io", "status",
                                           ["test_invalid_password_95312"])

    def test_dead_robot(self):
        '''Test if ActionManager checks a dead robot.'''
        robot = Robot("test_dead_robot_98176", "1234")
        robot.set_alive(False)
        self._database.add_robot(robot, (3, 2))
        self._database.commit()

        with self.assertRaises(AuthenticationFailedError):
            self._action_manager.do_action("1234", "status",
                                           ["test_dead_robot_98176"])

    def test_bad_actions(self):
        '''Test wrong action IDs.'''
        robot = Robot("test_bad_actions_2376", "123")
        self._database.add_robot(robot, (4, 1))
        self._database.commit()

        with self.assertRaises(actions.exceptions.InvalidActionError):
            self._action_manager.do_action("123", "not-exist-action",
                                           ["test_bad_actions_2376"])

        self._database.rollback()
        with self.assertRaises(actions.exceptions.InvalidActionError):
            self._action_manager.do_action("123", 5432,
                                           ["test_bad_actions_2376"])

        self._database.rollback()
        with self.assertRaises(actions.exceptions.InvalidActionError):
            self._action_manager.do_action("123", None,
                                           ["test_bad_actions_2376"])

        self._database.rollback()
        with self.assertRaises(actions.exceptions.InvalidActionError):
            self._action_manager.do_action("123", "",
                                           ["test_bad_actions_2376"])

    def test_ok(self):
        '''Execute a fine action.'''
        robot = Robot("test_ok_action_3278", "4467yrt-ddfjh-1u872-oiie")
        self._database.add_robot(robot, (3, 3))
        self._database.commit()

        initial_energy = robot.get_energy()
        initial_age = robot.get_life()

        result = self._action_manager.do_action("4467yrt-ddfjh-1u872-oiie",
                                                "status",
                                                ["test_ok_action_3278"])

        self.assertEqual(result['alive'], True)

        # Robot should lost energy and age.
        self._database.commit()
        robot = self._database.get_robot("test_ok_action_3278")
        self.assertEqual(robot.get_energy(), initial_energy - 1)
        self.assertEqual(robot.get_life(), initial_age - 1)

    def test_losing_energy_on_error(self):
        '''Tests if ActionManager reduces energy and age after an exception.'''
        robot = Robot("test_losing_energy_on_error_981", "091oikjdmncj")
        self._database.add_robot(robot, (5, 3))
        self._database.commit()

        initial_energy = robot.get_energy()
        initial_age = robot.get_life()

        with self.assertRaises(actions.exceptions.InvalidActionError):
            self._action_manager.do_action("091oikjdmncj", "invalid_action",
                                           ["test_losing_energy_on_error_981"])

        self._database.commit()
        robot = self._database.get_robot("test_losing_energy_on_error_981")
        self.assertEqual(robot.get_energy(), initial_energy - 1)
        self.assertEqual(robot.get_life(), initial_age - 1)

        # Robot shouldn't lose energy on authentication error.
        with self.assertRaises(AuthenticationFailedError):
            self._action_manager.do_action("wrong pass", "invalid_action",
                                           ["test_losing_energy_on_error_981"])

        self._database.rollback()
        robot = self._database.get_robot("test_losing_energy_on_error_981")
        self.assertEqual(robot.get_energy(), initial_energy - 1)
        self.assertEqual(robot.get_life(), initial_age - 1)

    def test_delay(self):
        '''Tests delay between robot actions.'''
        robot = Robot("test_delay_1223", "09112345")
        self._database.add_robot(robot, (6, 3))
        self._database.commit()

        self._action_manager.do_action("09112345", "sense", [robot.get_id()])
        self._database.commit()

        start_time = time.time()
        self._action_manager.do_action("09112345", "sense", [robot.get_id()])
        elapsed_time = time.time() - start_time
        self._database.commit()

        # one millisecond reduced from delay to cover error.
        self.assertGreater(elapsed_time, 0.029)
class TestActionManager(unittest.TestCase):

    def __init__(self, *args, **kwargs):
        super(TestActionManager, self).__init__(*args, **kwargs)

        self._action_manager = ActionManager()
        self._database = MemcachedDatabase()

    def tearDown(self):
        # Rolling back any remaining thing.
        self._database.rollback()

    def test_not_exist_robot(self):
        '''Test if ActionManager handles not-existed robot.'''
        with self.assertRaises(RobotNotFoundError):
            self._action_manager.do_action("123", "move", ["not_existed_robot_id"])

    def test_bad_robot_id(self):
        '''Test invalid robot IDs.'''
        # Note that listeners checks if `args' is a list, so action manager won't receive another type.

        with self.assertRaises(actions.exceptions.InvalidArgumentsError):
            self._action_manager.do_action("123", "move", [])

        with self.assertRaises(actions.exceptions.InvalidArgumentsError):
            self._action_manager.do_action("123", "move", [None])

    def test_invalid_password(self):
        '''Test if ActionManager authenticate passwords correctly.'''
        robot = Robot("test_invalid_password_95312", "andhue-ifue876-fkdnpw-1")
        self._database.add_robot(robot, (3, 1))
        self._database.commit()

        with self.assertRaises(AuthenticationFailedError):
            self._action_manager.do_action("ieukjdf-ioquiwe-751io", "status", ["test_invalid_password_95312"])

    def test_dead_robot(self):
        '''Test if ActionManager checks a dead robot.'''
        robot = Robot("test_dead_robot_98176", "1234")
        robot.set_alive(False)
        self._database.add_robot(robot, (3, 2))
        self._database.commit()

        with self.assertRaises(AuthenticationFailedError):
            self._action_manager.do_action("1234", "status", ["test_dead_robot_98176"])

    def test_bad_actions(self):
        '''Test wrong action IDs.'''
        robot = Robot("test_bad_actions_2376", "123")
        self._database.add_robot(robot, (4, 1))
        self._database.commit()

        with self.assertRaises(actions.exceptions.InvalidActionError):
            self._action_manager.do_action("123", "not-exist-action", ["test_bad_actions_2376"])

        self._database.rollback()
        with self.assertRaises(actions.exceptions.InvalidActionError):
            self._action_manager.do_action("123", 5432, ["test_bad_actions_2376"])

        self._database.rollback()
        with self.assertRaises(actions.exceptions.InvalidActionError):
            self._action_manager.do_action("123", None, ["test_bad_actions_2376"])

        self._database.rollback()
        with self.assertRaises(actions.exceptions.InvalidActionError):
            self._action_manager.do_action("123", "", ["test_bad_actions_2376"])

    def test_ok(self):
        '''Execute a fine action.'''
        robot = Robot("test_ok_action_3278", "4467yrt-ddfjh-1u872-oiie")
        self._database.add_robot(robot, (3, 3))
        self._database.commit()

        initial_energy = robot.get_energy()
        initial_age = robot.get_life()

        result = self._action_manager.do_action("4467yrt-ddfjh-1u872-oiie", "status", ["test_ok_action_3278"])

        self.assertEqual(result['alive'], True)

        # Robot should lost energy and age.
        self._database.commit()
        robot = self._database.get_robot("test_ok_action_3278")
        self.assertEqual(robot.get_energy(), initial_energy - 1)
        self.assertEqual(robot.get_life(), initial_age - 1)

    def test_losing_energy_on_error(self):
        '''Tests if ActionManager reduces energy and age after an exception.'''
        robot = Robot("test_losing_energy_on_error_981", "091oikjdmncj")
        self._database.add_robot(robot, (5, 3))
        self._database.commit()

        initial_energy = robot.get_energy()
        initial_age = robot.get_life()

        with self.assertRaises(actions.exceptions.InvalidActionError):
            self._action_manager.do_action("091oikjdmncj", "invalid_action", ["test_losing_energy_on_error_981"])

        self._database.commit()
        robot = self._database.get_robot("test_losing_energy_on_error_981")
        self.assertEqual(robot.get_energy(), initial_energy - 1)
        self.assertEqual(robot.get_life(), initial_age - 1)

        # Robot shouldn't lose energy on authentication error.
        with self.assertRaises(AuthenticationFailedError):
            self._action_manager.do_action("wrong pass", "invalid_action", ["test_losing_energy_on_error_981"])

        self._database.rollback()
        robot = self._database.get_robot("test_losing_energy_on_error_981")
        self.assertEqual(robot.get_energy(), initial_energy - 1)
        self.assertEqual(robot.get_life(), initial_age - 1)

    def test_delay(self):
        '''Tests delay between robot actions.'''
        robot = Robot("test_delay_1223", "09112345")
        self._database.add_robot(robot, (6, 3))
        self._database.commit()

        self._action_manager.do_action("09112345", "sense", [robot.get_id()])
        self._database.commit()

        start_time = time.time()
        self._action_manager.do_action("09112345", "sense", [robot.get_id()])
        elapsed_time = time.time() - start_time
        self._database.commit()

        # one millisecond reduced from delay to cover error.
        self.assertGreater(elapsed_time, 0.029)
Exemplo n.º 35
0
class ActionManager:

    def __init__(self):
        self._authenticator = Authenticator()
        self._database = MemcachedDatabase()

        configs = Configs()
        self._robots_action_delay = configs.get_robots_actions_delay() / 1000

        self._actions = {'status': StatusAction(),
                         'sense': SenseAction(),
                         'move': MoveAction(),
                         'plant': PlantAction(),
                         'pick_water': PickWaterAction(),
                         'info': InfoAction(),
                         'eat': EatAction(),
                         'water': WaterAction()}

    def do_action(self, password, action_type, args):
        '''Will do the requested action.'''
        if len(args) == 0:
            raise exceptions.InvalidArgumentsError("`robot_id' should be passes as the first argument.")

        robot_id = args[0]
        if not isinstance(robot_id, str):
            raise exceptions.InvalidArgumentsError(
                "First argument (robot_id) should be a string, not {0}".format(type(robot_id)))

        robot = None
        try:
            robot = self._database.get_robot(robot_id, for_update=True)

            self._authenticator.authenticate_robot(robot, password)

            actions_delay = time.time() - robot.get_last_executed_action_time()
            if actions_delay < self._robots_action_delay:
                # Robot is sending actions too fast. Putting a delay between its actions.
                time.sleep(self._robots_action_delay - actions_delay)

            handler = self._get_action_handler(action_type)

            result = handler.do_action(robot, args)

            # Reducing age and energy.
            robot.set_energy(robot.get_energy() - 1)
            robot.set_life(robot.get_life() - 1)

            # Updating last executed action time.
            robot.set_last_executed_action_time(time.time())

            return result

        except LockAlreadyAquiredError as error:
            # Logging all concurrency errors, so we can investigate them later.
            Logger().info("LockAlreadyAquiredError: {0}".format(error))
            raise
        except AuthenticationFailedError:
            raise
        except BinarySkyException:
            # Concurrency exception is a fault of the server. Also, we ignored
            # authentication errors. Otherwise, robot should lose energy and age.

            # Note that we didn't handled unexpected errors (e.g. Exception class).
            # XXX: Dirty code. It shouldn't rollback and/or commit database changes.
            #      But right now, there's no better way.
            self._database.rollback()
            if robot is None:
                # Seems we couldn't even get the robot. So, robot didn't do any actions
                # and shouldn't lose energy and age.
                raise

            robot = self._database.get_robot(robot_id, for_update=True)
            robot.set_energy(robot.get_energy() - 1)
            robot.set_life(robot.get_life() - 1)
            robot.set_last_executed_action_time(time.time())

            self._database.commit()

            raise


    def _get_action_handler(self, action_type):
        '''Returns instance that should handle this action.'''
        if not isinstance(action_type, str):
            raise exceptions.InvalidActionError("Action type must be str, not {0}".format(type(action_type)))

        handler = self._actions.get(action_type)

        if handler is None:
            raise exceptions.InvalidActionError("Action {0} does not exists.".format(action_type))

        return handler
Exemplo n.º 36
0
 def tearDown(cls):
     database = MemcachedDatabase()
     database.rollback()