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 new file mode 100644 index 000000000..4e220879d --- /dev/null +++ b/tests/sitl/test_110.py @@ -0,0 +1,56 @@ +from droneapi.lib import VehicleMode +from pymavlink import mavutil +import time +import sys +import os +from testlib import assert_equals + +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 + def armed_callback(attribute): + armed_callback.called += 1 + armed_callback.called = 0 + + # 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) + + # Disarm and see update. + v.armed = False + v.flush() + # Wait for ACK. + time.sleep(3) + + # 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) + callcount = armed_callback.called + + # Re-arm and see update. + v.armed = True + v.flush() + # Wait for ack + time.sleep(3) + + # Ensure the callback was called zero times. + assert_equals(armed_callback.called, callcount, "Callback should not have been called once removed.")