From dc91fdd86fbf9ba71356c9dede37f6f7c79cd50f Mon Sep 17 00:00:00 2001 From: Tim Ryan Date: Thu, 16 Jul 2015 15:17:11 -0700 Subject: [PATCH 1/2] Adds test for #110. --- tests/sitl/test_110.py | 49 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 tests/sitl/test_110.py diff --git a/tests/sitl/test_110.py b/tests/sitl/test_110.py new file mode 100644 index 000000000..96eea10c4 --- /dev/null +++ b/tests/sitl/test_110.py @@ -0,0 +1,49 @@ +from droneapi.lib import VehicleMode +from pymavlink import mavutil +import time +import sys +import os + +def test_110(local_connect): + api = local_connect() + v = api.get_vehicles()[0] + + # Change the vehicle into STABILIZE mode + v.mode = VehicleMode("STABILIZE") + + # NOTE wait crudely for ACK on mode update + time.sleep(3) + + # Define example callback for mode + allow_callback = True + bad_call = [False] + def armed_callback(attribute): + if not allow_callback: + bad_call[0] = True + + # Only one of the same observer fn should be added. + v.add_attribute_observer('armed', armed_callback) + v.add_attribute_observer('armed', armed_callback) + v.add_attribute_observer('armed', armed_callback) + v.add_attribute_observer('armed', armed_callback) + allow_callback = True + + # Disarm and see update. + v.armed = False + v.flush() + + time.sleep(3) + + # Rmove (all) observers. + v.remove_attribute_observer('armed', armed_callback) + v.remove_attribute_observer('armed', armed_callback) + allow_callback = False + + # Re-arm and see update. + v.armed = True + v.flush() + + time.sleep(3) + + # Make sure no bad calls happened. + assert not bad_call[0], "Callback should not have been called once removed." From de4b39b58ff7c40d3fde853719bcd4f3b9d61ba9 Mon Sep 17 00:00:00 2001 From: Tim Ryan Date: Thu, 16 Jul 2015 15:18:59 -0700 Subject: [PATCH 2/2] Ensures observers can't be added twice. Fixes #110 --- droneapi/lib/__init__.py | 3 ++- tests/sitl/test_110.py | 31 +++++++++++++++++++------------ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/droneapi/lib/__init__.py b/droneapi/lib/__init__.py index bcb7a37a6..87d7fa47d 100644 --- a/droneapi/lib/__init__.py +++ b/droneapi/lib/__init__.py @@ -351,7 +351,8 @@ def location_callback(location): if l is None: l = [] self.__observers[attr_name] = l - l.append(observer) + if not observer in l: + l.append(observer) def remove_attribute_observer(self, attr_name, observer): """ diff --git a/tests/sitl/test_110.py b/tests/sitl/test_110.py index 96eea10c4..4e220879d 100644 --- a/tests/sitl/test_110.py +++ b/tests/sitl/test_110.py @@ -3,6 +3,7 @@ import time import sys import os +from testlib import assert_equals def test_110(local_connect): api = local_connect() @@ -15,35 +16,41 @@ def test_110(local_connect): time.sleep(3) # Define example callback for mode - allow_callback = True - bad_call = [False] def armed_callback(attribute): - if not allow_callback: - bad_call[0] = True + armed_callback.called += 1 + armed_callback.called = 0 - # Only one of the same observer fn should be added. + # When the same (event, callback) pair is passed to add_attribute_observer, + # only one instance of the observer callback should be added. + v.add_attribute_observer('armed', armed_callback) v.add_attribute_observer('armed', armed_callback) v.add_attribute_observer('armed', armed_callback) v.add_attribute_observer('armed', armed_callback) v.add_attribute_observer('armed', armed_callback) - allow_callback = True # Disarm and see update. v.armed = False v.flush() - + # Wait for ACK. time.sleep(3) - # Rmove (all) observers. + # Ensure the callback was called. + assert armed_callback.called > 0, "Callback should have been called." + + # Rmove all observers. The first call should remove all listeners + # we've added; the second call should be ignored and not throw. + # NOTE: We test if armed_callback were treating adding each additional callback + # and remove_attribute_observer were removing them one at a time; in this + # case, there would be three callbacks still attached. v.remove_attribute_observer('armed', armed_callback) v.remove_attribute_observer('armed', armed_callback) - allow_callback = False + callcount = armed_callback.called # Re-arm and see update. v.armed = True v.flush() - + # Wait for ack time.sleep(3) - # Make sure no bad calls happened. - assert not bad_call[0], "Callback should not have been called once removed." + # Ensure the callback was called zero times. + assert_equals(armed_callback.called, callcount, "Callback should not have been called once removed.")