def setUp(self): self.logger = Mock() self.logger.error = Mock(side_effect=print_msg) self.servo = Mock() self.exchange = Exchange(self.logger) self.timer = StubTimer() TimeShift(self.exchange, self.timer.time)
def setUp(self): self.logger = Mock() self.logger.error = Mock(side_effect=print_msg) self.servo = Mock() self.exchange = Exchange(self.logger) self.timer = StubTimer() TimeShift(self.exchange,self.timer.time)
class EventTestCase(unittest.TestCase): def setUp(self): self.last_event = Event("None") self.events = {} self.exchange = Exchange(test_logger(logging.ERROR)) def event_recorder(self, event): self.last_event = event self.events[event.name].append(event) def listen(self, event_name): self.events[event_name] = [] self.exchange.subscribe(event_name, self.event_recorder) def event_count(self, event_name): if event_name in self.events: return len(self.events[event_name]) else: return 0
class EventTestCase(unittest.TestCase): def setUp(self): self.last_event = Event("None") self.events = {} self.exchange = Exchange(test_logger(logging.ERROR)) def event_recorder(self,event): self.last_event = event self.events[event.name].append(event) def listen(self,event_name): self.events[event_name] = [] self.exchange.subscribe(event_name,self.event_recorder) def event_count(self,event_name): if event_name in self.events: return len(self.events[event_name]) else: return 0
def __init__(self, gps=False, servo_port=SERVO_PORT): # devices self._gps = gps self.windsensor = WindSensor(I2C(WINDSENSOR_I2C_ADDRESS)) self.compass = Compass(I2C(COMPASS_I2C_ADDRESS), I2C(ACCELEROMETER_I2C_ADDRESS)) self.red_led = GpioWriter(17, os) self.green_led = GpioWriter(18, os) # Navigation self.globe = Globe() self.timer = Timer() self.application_logger = self._rotating_logger(APPLICATION_NAME) self.position_logger = self._rotating_logger("position") self.exchange = Exchange(self.application_logger) self.timeshift = TimeShift(self.exchange, self.timer.time) self.event_source = EventSource(self.exchange, self.timer, self.application_logger, CONFIG['event source']) self.sensors = Sensors(self.gps, self.windsensor, self.compass, self.timer.time, self.exchange, self.position_logger, CONFIG['sensors']) self.gps_console_writer = GpsConsoleWriter(self.gps) self.rudder_servo = Servo(serial.Serial(servo_port), RUDDER_SERVO_CHANNEL, RUDDER_MIN_PULSE, RUDDER_MIN_ANGLE, RUDDER_MAX_PULSE, RUDDER_MAX_ANGLE) self.steerer = Steerer(self.rudder_servo, self.application_logger, CONFIG['steerer']) self.helm = Helm(self.exchange, self.sensors, self.steerer, self.application_logger, CONFIG['helm']) self.course_steerer = CourseSteerer(self.sensors, self.helm, self.timer, CONFIG['course steerer']) self.navigator = Navigator(self.sensors, self.globe, self.exchange, self.application_logger, CONFIG['navigator']) self.self_test = SelfTest(self.red_led, self.green_led, self.timer, self.rudder_servo, RUDDER_MIN_ANGLE, RUDDER_MAX_ANGLE) # Tracking self.tracking_logger = self._rotating_logger("track") self.tracking_sensors = Sensors(self.gps, self.windsensor, self.compass, self.timer.time, self.exchange, self.tracking_logger, CONFIG['sensors']) self.tracker = Tracker(self.tracking_logger, self.tracking_sensors, self.timer)
def __init__(self): self.globe = Globe() self.console_logger = self._console_logger() self.exchange = Exchange(self.console_logger) self.gps = SimulatedGPS(CHORLTON.position,0,0.1) self.vehicle = SimulatedVehicle(self.gps, self.globe,self.console_logger,single_step=False) self.timeshift = TimeShift(self.exchange,self.vehicle.timer.time) self.event_source = EventSource(self.exchange,self.vehicle.timer,self.console_logger,CONFIG['event source']) self.sensors = Sensors(self.vehicle.gps, self.vehicle.windsensor,self.vehicle.compass,self.vehicle.timer.time,self.exchange,self.console_logger,CONFIG['sensors']) self.steerer = Steerer(self.vehicle.rudder,self.console_logger,CONFIG['steerer']) self.helm = Helm(self.exchange, self.sensors, self.steerer, self.console_logger, CONFIG['helm']) self.course_steerer = CourseSteerer(self.sensors,self.helm,self.vehicle.timer, CONFIG['course steerer']) self.navigator_simulator = Navigator(self.sensors,self.globe,self.exchange,self.console_logger,CONFIG['navigator']) self.tracking_timer = Timer() self.tracker_simulator = Tracker(self.console_logger,self.sensors,self.tracking_timer)
def test_errors_during_logging_should_be_ignored_and_event_processing_continues( self): failing_logger = Mock() failing_logger.configure_mock(**{'error.side_effect': RuntimeError}) exchange = Exchange(failing_logger) ts_error = TestSubscriber(exchange) ts_after_error = TestSubscriber(exchange) event = Event("bong") exchange.subscribe("bong", ts_error.bad_call) exchange.subscribe("bong", ts_after_error.callme) exchange.publish(event) failing_logger.error.assert_has_calls( [call('Exchange, RuntimeError: oops')]) self.assertEqual(ts_after_error.event_call_count("bong"), 1)
def test_errors_during_logging_should_be_ignored_and_event_processing_continues(self): failing_logger = Mock() failing_logger.configure_mock(**{'error.side_effect': RuntimeError}) exchange = Exchange(failing_logger) ts_error = TestSubscriber(exchange) ts_after_error = TestSubscriber(exchange) event = Event("bong") exchange.subscribe("bong",ts_error.bad_call) exchange.subscribe("bong",ts_after_error.callme) exchange.publish(event) failing_logger.error.assert_has_calls([call('Exchange, RuntimeError: oops')]) self.assertEqual(ts_after_error.event_call_count("bong"),1)
def setUp(self): self.mock_logger = Mock() self.mock_logger.error = Mock(side_effect=print_msg) self.exchange = Exchange(self.mock_logger) self.timer = StubTimer() self.timeshift = TimeShift(self.exchange, self.timer.time) self.event_source = EventSource(self.exchange, self.timer, self.mock_logger, {'tick interval': 0.2}) gps = FakeMovingGPS([ Position(10, 10), Position(11, 11), Position(12, 12), Position(13, 13) ]) self.navigator = Navigator(gps, Globe(), self.exchange, self.mock_logger, { 'min time to steer': 5, 'max time to steer': 20 })
class TestEventSource(unittest.TestCase): def setUp(self): self.mock_logger = Mock() self.exchange = Exchange(self.mock_logger) self.timer = Mock() self.last_event = None def event_recorder(self,event): self.last_listened_event = event self.event_count += 1 def intercept_publish(self,event): self.exchange.original_publish(event) if event.name == EventName.tick: raise RuntimeError("oops") def listen(self,event_name): self.event_count = 0 self.exchange.subscribe(event_name,self.event_recorder) def after(self,times,event_name): self.ticks_left = times self.end_event_name = event_name def count_down_ticks(self,args): self.ticks_left -= 1 if self.ticks_left <= 0: self.exchange.publish(Event(self.end_event_name)) def finish(self,args): self.exchange.publish(Event(EventName.end)) def test_should_publish_start_navigation_event(self): self.listen(EventName.start) self.timer.wait_for = Mock(side_effect=self.finish) event_source = EventSource(self.exchange,self.timer, self.mock_logger,CONFIG) event_source.start() self.assertEqual(self.last_listened_event.name,EventName.start) def test_should_publish_a_tick_event(self): self.listen(EventName.tick) self.timer.wait_for = Mock(side_effect=self.finish) event_source = EventSource(self.exchange, self.timer, self.mock_logger, CONFIG) event_source.start() self.assertEqual(self.last_listened_event.name,EventName.tick) def test_should_publish_multiple_events_until_nav_complete(self): self.listen(EventName.tick) self.after(5,EventName.end) self.timer.wait_for = Mock(side_effect=self.count_down_ticks) event_source = EventSource(self.exchange,self.timer, self.mock_logger,CONFIG) event_source.start() self.assertEqual(self.event_count,5) def test_errors_should_be_logged_and_events_continue(self): self.listen(EventName.tick) self.after(2,EventName.end) self.timer.wait_for = Mock(side_effect=self.count_down_ticks) self.exchange.original_publish = self.exchange.publish self.exchange.publish = self.intercept_publish event_source = EventSource(self.exchange,self.timer, self.mock_logger,CONFIG) event_source.start() self.mock_logger.error.assert_has_calls([call('EventSource, RuntimeError: oops')]) self.assertEqual(self.event_count,2) def test_errors_during_logging_should_be_ignored_and_event_processing_continues(self): failing_logger = Mock() failing_logger.configure_mock(**{'error.side_effect': RuntimeError}) self.listen(EventName.tick) self.after(2,EventName.end) self.timer.wait_for = Mock(side_effect=self.count_down_ticks) self.exchange.original_publish = self.exchange.publish self.exchange.publish = self.intercept_publish event_source = EventSource(self.exchange,self.timer, failing_logger,CONFIG) event_source.start() failing_logger.error.assert_has_calls([call('EventSource, RuntimeError: oops')]) self.assertEqual(self.event_count,2)
class TestNavigationAndHelm(unittest.TestCase): def ticks(self,number,duration): for i in range(1,number): self.exchange.publish(Event(EventName.tick)) self.timer.wait_for(duration) def setUp(self): self.logger = Mock() self.logger.error = Mock(side_effect=print_msg) self.servo = Mock() self.exchange = Exchange(self.logger) self.timer = StubTimer() TimeShift(self.exchange,self.timer.time) def test_should_steer_to_next_waypoint(self): destination = Waypoint(Position(10.03,10.03),10) gps = FakeMovingGPS([Position(10,10),Position(10.01,10.01),Position(10.02,10.02),Position(10.03,10.03)]) sensors = FakeSensors(gps,1,45) steerer = Steerer(self.servo,self.logger, CONFIG['steerer']) helm = Helm(self.exchange,sensors,steerer,self.logger, CONFIG['helm']) navigator = Navigator(sensors,Globe(),self.exchange,self.logger, CONFIG['navigator']) self.exchange.publish(Event(EventName.navigate,waypoint = destination)) self.ticks(number = 14,duration=200) self.logger.info.assert_has_calls( [call('Navigator, steering to +10.030000,+10.030000, bearing 44.6, distance 4681.8m, review after 600s'), call('Navigator, steering to +10.030000,+10.030000, bearing 44.6, distance 3121.2m, review after 600s'), call('Navigator, steering to +10.030000,+10.030000, bearing 44.6, distance 1560.6m, review after 600s'), call('Navigator, arrived at +10.030000,+10.030000')]) def test_should_navigate_to_next_waypoint_with_kink_in_route(self): destination = Waypoint(Position(10.03,10.03),10) gps = FakeMovingGPS([Position(10,10),Position(10.01,10.01),Position(10.025,10.015),Position(10.03,10.03)]) sensors = FakeSensors(gps,1,45) steerer = Steerer(self.servo,self.logger, CONFIG['steerer']) helm = Helm(self.exchange,sensors,steerer,self.logger, CONFIG['helm']) navigator = Navigator(sensors,Globe(),self.exchange,self.logger, CONFIG['navigator']) self.exchange.publish(Event(EventName.navigate,waypoint = destination)) self.ticks(number = 14,duration=200) self.logger.info.assert_has_calls( [call('Navigator, steering to +10.030000,+10.030000, bearing 44.6, distance 4681.8m, review after 600s'), call('Navigator, steering to +10.030000,+10.030000, bearing 44.6, distance 3121.2m, review after 600s'), call('Navigator, steering to +10.030000,+10.030000, bearing 71.3, distance 1734.0m, review after 600s'), call('Navigator, arrived at +10.030000,+10.030000')]) def test_should_steer_repeatedly_during_navigation(self): logger = Mock() destination = Waypoint(Position(10.0003,10.0003),10) gps = FakeMovingGPS([Position(10,10),Position(10.0001,10.00015),Position(10.00025,10.0002),Position(10.0003,10.0003)]) sensors = FakeSensors(gps,1,45) steerer = Steerer(self.servo,logger, CONFIG['steerer']) helm = Helm(self.exchange,sensors,steerer,logger, CONFIG['helm']) navigator = Navigator(sensors,Globe(),self.exchange,logger, CONFIG['navigator']) self.exchange.publish(Event(EventName.navigate,waypoint = destination)) self.ticks(number = 7,duration=20) logger.debug.assert_has_calls( [call('Navigator, distance from waypoint +46.819018, combined tolerance +10.000000'), call('Navigator, distance from waypoint +27.647432, combined tolerance +10.000000'), call('Steerer, steering 36.4, heading 45.0, rate of turn +1.0, rudder +0.0, new rudder +4.6'), call('Steerer, steering 36.4, heading 45.0, rate of turn +1.0, rudder +4.6, new rudder +9.2'), call('Navigator, distance from waypoint +12.281099, combined tolerance +10.000000'), call('Steerer, steering 63.1, heading 45.0, rate of turn +1.0, rudder +9.2, new rudder +0.4'), call('Navigator, distance from waypoint +0.000000, combined tolerance +10.000000'), call('Steerer, steering 63.1, heading 45.0, rate of turn +1.0, rudder +0.4, new rudder -8.3'), call('Steerer, steering 63.1, heading 45.0, rate of turn +1.0, rudder -8.3, new rudder -17.1')]) logger.info.assert_has_calls( [call('Navigator, steering to +10.000300,+10.000300, bearing 44.6, distance 46.8m, review after 23s'), call('Navigator, steering to +10.000300,+10.000300, bearing 36.4, distance 27.6m, review after 13s'), call('Navigator, steering to +10.000300,+10.000300, bearing 63.1, distance 12.3m, review after 6s'), call('Navigator, arrived at +10.000300,+10.000300')])
def setUp(self): self.last_event = Event("None") self.events = {} self.exchange = Exchange(test_logger(logging.ERROR))
def setUp(self): self.mock_logger = Mock() self.exchange = Exchange(self.mock_logger) self.timer = Mock() self.last_event = None
class TestEventSource(unittest.TestCase): def setUp(self): self.mock_logger = Mock() self.exchange = Exchange(self.mock_logger) self.timer = Mock() self.last_event = None def event_recorder(self, event): self.last_listened_event = event self.event_count += 1 def intercept_publish(self, event): self.exchange.original_publish(event) if event.name == EventName.tick: raise RuntimeError("oops") def listen(self, event_name): self.event_count = 0 self.exchange.subscribe(event_name, self.event_recorder) def after(self, times, event_name): self.ticks_left = times self.end_event_name = event_name def count_down_ticks(self, args): self.ticks_left -= 1 if self.ticks_left <= 0: self.exchange.publish(Event(self.end_event_name)) def finish(self, args): self.exchange.publish(Event(EventName.end)) def test_should_publish_start_navigation_event(self): self.listen(EventName.start) self.timer.wait_for = Mock(side_effect=self.finish) event_source = EventSource(self.exchange, self.timer, self.mock_logger, CONFIG) event_source.start() self.assertEqual(self.last_listened_event.name, EventName.start) def test_should_publish_a_tick_event(self): self.listen(EventName.tick) self.timer.wait_for = Mock(side_effect=self.finish) event_source = EventSource(self.exchange, self.timer, self.mock_logger, CONFIG) event_source.start() self.assertEqual(self.last_listened_event.name, EventName.tick) def test_should_publish_multiple_events_until_nav_complete(self): self.listen(EventName.tick) self.after(5, EventName.end) self.timer.wait_for = Mock(side_effect=self.count_down_ticks) event_source = EventSource(self.exchange, self.timer, self.mock_logger, CONFIG) event_source.start() self.assertEqual(self.event_count, 5) def test_errors_should_be_logged_and_events_continue(self): self.listen(EventName.tick) self.after(2, EventName.end) self.timer.wait_for = Mock(side_effect=self.count_down_ticks) self.exchange.original_publish = self.exchange.publish self.exchange.publish = self.intercept_publish event_source = EventSource(self.exchange, self.timer, self.mock_logger, CONFIG) event_source.start() self.mock_logger.error.assert_has_calls( [call('EventSource, RuntimeError: oops')]) self.assertEqual(self.event_count, 2) def test_errors_during_logging_should_be_ignored_and_event_processing_continues( self): failing_logger = Mock() failing_logger.configure_mock(**{'error.side_effect': RuntimeError}) self.listen(EventName.tick) self.after(2, EventName.end) self.timer.wait_for = Mock(side_effect=self.count_down_ticks) self.exchange.original_publish = self.exchange.publish self.exchange.publish = self.intercept_publish event_source = EventSource(self.exchange, self.timer, failing_logger, CONFIG) event_source.start() failing_logger.error.assert_has_calls( [call('EventSource, RuntimeError: oops')]) self.assertEqual(self.event_count, 2)
def setUp(self): self.mock_logger = Mock() self.exchange = Exchange(self.mock_logger)
class TestEvents(unittest.TestCase): def setUp(self): self.mock_logger = Mock() self.exchange = Exchange(self.mock_logger) def test_should_return_nil_event_if_not_called(self): ts = TestSubscriber() self.assertEqual(ts.last_event_called.name, "nil") def test_should_call_back(self): ts = TestSubscriber(self.exchange) self.exchange.subscribe("thing", ts.callme) self.exchange.publish(Event("thing")) def test_should_ignore_events_with_no_subscribers_and_log_warning(self): self.exchange.publish(Event("doesnotexist")) self.mock_logger.warn.assert_called_with( "Event(doesnotexist) published but no subscribers") def test_should_be_able_to_unsubscribe(self): ts1 = TestSubscriber(self.exchange) self.exchange.subscribe("never", ts1.callme) self.exchange.unsubscribe("never", ts1.callme) self.exchange.publish(Event("never")) self.assertEqual(ts1.last_event_called.name, "nil") self.mock_logger.warn.assert_called_with( "Event(never) published but no subscribers") def test_should_accept_unsubscribe_without_subscribe(self): ts1 = TestSubscriber(self.exchange) self.exchange.unsubscribe("never", ts1.callme) self.exchange.publish(Event("never")) self.assertEqual(ts1.last_event_called.name, "nil") self.mock_logger.warn.assert_called_with( "Event(never) published but no subscribers") def test_should_signal_events_to_multiple_subscribers(self): ts1 = TestSubscriber(self.exchange) ts2 = TestSubscriber(self.exchange) self.exchange.subscribe("boo", ts1.callme) self.exchange.subscribe("boo", ts2.callme) self.exchange.publish(Event("boo")) self.assertEqual(ts1.last_event_called.name, "boo") self.assertEqual(ts2.last_event_called.name, "boo") def test_should_be_able_to_chain_events(self): ts1 = TestSubscriber(self.exchange) ts2 = TestSubscriber(self.exchange) self.exchange.subscribe("chain", ts1.callme) self.exchange.subscribe("secondevent", ts2.callme) self.exchange.publish(Event("chain")) self.assertEqual(ts2.last_event_called.name, "secondevent") def test_should_chain_a_few_events(self): ts = TestSubscriber(self.exchange) ts2 = TestSubscriber(self.exchange) self.exchange.subscribe("chain", ts.callme) self.exchange.subscribe("secondevent", ts.callme) self.exchange.subscribe("secondevent", ts2.callme) self.exchange.publish(Event("chain")) self.assertEqual(ts.event_call_count("secondevent"), 1) self.assertEqual(ts2.event_call_count("secondevent"), 1) def test_should_call_events_again_if_we_signal_primary_event_again(self): ts = TestSubscriber(self.exchange) event = Event("bing") self.exchange.subscribe("bing", ts.callme) self.exchange.publish(event) self.exchange.publish(event) self.exchange.publish(event) self.assertEqual(ts.event_call_count("bing"), 3) def test_errors_should_be_logged_and_event_processing_continues(self): ts_error = TestSubscriber(self.exchange) ts_after_error = TestSubscriber(self.exchange) event = Event("bong") self.exchange.subscribe("bong", ts_error.bad_call) self.exchange.subscribe("bong", ts_after_error.callme) self.exchange.publish(event) self.mock_logger.error.assert_has_calls( [call('Exchange, RuntimeError: oops')]) self.assertEqual(ts_after_error.event_call_count("bong"), 1) def test_errors_during_logging_should_be_ignored_and_event_processing_continues( self): failing_logger = Mock() failing_logger.configure_mock(**{'error.side_effect': RuntimeError}) exchange = Exchange(failing_logger) ts_error = TestSubscriber(exchange) ts_after_error = TestSubscriber(exchange) event = Event("bong") exchange.subscribe("bong", ts_error.bad_call) exchange.subscribe("bong", ts_after_error.callme) exchange.publish(event) failing_logger.error.assert_has_calls( [call('Exchange, RuntimeError: oops')]) self.assertEqual(ts_after_error.event_call_count("bong"), 1)
class TestEvents(unittest.TestCase): def setUp(self): self.mock_logger = Mock() self.exchange = Exchange(self.mock_logger) def test_should_return_nil_event_if_not_called(self): ts = TestSubscriber() self.assertEqual(ts.last_event_called.name,"nil") def test_should_call_back(self): ts = TestSubscriber(self.exchange) self.exchange.subscribe("thing",ts.callme) self.exchange.publish(Event("thing")) def test_should_ignore_events_with_no_subscribers_and_log_warning(self): self.exchange.publish(Event("doesnotexist")) self.mock_logger.warn.assert_called_with("Event(doesnotexist) published but no subscribers") def test_should_be_able_to_unsubscribe(self): ts1 = TestSubscriber(self.exchange) self.exchange.subscribe("never",ts1.callme) self.exchange.unsubscribe("never",ts1.callme) self.exchange.publish(Event("never")) self.assertEqual(ts1.last_event_called.name,"nil") self.mock_logger.warn.assert_called_with("Event(never) published but no subscribers") def test_should_accept_unsubscribe_without_subscribe(self): ts1 = TestSubscriber(self.exchange) self.exchange.unsubscribe("never",ts1.callme) self.exchange.publish(Event("never")) self.assertEqual(ts1.last_event_called.name,"nil") self.mock_logger.warn.assert_called_with("Event(never) published but no subscribers") def test_should_signal_events_to_multiple_subscribers(self): ts1 = TestSubscriber(self.exchange) ts2 = TestSubscriber(self.exchange) self.exchange.subscribe("boo",ts1.callme) self.exchange.subscribe("boo",ts2.callme) self.exchange.publish(Event("boo")) self.assertEqual(ts1.last_event_called.name,"boo") self.assertEqual(ts2.last_event_called.name,"boo") def test_should_be_able_to_chain_events(self): ts1 = TestSubscriber(self.exchange) ts2 = TestSubscriber(self.exchange) self.exchange.subscribe("chain",ts1.callme) self.exchange.subscribe("secondevent",ts2.callme) self.exchange.publish(Event("chain")) self.assertEqual(ts2.last_event_called.name,"secondevent") def test_should_chain_a_few_events(self): ts = TestSubscriber(self.exchange) ts2 = TestSubscriber(self.exchange) self.exchange.subscribe("chain",ts.callme) self.exchange.subscribe("secondevent",ts.callme) self.exchange.subscribe("secondevent",ts2.callme) self.exchange.publish(Event("chain")) self.assertEqual(ts.event_call_count("secondevent"),1) self.assertEqual(ts2.event_call_count("secondevent"),1) def test_should_call_events_again_if_we_signal_primary_event_again(self): ts = TestSubscriber(self.exchange) event = Event("bing") self.exchange.subscribe("bing",ts.callme) self.exchange.publish(event) self.exchange.publish(event) self.exchange.publish(event) self.assertEqual(ts.event_call_count("bing"),3) def test_errors_should_be_logged_and_event_processing_continues(self): ts_error = TestSubscriber(self.exchange) ts_after_error = TestSubscriber(self.exchange) event = Event("bong") self.exchange.subscribe("bong",ts_error.bad_call) self.exchange.subscribe("bong",ts_after_error.callme) self.exchange.publish(event) self.mock_logger.error.assert_has_calls([call('Exchange, RuntimeError: oops')]) self.assertEqual(ts_after_error.event_call_count("bong"),1) def test_errors_during_logging_should_be_ignored_and_event_processing_continues(self): failing_logger = Mock() failing_logger.configure_mock(**{'error.side_effect': RuntimeError}) exchange = Exchange(failing_logger) ts_error = TestSubscriber(exchange) ts_after_error = TestSubscriber(exchange) event = Event("bong") exchange.subscribe("bong",ts_error.bad_call) exchange.subscribe("bong",ts_after_error.callme) exchange.publish(event) failing_logger.error.assert_has_calls([call('Exchange, RuntimeError: oops')]) self.assertEqual(ts_after_error.event_call_count("bong"),1)
class TestNavigationAndHelm(unittest.TestCase): def ticks(self, number, duration): for i in range(1, number): self.exchange.publish(Event(EventName.tick)) self.timer.wait_for(duration) def setUp(self): self.logger = Mock() self.logger.error = Mock(side_effect=print_msg) self.servo = Mock() self.exchange = Exchange(self.logger) self.timer = StubTimer() TimeShift(self.exchange, self.timer.time) def test_should_steer_to_next_waypoint(self): destination = Waypoint(Position(10.03, 10.03), 10) gps = FakeMovingGPS([ Position(10, 10), Position(10.01, 10.01), Position(10.02, 10.02), Position(10.03, 10.03) ]) sensors = FakeSensors(gps, 1, 45) steerer = Steerer(self.servo, self.logger, CONFIG['steerer']) helm = Helm(self.exchange, sensors, steerer, self.logger, CONFIG['helm']) navigator = Navigator(sensors, Globe(), self.exchange, self.logger, CONFIG['navigator']) self.exchange.publish(Event(EventName.navigate, waypoint=destination)) self.ticks(number=14, duration=200) self.logger.info.assert_has_calls([ call( 'Navigator, steering to +10.030000,+10.030000, bearing 44.6, distance 4681.8m, review after 600s' ), call( 'Navigator, steering to +10.030000,+10.030000, bearing 44.6, distance 3121.2m, review after 600s' ), call( 'Navigator, steering to +10.030000,+10.030000, bearing 44.6, distance 1560.6m, review after 600s' ), call('Navigator, arrived at +10.030000,+10.030000') ]) def test_should_navigate_to_next_waypoint_with_kink_in_route(self): destination = Waypoint(Position(10.03, 10.03), 10) gps = FakeMovingGPS([ Position(10, 10), Position(10.01, 10.01), Position(10.025, 10.015), Position(10.03, 10.03) ]) sensors = FakeSensors(gps, 1, 45) steerer = Steerer(self.servo, self.logger, CONFIG['steerer']) helm = Helm(self.exchange, sensors, steerer, self.logger, CONFIG['helm']) navigator = Navigator(sensors, Globe(), self.exchange, self.logger, CONFIG['navigator']) self.exchange.publish(Event(EventName.navigate, waypoint=destination)) self.ticks(number=14, duration=200) self.logger.info.assert_has_calls([ call( 'Navigator, steering to +10.030000,+10.030000, bearing 44.6, distance 4681.8m, review after 600s' ), call( 'Navigator, steering to +10.030000,+10.030000, bearing 44.6, distance 3121.2m, review after 600s' ), call( 'Navigator, steering to +10.030000,+10.030000, bearing 71.3, distance 1734.0m, review after 600s' ), call('Navigator, arrived at +10.030000,+10.030000') ]) def test_should_steer_repeatedly_during_navigation(self): logger = Mock() destination = Waypoint(Position(10.0003, 10.0003), 10) gps = FakeMovingGPS([ Position(10, 10), Position(10.0001, 10.00015), Position(10.00025, 10.0002), Position(10.0003, 10.0003) ]) sensors = FakeSensors(gps, 1, 45) steerer = Steerer(self.servo, logger, CONFIG['steerer']) helm = Helm(self.exchange, sensors, steerer, logger, CONFIG['helm']) navigator = Navigator(sensors, Globe(), self.exchange, logger, CONFIG['navigator']) self.exchange.publish(Event(EventName.navigate, waypoint=destination)) self.ticks(number=7, duration=20) logger.debug.assert_has_calls([ call( 'Navigator, distance from waypoint +46.819018, combined tolerance +10.000000' ), call( 'Navigator, distance from waypoint +27.647432, combined tolerance +10.000000' ), call( 'Steerer, steering 36.4, heading 45.0, rate of turn +1.0, rudder +0.0, new rudder +4.6' ), call( 'Steerer, steering 36.4, heading 45.0, rate of turn +1.0, rudder +4.6, new rudder +9.2' ), call( 'Navigator, distance from waypoint +12.281099, combined tolerance +10.000000' ), call( 'Steerer, steering 63.1, heading 45.0, rate of turn +1.0, rudder +9.2, new rudder +0.4' ), call( 'Navigator, distance from waypoint +0.000000, combined tolerance +10.000000' ), call( 'Steerer, steering 63.1, heading 45.0, rate of turn +1.0, rudder +0.4, new rudder -8.3' ), call( 'Steerer, steering 63.1, heading 45.0, rate of turn +1.0, rudder -8.3, new rudder -17.1' ) ]) logger.info.assert_has_calls([ call( 'Navigator, steering to +10.000300,+10.000300, bearing 44.6, distance 46.8m, review after 23s' ), call( 'Navigator, steering to +10.000300,+10.000300, bearing 36.4, distance 27.6m, review after 13s' ), call( 'Navigator, steering to +10.000300,+10.000300, bearing 63.1, distance 12.3m, review after 6s' ), call('Navigator, arrived at +10.000300,+10.000300') ])