def test_update_forward_should_update_state(self): state = State(has_taken_off=True, x_meters=1, y_meters=2, z_meters=3, orientation_degrees=60) actual = self.state_updater.update(Command.forward(1), state) expected = State(has_taken_off=True, time_used_seconds=1 / 0.5, x_meters=1 + sqrt(3) / 2, y_meters=2 + 0.5, z_meters=3, orientation_degrees=60) self.assertEqual(expected, actual) state = State(has_taken_off=True, x_meters=1, y_meters=2, z_meters=3, orientation_degrees=240) actual = self.state_updater.update(Command.forward(1), state) expected = State(has_taken_off=True, time_used_seconds=1 / 0.5, x_meters=1 - sqrt(3) / 2, y_meters=2 - 0.5, z_meters=3, orientation_degrees=240) self.assertEqual(expected, actual)
def test_function_procedure_in_function(self): commands = generate_commands(""" function func(int i) return int { while i < 10 { i <- i + 1; } return i; } procedure proc(int i) { forward(i * i); } function func2(int i) return int { proc(func(i)); return func(i) * func(i); } main () { takeoff(); forward(func2(1)); land(); } """) expected_commands = [SingleDroneCommand("DEFAULT", Command.takeoff()), SingleDroneCommand("DEFAULT", Command.forward(100)), SingleDroneCommand("DEFAULT", Command.forward(100)), SingleDroneCommand("DEFAULT", Command.land())] self.assertEqual(expected_commands, commands)
def test_corrupted_command_should_not_affect_correct_type(self): command = Command.forward(1) corrupted_command = Command.forward(1) corrupted_command._opcode = "corrupted" self.assertNotEqual(command, corrupted_command) self.assertEqual("forward", Command.forward(1).opcode) self.assertEqual([1], Command.forward(1).operands) self.assertEqual("corrupted", corrupted_command.opcode) self.assertEqual([1], corrupted_command.operands)
def test_eq(self): commands1 = [ None, Command.takeoff(), Command.land(), Command.up(1), Command.down(1), Command.left(1), Command.right(1), Command.forward(1), Command.backward(1), Command.rotate_left(1), Command.rotate_right(1), Command.wait(1), Command.up(2), Command.down(2), Command.left(2), Command.right(2), Command.forward(2), Command.backward(2), Command.rotate_left(2), Command.rotate_right(2), Command.wait(2) ] commands2 = [ None, Command.takeoff(), Command.land(), Command.up(1), Command.down(1), Command.left(1), Command.right(1), Command.forward(1), Command.backward(1), Command.rotate_left(1), Command.rotate_right(1), Command.wait(1), Command.up(2), Command.down(2), Command.left(2), Command.right(2), Command.forward(2), Command.backward(2), Command.rotate_left(2), Command.rotate_right(2), Command.wait(2) ] for i in range(len(commands1)): for j in range(len(commands2)): if i == j: self.assertEqual(commands1[i], commands2[j]) else: self.assertNotEqual(commands1[i], commands2[j])
def test_multiple_commands_should_return_correct_command(self): actual = generate_commands(""" main() { takeoff(); up(1); down(2); left(3); right(4); forward(5); backward(6); rotate_left(7); rotate_right(8); wait(9); land(); } """) expected = [ SingleDroneCommand("DEFAULT", Command.takeoff()), SingleDroneCommand("DEFAULT", Command.up(1)), SingleDroneCommand("DEFAULT", Command.down(2)), SingleDroneCommand("DEFAULT", Command.left(3)), SingleDroneCommand("DEFAULT", Command.right(4)), SingleDroneCommand("DEFAULT", Command.forward(5)), SingleDroneCommand("DEFAULT", Command.backward(6)), SingleDroneCommand("DEFAULT", Command.rotate_left(7)), SingleDroneCommand("DEFAULT", Command.rotate_right(8)), SingleDroneCommand("DEFAULT", Command.wait(9)), SingleDroneCommand("DEFAULT", Command.land()) ] self.assertEqual(expected, actual)
def test_forward_with_int_parameter_should_return_correct_command(self): actual = generate_commands(""" main() { takeoff(); forward(1); land(); } """) expected = [ SingleDroneCommand("DEFAULT", Command.takeoff()), SingleDroneCommand("DEFAULT", Command.forward(1)), SingleDroneCommand("DEFAULT", Command.land()) ] self.assertEqual(expected, actual)
def test_forward_with_drone_name_should_return_correct_command(self): actual = generate_commands( """ main() { DRONE1.takeoff(); DRONE1.forward(1); DRONE1.land(); } """, drone_config_map={"DRONE1": DefaultDroneConfig()}) expected = [ SingleDroneCommand("DRONE1", Command.takeoff()), SingleDroneCommand("DRONE1", Command.forward(1)), SingleDroneCommand("DRONE1", Command.land()) ] self.assertEqual(expected, actual)
def visitForward(self, ctx: xDroneParser.ForwardContext) -> None: exprs = [self.visit(expr) for expr in ctx.expr()] if ctx.DOT(): drone_expr, expr = exprs else: drone_expr, expr = None, exprs[0] if expr.type != Type.int() and expr.type != Type.decimal(): raise CompileError( "Expression {} should have type int or decimal, but is {}". format(expr, expr.type)) drone_name = self._get_drone_name(drone_expr) self._get_latest_commands().append( SingleDroneCommand(drone_name, Command.forward(expr.value)))
def test_function_procedure_in_procedure(self): commands = generate_commands(""" function func(int i) return int { return i * i; } procedure proc(int i) { forward(i); } procedure proc2() { takeoff(); proc(func(10)); proc(func(10)); land(); } main () { proc2(); } """) expected_commands = [SingleDroneCommand("DEFAULT", Command.takeoff()), SingleDroneCommand("DEFAULT", Command.forward(100)), SingleDroneCommand("DEFAULT", Command.forward(100)), SingleDroneCommand("DEFAULT", Command.land())] self.assertEqual(expected_commands, commands)
def test_repeat_should_run_correct_commands(self): actual_commands = generate_commands(""" main () { takeoff(); repeat 4 times { forward(1); } land(); } """) expected_commands = [SingleDroneCommand("DEFAULT", Command.takeoff())] + \ [SingleDroneCommand("DEFAULT", Command.forward(1)) for _ in range(4)] + \ [SingleDroneCommand("DEFAULT", Command.land())] self.assertEqual(expected_commands, actual_commands)
def test_check_single_drone_command_should_detect_collision_and_give_error(self): drone_commands = [SingleDroneCommand("DRONE1", Command.takeoff()), SingleDroneCommand("DRONE2", Command.takeoff()), SingleDroneCommand("DRONE1", Command.right(1)), SingleDroneCommand("DRONE2", Command.wait(1)), SingleDroneCommand("DRONE2", Command.rotate_left(90)), SingleDroneCommand("DRONE2", Command.forward(1)), SingleDroneCommand("DRONE1", Command.land()), SingleDroneCommand("DRONE2", Command.land())] with self.assertRaises(SafetyCheckError) as context: self.collision_checker.check(drone_commands, self.state_updater_map) self.assertTrue("Collisions might happen!\nCollision might happen between DRONE1 and DRONE2" in str(context.exception))
def test_loops_in_function(self): commands = generate_commands(""" function func(int i) return int { while i < 10 { i <- i + 1; } return i; } main () { takeoff(); forward(func(1)); land(); } """) expected_commands = [SingleDroneCommand("DEFAULT", Command.takeoff()), SingleDroneCommand("DEFAULT", Command.forward(10)), SingleDroneCommand("DEFAULT", Command.land())] self.assertEqual(expected_commands, commands)
def test_recursion(self): commands = generate_commands(""" function func(int i) return int { if i >= 10 { return 10; } return func(i + 1); } main () { takeoff(); forward(func(1)); land(); } """) expected_commands = [SingleDroneCommand("DEFAULT", Command.takeoff()), SingleDroneCommand("DEFAULT", Command.forward(10)), SingleDroneCommand("DEFAULT", Command.land())] self.assertEqual(expected_commands, commands)
def test_loops_in_function(self): commands = generate_commands(""" procedure proc(int i) { while i < 10 { i <- i + 1; } forward(i); } main () { takeoff(); proc(1); land(); } """) expected_commands = [SingleDroneCommand("DEFAULT", Command.takeoff()), SingleDroneCommand("DEFAULT", Command.forward(10)), SingleDroneCommand("DEFAULT", Command.land())] self.assertEqual(expected_commands, commands)
def test_if_true_without_else_should_run_correct_commands(self): actual_st = SymbolTable() actual_commands = generate_commands(""" main () { takeoff(); if true { int a <- 1; forward(a); } land(); } """, symbol_table=actual_st) expected_st = SymbolTable() self.assertEqual(expected_st, actual_st) expected_commands = [SingleDroneCommand("DEFAULT", Command.takeoff()), SingleDroneCommand("DEFAULT", Command.forward(1)), SingleDroneCommand("DEFAULT", Command.land())] self.assertEqual(expected_commands, actual_commands)
def test_execute_single_drone_command(self): single_commands = [ SingleDroneCommand("name1", Command.takeoff()), SingleDroneCommand("name1", Command.land()), SingleDroneCommand("name2", Command.up(1.1)), SingleDroneCommand("name2", Command.down(1.2)), SingleDroneCommand("name2", Command.left(1.3)), SingleDroneCommand("name2", Command.right(1.4)), SingleDroneCommand("name2", Command.forward(1.5)), SingleDroneCommand("name2", Command.backward(1.6)), SingleDroneCommand("name2", Command.rotate_left(91)), SingleDroneCommand("name2", Command.rotate_right(92)), SingleDroneCommand("name2", Command.wait(1)) ] with patch( 'xdrone.command_converters.dji_tello_edu_drone_executor.FlyTello', return_value=self.fly_tello): executor = DJITelloEduExecutor(self.name_id_map) executor.execute_drone_commands(single_commands) calls = [ call.wait_sync(), call.takeoff(tello=1), call.wait_sync(), call.land(tello=1), call.wait_sync(), call.up(110, tello=2), call.wait_sync(), call.down(120, tello=2), call.wait_sync(), call.left(130, tello=2), call.wait_sync(), call.right(140, tello=2), call.wait_sync(), call.forward(150, tello=2), call.wait_sync(), call.back(160, tello=2), call.wait_sync(), call.rotate_ccw(91, tello=2), call.wait_sync(), call.rotate_cw(92, tello=2), call.wait_sync(), call.pause(1) ] self.fly.assert_has_calls(calls)
def test_call_function_should_run_commands_and_return(self): commands = generate_commands(""" function func(int i, int j) return int { up(i + j); down(i); return i + j; } main () { takeoff(); forward(func(100, 200)); land(); } """) expected_commands = [SingleDroneCommand("DEFAULT", Command.takeoff()), SingleDroneCommand("DEFAULT", Command.up(300)), SingleDroneCommand("DEFAULT", Command.down(100)), SingleDroneCommand("DEFAULT", Command.forward(300)), SingleDroneCommand("DEFAULT", Command.land())] self.assertEqual(expected_commands, commands)
def test_recursion(self): commands = generate_commands(""" procedure proc(int i) { if i >= 10 { forward(10); return; } proc(i + 1); } main () { takeoff(); proc(1); land(); } """) expected_commands = [SingleDroneCommand("DEFAULT", Command.takeoff()), SingleDroneCommand("DEFAULT", Command.forward(10)), SingleDroneCommand("DEFAULT", Command.land())] self.assertEqual(expected_commands, commands)
def test_while_should_run_correct_commands(self): actual = SymbolTable() actual_commands = generate_commands(""" main () { takeoff(); int a <- 1; while a < 5 { a <- a + 1; forward(a); } land(); } """, symbol_table=actual) expected = SymbolTable() expected.store("a", Expression(Type.int(), 5, ident="a")) self.assertEqual(expected, actual) expected_commands = [SingleDroneCommand("DEFAULT", Command.takeoff())] + \ [SingleDroneCommand("DEFAULT", Command.forward(i)) for i in [2, 3, 4, 5]] + \ [SingleDroneCommand("DEFAULT", Command.land())] self.assertEqual(expected_commands, actual_commands)
def test_check_single_drone_command_other_command_when_not_taken_off_should_give_error( self): commands = [ Command.land(), Command.up(1), Command.down(1), Command.left(1), Command.right(1), Command.forward(1), Command.backward(1), Command.rotate_left(90), Command.rotate_right(90) ] for command in commands: with self.assertRaises(SafetyCheckError) as context: drone_commands = [SingleDroneCommand("DRONE1", command)] BoundaryChecker(self.boundary_config).check( drone_commands, self.state_updater_map) self.assertTrue( "'{}' command used when drone 'DRONE1' has not been taken off". format(command.opcode) in str(context.exception))
def test_for_with_step_should_run_correct_commands_and_update_symbol_table(self): actual_st = SymbolTable() actual_commands = generate_commands(""" main () { takeoff(); int i; int a <- 0; for i from 0 to 9 step 2 { a <- a + 1; forward(i); } land(); } """, symbol_table=actual_st) expected_st = SymbolTable() expected_st.store("i", Expression(Type.int(), 8, ident="i")) expected_st.store("a", Expression(Type.int(), 5, ident="a")) self.assertEqual(expected_st, actual_st) expected_commands = [SingleDroneCommand("DEFAULT", Command.takeoff())] + \ [SingleDroneCommand("DEFAULT", Command.forward(i)) for i in [0, 2, 4, 6, 8]] + \ [SingleDroneCommand("DEFAULT", Command.land())] self.assertEqual(expected_commands, actual_commands)
def test_if_false_with_else_should_run_correct_commands(self): actual = SymbolTable() actual_commands = generate_commands(""" main () { takeoff(); if false { int a <- 1; forward(a); } else { int a <- 2; int b <- 3; forward(b); } land(); } """, symbol_table=actual) expected = SymbolTable() self.assertEqual(expected, actual) expected_commands = [SingleDroneCommand("DEFAULT", Command.takeoff()), SingleDroneCommand("DEFAULT", Command.forward(3)), SingleDroneCommand("DEFAULT", Command.land())] self.assertEqual(expected_commands, actual_commands)
def test_scope(self): actual = SymbolTable() commands = generate_commands(""" function func(int i) return int { int j <- 100; while i < 10 { i <- i + 1; } return i; } main () { takeoff(); int i <- 1; forward(func(i)); land(); } """, symbol_table=actual) expected_commands = [SingleDroneCommand("DEFAULT", Command.takeoff()), SingleDroneCommand("DEFAULT", Command.forward(10)), SingleDroneCommand("DEFAULT", Command.land())] self.assertEqual(expected_commands, commands) expected = SymbolTable() expected.store("i", Expression(Type.int(), 1, ident="i")) self.assertEqual(expected, actual)
def test_convert_command(self): commands = [ SingleDroneCommand("DRONE1", Command.takeoff()), SingleDroneCommand("DRONE1", Command.up(1)), SingleDroneCommand("DRONE1", Command.down(1)), SingleDroneCommand("DRONE1", Command.left(1)), SingleDroneCommand("DRONE1", Command.right(1)), SingleDroneCommand("DRONE1", Command.forward(1)), SingleDroneCommand("DRONE1", Command.backward(1)), SingleDroneCommand("DRONE1", Command.rotate_left(1)), SingleDroneCommand("DRONE1", Command.rotate_right(1)), ParallelDroneCommands( [[ ParallelDroneCommands( [[], [SingleDroneCommand("DRONE1", Command.up(1))]]) ], [ SingleDroneCommand("DRONE2", Command.takeoff()), SingleDroneCommand("DRONE2", Command.land()) ]]), SingleDroneCommand("DRONE1", Command.land()), SingleDroneCommand("DRONE1", Command.wait(1)) ] drone_config_map = { "DRONE1": DroneConfig((1, 2, 3), 1, 90, 2), "DRONE2": DroneConfig((0, 0, 0), 1, 90, 1) } expected = { "config": [{ "name": "DRONE1", "init_pos": [1, 2, 3], "speed": 1, "rotate_speed": 90, "takeoff_height": 2 }, { "name": "DRONE2", "init_pos": [0, 0, 0], "speed": 1, "rotate_speed": 90, "takeoff_height": 1 }], "commands": [{ "type": "single", "name": "DRONE1", "action": "takeoff", "values": [] }, { "type": "single", "name": "DRONE1", "action": "up", "values": [1] }, { "type": "single", "name": "DRONE1", "action": "down", "values": [1] }, { "type": "single", "name": "DRONE1", "action": "left", "values": [1] }, { "type": "single", "name": "DRONE1", "action": "right", "values": [1] }, { "type": "single", "name": "DRONE1", "action": "forward", "values": [1] }, { "type": "single", "name": "DRONE1", "action": "backward", "values": [1] }, { "type": "single", "name": "DRONE1", "action": "rotate_left", "values": [1] }, { "type": "single", "name": "DRONE1", "action": "rotate_right", "values": [1] }, { "type": "parallel", "branches": [[{ "type": "parallel", "branches": [[], [{ "type": "single", "name": "DRONE1", "action": "up", "values": [1] }]] }], [{ "type": "single", "name": "DRONE2", "action": "takeoff", "values": [] }, { "type": "single", "name": "DRONE2", "action": "land", "values": [] }]] }, { "type": "single", "name": "DRONE1", "action": "land", "values": [] }, { "type": "single", "name": "DRONE1", "action": "wait", "values": [1] }] } actual = SimulationConverter().convert(commands, drone_config_map) self.assertEqual(expected, json.loads(zlib.decompress(b64decode(actual))))
def test_forward(self): self.assertEqual(Command.forward(1), Command.forward(1)) self.assertEqual("forward", Command.forward(1).opcode) self.assertEqual([1], Command.forward(1).operands) self.assertEqual("Command: { opcode: forward, operands: [1] }", str(Command.forward(1)))
def test_dji_tello_executor_should_send_correct_message(self) -> None: mocked_socket = Mock() mocked_socket.recvfrom = Mock(return_value=(b'ok', ('192.168.10.1', 8889))) mocked_socket.sendto = Mock() commands = [ Command.takeoff(), Command.up(1), Command.down(1), Command.left(1), Command.right(1), Command.forward(1), Command.backward(1), Command.rotate_left(90), Command.rotate_right(90), Command.wait(0), Command.land() ] executor = DJITelloExecutor() executor.sock.close() executor.sock = mocked_socket with self.assertLogs(logging.getLogger()) as log: executor.execute_commands(commands) calls = [ call(b"command", ('192.168.10.1', 8889)), call(b"takeoff", ('192.168.10.1', 8889)), call(b"up 100", ('192.168.10.1', 8889)), call(b"down 100", ('192.168.10.1', 8889)), call(b"left 100", ('192.168.10.1', 8889)), call(b"right 100", ('192.168.10.1', 8889)), call(b"forward 100", ('192.168.10.1', 8889)), call(b"back 100", ('192.168.10.1', 8889)), call(b"ccw 90", ('192.168.10.1', 8889)), call(b"cw 90", ('192.168.10.1', 8889)), call(b"land", ('192.168.10.1', 8889)) ] mocked_socket.sendto.assert_has_calls(calls) expected_log = [ "INFO:root:sent message: b'command'", "INFO:root:received response: ok", "INFO:root:sent message: b'takeoff'", "INFO:root:received response: ok", "INFO:root:sent message: b'up 100'", "INFO:root:received response: ok", "INFO:root:sent message: b'down 100'", "INFO:root:received response: ok", "INFO:root:sent message: b'left 100'", "INFO:root:received response: ok", "INFO:root:sent message: b'right 100'", "INFO:root:received response: ok", "INFO:root:sent message: b'forward 100'", "INFO:root:received response: ok", "INFO:root:sent message: b'back 100'", "INFO:root:received response: ok", "INFO:root:sent message: b'ccw 90'", "INFO:root:received response: ok", "INFO:root:sent message: b'cw 90'", "INFO:root:received response: ok", "INFO:root:sent message: b'land'", "INFO:root:received response: ok" ] self.assertEqual(expected_log, log.output)