|
1 | 1 | import logging
|
2 | 2 | import unittest
|
3 |
| -from threading import Event |
| 3 | +import threading |
4 | 4 |
|
5 | 5 | import canopen
|
6 | 6 | import can
|
@@ -231,34 +231,58 @@ def test_network_send_periodic(self):
|
231 | 231 | DATA2 = bytes([4, 5, 6])
|
232 | 232 | COB_ID = 0x123
|
233 | 233 | PERIOD = 0.1
|
| 234 | + TIMEOUT = PERIOD * 10 |
234 | 235 | self.network.connect(interface="virtual", receive_own_messages=True)
|
235 | 236 | self.addCleanup(self.network.disconnect)
|
236 | 237 |
|
237 | 238 | acc = []
|
238 |
| - event = Event() |
| 239 | + condition = threading.Condition() |
239 | 240 |
|
240 | 241 | 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() |
243 | 246 |
|
244 | 247 | self.network.subscribe(COB_ID, hook)
|
245 | 248 | self.addCleanup(self.network.unsubscribe, COB_ID)
|
246 | 249 |
|
247 | 250 | task = self.network.send_periodic(COB_ID, DATA1, PERIOD)
|
248 | 251 | self.addCleanup(task.stop)
|
249 | 252 |
|
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. |
253 | 268 | 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. |
258 | 274 | 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)) |
262 | 286 |
|
263 | 287 |
|
264 | 288 | class TestScanner(unittest.TestCase):
|
|
0 commit comments