def test_get_next_with_invalid_command(self): CommandsParser.Register(ParserTestCase.MockCommand('MOVE')) parser = CommandsParser(['MOVE', 'DANCE', 'SIT', 'MOVE']) self.assertIsNotNone(parser.get_next_command()) self.assertIsNotNone(parser.get_next_command()) self.assertIsNone(parser.get_next_command()) # MOVE, MOVE, None CommandsParser.COMMANDS = {}
def main(): """ Main logic loop. Not tested under Unit testing, since it's more suitable for integration tests! """ parser = argparse.ArgumentParser(description='REA Robot') parser.add_argument('--c', metavar='FILE', type=str, required=False, help='File with commands to execute. One command per line') args = parser.parse_args() # Get list of commands to execute commands = load_command_list(args.c) if len(commands) == 0: commands = read_commands_from_console() logger.debug('List of commands to execute: {}'.format(commands)) # Run the Robot robot = Robot() cmd_parser = CommandsParser(commands) while True: cmd_and_args = cmd_parser.get_next_command() if cmd_and_args: cmd_and_args[0].run(robot, cmd_and_args[1]) else: break
def test_get_next_on_non_empty_list(self): CommandsParser.Register(ParserTestCase.MockCommand('MOVE')) CommandsParser.Register(ParserTestCase.MockCommand('REPORT')) parser = CommandsParser(['MOVE', 'REPORT']) self.assertIsNotNone(parser.get_next_command()) self.assertIsNotNone(parser.get_next_command()) self.assertIsNone(parser.get_next_command()) # MOVE, REPORT, None CommandsParser.COMMANDS = {}
def test_static_register_method(self): self.assertEquals(len(CommandsParser.COMMANDS), 0) mock = ParserTestCase.MockCommand('mock') CommandsParser.Register(mock) self.assertEquals(len(CommandsParser.COMMANDS), 1) self.assertIsInstance(CommandsParser.COMMANDS['mock'], ParserTestCase.MockCommand)
def test_get_next_on_empty_list(self): parser = CommandsParser([]) self.assertIsNone(parser.get_next_command()) # None
def test_instance(self): parser = CommandsParser() self.assertIsInstance(parser, CommandsParser)
""" Implementation of the MOVE command """ def __init__(self): self.ID = 'RIGHT' def run(self, robot, arguments=None): """ Execute the logic of the MOVE command """ if robot is None: return # NOTE: This has to be done for every command except PLACE. I Could have also implemented this completely in # the Robot class. The choice between these two options, in my opinion, is a design decision for who's # responsible - is the Robot responsible for all it's business rules (and so there's not really a need for # Commands - they can be implemented inside the Robot, or is he a dumb state machine and the rules are applied # by the commands. I believe in the context of this exercise both are valid, and so I chose the latter. # Also, please note that the Initialised/PLACED rule IS DIFFERENT from the bounds rule. I consider the latter # to be input validation and not really a business rule (but of course, the line is very thin here). if str(robot) == RobotUninitialisedState: return logger.info('Running command RIGHT') robot.update(robot.X, robot.Y, Robot.get_next_prev_direction(robot.Dir, 1)) # Register with the CommandParser class so that this command can be used by the parser CommandsParser.Register(RightCommand())
""" Implementation of the LEFT command """ def __init__(self): self.ID = 'LEFT' def run(self, robot, arguments=None): """ Execute the logic of the MOVE command """ if robot is None: return # NOTE: This has to be done for every command except PLACE. I Could have also implemented this completely in # the Robot class. The choice between these two options, in my opinion, is a design decision for who's # responsible - is the Robot responsible for all it's business rules (and so there's not really a need for # Commands - they can be implemented inside the Robot, or is he a dumb state machine and the rules are applied # by the commands. I believe in the context of this exercise both are valid, and so I chose the latter. # Also, please note that the Initialised/PLACED rule IS DIFFERENT from the bounds rule. I consider the latter # to be input validation and not really a business rule (but of course, the line is very thin here). if str(robot) == RobotUninitialisedState: return logger.info('Running command LEFT') robot.update(robot.X, robot.Y, Robot.get_next_prev_direction(robot.Dir, -1)) # Register with the CommandParser class so that this command can be used by the parser CommandsParser.Register(LeftCommand())
from parser import CommandsParser import logging logger = logging.getLogger(__name__) class PlaceCommand(CommandBase): """ Implementation of the PLACE command """ def __init__(self): self.ID = 'PLACE' def run(self, robot, arguments): """ Execute the logic of the PLACE command """ if robot is None: return if isinstance(arguments, str): arguments = [x.strip() for x in arguments.split(',') if x.strip() != ''] logger.info('Running command PLACE with arguments {},{},{}'.format(arguments[0], arguments[1], arguments[2])) robot.update(int(arguments[0]), int(arguments[1]), arguments[2]) # Register with the CommandParser class so that this command can be used by the parser CommandsParser.Register(PlaceCommand())
class ReportCommand(CommandBase): """ Implementation of the MOVE command """ def __init__(self): self.ID = 'REPORT' def run(self, robot, arguments=None): """ Execute the logic of the MOVE command """ if robot is None: return # NOTE: This has to be done for every command except PLACE. I Could have also implemented this completely in # the Robot class. The choice between these two options, in my opinion, is a design decision for who's # responsible - is the Robot responsible for all it's business rules (and so there's not really a need for # Commands - they can be implemented inside the Robot, or is he a dumb state machine and the rules are applied # by the commands. I believe in the context of this exercise both are valid, and so I chose the latter. # Also, please note that the Initialised/PLACED rule IS DIFFERENT from the bounds rule. I consider the latter # to be input validation and not really a business rule (but of course, the line is very thin here). if str(robot) == RobotUninitialisedState: return logger.info('Running command REPORT') print str(robot) # Register with the CommandParser class so that this command can be used by the parser CommandsParser.Register(ReportCommand())
def run(self, robot, arguments=None): """ Execute the logic of the MOVE command """ if robot is None: return # NOTE: This has to be done for every command except PLACE. I Could have also implemented this completely in # the Robot class. The choice between these two options, in my opinion, is a design decision for who's # responsible - is the Robot responsible for all it's business rules (and so there's not really a need for # Commands - they can be implemented inside the Robot, or is he a dumb state machine and the rules are applied # by the commands. I believe in the context of this exercise both are valid, and so I chose the latter. # Also, please note that the Initialised/PLACED rule IS DIFFERENT from the bounds rule. I consider the latter # to be input validation and not really a business rule (but of course, the line is very thin here). if str(robot) == RobotUninitialisedState: return logger.info('Running command MOVE {}'.format(robot.Dir)) if robot.Dir == 'NORTH': robot.update(robot.X, robot.Y + 1, robot.Dir) elif robot.Dir == 'SOUTH': robot.update(robot.X, robot.Y - 1, robot.Dir) elif robot.Dir == 'EAST': robot.update(robot.X + 1, robot.Y, robot.Dir) elif robot.Dir == 'WEST': robot.update(robot.X - 1, robot.Y, robot.Dir) # Register with the CommandParser class so that this command can be used by the parser CommandsParser.Register(MoveCommand())