Skip to content

Commit a32a59d

Browse files
committed
Merge pull request #224 from dronekit/tcr-110
Ensures observers can't be added twice. Fixes #110
2 parents 09fe6d6 + de4b39b commit a32a59d

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

droneapi/lib/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,8 @@ def location_callback(location):
351351
if l is None:
352352
l = []
353353
self.__observers[attr_name] = l
354-
l.append(observer)
354+
if not observer in l:
355+
l.append(observer)
355356

356357
def remove_attribute_observer(self, attr_name, observer):
357358
"""

tests/sitl/test_110.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from droneapi.lib import VehicleMode
2+
from pymavlink import mavutil
3+
import time
4+
import sys
5+
import os
6+
from testlib import assert_equals
7+
8+
def test_110(local_connect):
9+
api = local_connect()
10+
v = api.get_vehicles()[0]
11+
12+
# Change the vehicle into STABILIZE mode
13+
v.mode = VehicleMode("STABILIZE")
14+
15+
# NOTE wait crudely for ACK on mode update
16+
time.sleep(3)
17+
18+
# Define example callback for mode
19+
def armed_callback(attribute):
20+
armed_callback.called += 1
21+
armed_callback.called = 0
22+
23+
# When the same (event, callback) pair is passed to add_attribute_observer,
24+
# only one instance of the observer callback should be added.
25+
v.add_attribute_observer('armed', armed_callback)
26+
v.add_attribute_observer('armed', armed_callback)
27+
v.add_attribute_observer('armed', armed_callback)
28+
v.add_attribute_observer('armed', armed_callback)
29+
v.add_attribute_observer('armed', armed_callback)
30+
31+
# Disarm and see update.
32+
v.armed = False
33+
v.flush()
34+
# Wait for ACK.
35+
time.sleep(3)
36+
37+
# Ensure the callback was called.
38+
assert armed_callback.called > 0, "Callback should have been called."
39+
40+
# Rmove all observers. The first call should remove all listeners
41+
# we've added; the second call should be ignored and not throw.
42+
# NOTE: We test if armed_callback were treating adding each additional callback
43+
# and remove_attribute_observer were removing them one at a time; in this
44+
# case, there would be three callbacks still attached.
45+
v.remove_attribute_observer('armed', armed_callback)
46+
v.remove_attribute_observer('armed', armed_callback)
47+
callcount = armed_callback.called
48+
49+
# Re-arm and see update.
50+
v.armed = True
51+
v.flush()
52+
# Wait for ack
53+
time.sleep(3)
54+
55+
# Ensure the callback was called zero times.
56+
assert_equals(armed_callback.called, callcount, "Callback should not have been called once removed.")

0 commit comments

Comments
 (0)