Skip to content

Commit ecf216a

Browse files
Tests: harden TestNetwork and TestNmt even more (#508)
- take into account that payloads with DATA1 may be in flight when we update the task - test task.stop() - use more lenient timeout - use threading.Condition for improved readability - wait for periodicity to be established before validation; some platforms are a little bit flaky Co-authored-by: André Colomb <[email protected]>
1 parent 7d7e2f1 commit ecf216a

File tree

2 files changed

+43
-15
lines changed

2 files changed

+43
-15
lines changed

test/test_network.py

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import logging
22
import unittest
3-
from threading import Event
3+
import threading
44

55
import canopen
66
import can
@@ -231,34 +231,58 @@ def test_network_send_periodic(self):
231231
DATA2 = bytes([4, 5, 6])
232232
COB_ID = 0x123
233233
PERIOD = 0.1
234+
TIMEOUT = PERIOD * 10
234235
self.network.connect(interface="virtual", receive_own_messages=True)
235236
self.addCleanup(self.network.disconnect)
236237

237238
acc = []
238-
event = Event()
239+
condition = threading.Condition()
239240

240241
def hook(_, data, ts):
241-
acc.append((data, ts))
242-
event.set()
242+
with condition:
243+
item = data, ts
244+
acc.append(item)
245+
condition.notify_all()
243246

244247
self.network.subscribe(COB_ID, hook)
245248
self.addCleanup(self.network.unsubscribe, COB_ID)
246249

247250
task = self.network.send_periodic(COB_ID, DATA1, PERIOD)
248251
self.addCleanup(task.stop)
249252

250-
event.wait(PERIOD*2)
251-
252-
# Update task data.
253+
def periodicity():
254+
# Check if periodicity is established; flakiness has been observed
255+
# on macOS.
256+
if len(acc) >= 2:
257+
delta = acc[-1][1] - acc[-2][1]
258+
return round(delta, ndigits=1) == PERIOD
259+
return False
260+
261+
# Wait for frames to arrive; then check the result.
262+
with condition:
263+
condition.wait_for(periodicity, TIMEOUT)
264+
self.assertTrue(all(v[0] == DATA1 for v in acc))
265+
266+
# Update task data, which may implicitly restart the timer.
267+
# Wait for frames to arrive; then check the result.
253268
task.update(DATA2)
254-
event.clear()
255-
event.wait(PERIOD*2)
256-
task.stop()
257-
269+
with condition:
270+
acc.clear()
271+
condition.wait_for(periodicity, TIMEOUT)
272+
# Find the first message with new data, and verify that all subsequent
273+
# messages also carry the new payload.
258274
data = [v[0] for v in acc]
259-
self.assertEqual(data, [DATA1, DATA2])
260-
ts = [v[1] for v in acc]
261-
self.assertAlmostEqual(ts[1]-ts[0], PERIOD, places=1)
275+
idx = data.index(DATA2)
276+
self.assertTrue(all(v[0] == DATA2 for v in acc[idx:]))
277+
278+
# Stop the task.
279+
task.stop()
280+
# A message may have been in flight when we stopped the timer,
281+
# so allow a single failure.
282+
bus = self.network.bus
283+
msg = bus.recv(TIMEOUT)
284+
if msg is not None:
285+
self.assertIsNone(bus.recv(TIMEOUT))
262286

263287

264288
class TestScanner(unittest.TestCase):

test/test_nmt.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,11 @@ def test_nmt_master_node_guarding(self):
117117
self.assertEqual(msg.dlc, 0)
118118

119119
self.node.nmt.stop_node_guarding()
120-
self.assertIsNone(self.bus.recv(self.TIMEOUT))
120+
# A message may have been in flight when we stopped the timer,
121+
# so allow a single failure.
122+
msg = self.bus.recv(self.TIMEOUT)
123+
if msg is not None:
124+
self.assertIsNone(self.bus.recv(self.TIMEOUT))
121125

122126

123127
class TestNmtSlave(unittest.TestCase):

0 commit comments

Comments
 (0)