Skip to content

Commit

Permalink
switch cereal to pytest (commaai#32950)
Browse files Browse the repository at this point in the history
pytest
  • Loading branch information
maxime-desroches authored Jul 9, 2024
1 parent 35df0a4 commit 133f25e
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 66 deletions.
52 changes: 21 additions & 31 deletions cereal/messaging/tests/test_messaging.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
#!/usr/bin/env python3
import os
import capnp
import multiprocessing
import numbers
import random
import threading
import time
import unittest
from parameterized import parameterized
import pytest

from cereal import log, car
import cereal.messaging as messaging
Expand All @@ -28,12 +27,6 @@ def zmq_sleep(t=1):
if "ZMQ" in os.environ:
time.sleep(t)

def zmq_expected_failure(func):
if "ZMQ" in os.environ:
return unittest.expectedFailure(func)
else:
return func


# TODO: this should take any capnp struct and returrn a msg with random populated data
def random_carstate():
Expand All @@ -58,12 +51,12 @@ def send_func():
threading.Timer(delay, send_func).start()


class TestMessaging(unittest.TestCase):
class TestMessaging:
def setUp(self):
# TODO: ZMQ tests are too slow; all sleeps will need to be
# replaced with logic to block on the necessary condition
if "ZMQ" in os.environ:
raise unittest.SkipTest
pytest.skip()

# ZMQ pub socket takes too long to die
# sleep to prevent multiple publishers error between tests
Expand All @@ -75,9 +68,9 @@ def test_new_message(self, evt):
msg = messaging.new_message(evt)
except capnp.lib.capnp.KjException:
msg = messaging.new_message(evt, random.randrange(200))
self.assertLess(time.monotonic() - msg.logMonoTime, 0.1)
self.assertFalse(msg.valid)
self.assertEqual(evt, msg.which())
assert (time.monotonic() - msg.logMonoTime) < 0.1
assert not msg.valid
assert evt == msg.which()

@parameterized.expand(events)
def test_pub_sock(self, evt):
Expand All @@ -99,18 +92,18 @@ def test_drain_sock(self, func, expected_type):

# no wait and no msgs in queue
msgs = func(sub_sock)
self.assertIsInstance(msgs, list)
self.assertEqual(len(msgs), 0)
assert isinstance(msgs, list)
assert len(msgs) == 0

# no wait but msgs are queued up
num_msgs = random.randrange(3, 10)
for _ in range(num_msgs):
pub_sock.send(messaging.new_message(sock).to_bytes())
time.sleep(0.1)
msgs = func(sub_sock)
self.assertIsInstance(msgs, list)
self.assertTrue(all(isinstance(msg, expected_type) for msg in msgs))
self.assertEqual(len(msgs), num_msgs)
assert isinstance(msgs, list)
assert all(isinstance(msg, expected_type) for msg in msgs)
assert len(msgs) == num_msgs

def test_recv_sock(self):
sock = "carState"
Expand All @@ -120,14 +113,14 @@ def test_recv_sock(self):

# no wait and no msg in queue, socket should timeout
recvd = messaging.recv_sock(sub_sock)
self.assertTrue(recvd is None)
assert recvd is None

# no wait and one msg in queue
msg = random_carstate()
pub_sock.send(msg.to_bytes())
time.sleep(0.01)
recvd = messaging.recv_sock(sub_sock)
self.assertIsInstance(recvd, capnp._DynamicStructReader)
assert isinstance(recvd, capnp._DynamicStructReader)
# https://github.com/python/mypy/issues/13038
assert_carstate(msg.carState, recvd.carState)

Expand All @@ -139,16 +132,16 @@ def test_recv_one(self):

# no msg in queue, socket should timeout
recvd = messaging.recv_one(sub_sock)
self.assertTrue(recvd is None)
assert recvd is None

# one msg in queue
msg = random_carstate()
pub_sock.send(msg.to_bytes())
recvd = messaging.recv_one(sub_sock)
self.assertIsInstance(recvd, capnp._DynamicStructReader)
assert isinstance(recvd, capnp._DynamicStructReader)
assert_carstate(msg.carState, recvd.carState)

@zmq_expected_failure
@pytest.mark.xfail(condition="ZMQ" in os.environ, reason='ZMQ detected')
def test_recv_one_or_none(self):
sock = "carState"
pub_sock = messaging.pub_sock(sock)
Expand All @@ -157,13 +150,13 @@ def test_recv_one_or_none(self):

# no msg in queue, socket shouldn't block
recvd = messaging.recv_one_or_none(sub_sock)
self.assertTrue(recvd is None)
assert recvd is None

# one msg in queue
msg = random_carstate()
pub_sock.send(msg.to_bytes())
recvd = messaging.recv_one_or_none(sub_sock)
self.assertIsInstance(recvd, capnp._DynamicStructReader)
assert isinstance(recvd, capnp._DynamicStructReader)
assert_carstate(msg.carState, recvd.carState)

def test_recv_one_retry(self):
Expand All @@ -179,17 +172,14 @@ def test_recv_one_retry(self):
p = multiprocessing.Process(target=messaging.recv_one_retry, args=(sub_sock,))
p.start()
time.sleep(sock_timeout*15)
self.assertTrue(p.is_alive())
assert p.is_alive()
p.terminate()

# wait 15 socket timeouts before sending
msg = random_carstate()
delayed_send(sock_timeout*15, pub_sock, msg.to_bytes())
start_time = time.monotonic()
recvd = messaging.recv_one_retry(sub_sock)
self.assertGreaterEqual(time.monotonic() - start_time, sock_timeout*15)
self.assertIsInstance(recvd, capnp._DynamicStructReader)
assert (time.monotonic() - start_time) >= sock_timeout*15
assert isinstance(recvd, capnp._DynamicStructReader)
assert_carstate(msg.carState, recvd.carState)

if __name__ == "__main__":
unittest.main()
44 changes: 19 additions & 25 deletions cereal/messaging/tests/test_pub_sub_master.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
#!/usr/bin/env python3
import random
import time
from typing import Sized, cast
import unittest

import cereal.messaging as messaging
from cereal.messaging.tests.test_messaging import events, random_sock, random_socks, \
random_bytes, random_carstate, assert_carstate, \
zmq_sleep


class TestSubMaster(unittest.TestCase):
class TestSubMaster:

def setUp(self):
def setup_method(self):
# ZMQ pub socket takes too long to die
# sleep to prevent multiple publishers error between tests
zmq_sleep(3)
Expand All @@ -21,21 +19,21 @@ def test_init(self):
sm = messaging.SubMaster(events)
for p in [sm.updated, sm.recv_time, sm.recv_frame, sm.alive,
sm.sock, sm.data, sm.logMonoTime, sm.valid]:
self.assertEqual(len(cast(Sized, p)), len(events))
assert len(cast(Sized, p)) == len(events)

def test_init_state(self):
socks = random_socks()
sm = messaging.SubMaster(socks)
self.assertEqual(sm.frame, -1)
self.assertFalse(any(sm.updated.values()))
self.assertFalse(any(sm.alive.values()))
self.assertTrue(all(t == 0. for t in sm.recv_time.values()))
self.assertTrue(all(f == 0 for f in sm.recv_frame.values()))
self.assertTrue(all(t == 0 for t in sm.logMonoTime.values()))
assert sm.frame == -1
assert not any(sm.updated.values())
assert not any(sm.alive.values())
assert all(t == 0. for t in sm.recv_time.values())
assert all(f == 0 for f in sm.recv_frame.values())
assert all(t == 0 for t in sm.logMonoTime.values())

for p in [sm.updated, sm.recv_time, sm.recv_frame, sm.alive,
sm.sock, sm.data, sm.logMonoTime, sm.valid]:
self.assertEqual(len(cast(Sized, p)), len(socks))
assert len(cast(Sized, p)) == len(socks)

def test_getitem(self):
sock = "carState"
Expand All @@ -59,8 +57,8 @@ def test_update(self):
msg = messaging.new_message(sock)
pub_sock.send(msg.to_bytes())
sm.update(1000)
self.assertEqual(sm.frame, i)
self.assertTrue(all(sm.updated.values()))
assert sm.frame == i
assert all(sm.updated.values())

def test_update_timeout(self):
sock = random_sock()
Expand All @@ -70,9 +68,9 @@ def test_update_timeout(self):
start_time = time.monotonic()
sm.update(timeout)
t = time.monotonic() - start_time
self.assertGreaterEqual(t, timeout/1000.)
self.assertLess(t, 5)
self.assertFalse(any(sm.updated.values()))
assert t >= timeout/1000.
assert t < 5
assert not any(sm.updated.values())

def test_avg_frequency_checks(self):
for poll in (True, False):
Expand Down Expand Up @@ -118,12 +116,12 @@ def test_conflate(self):
pub_sock.send(msg.to_bytes())
time.sleep(0.01)
sm.update(1000)
self.assertEqual(sm[sock].vEgo, n)
assert sm[sock].vEgo == n


class TestPubMaster(unittest.TestCase):
class TestPubMaster:

def setUp(self):
def setup_method(self):
# ZMQ pub socket takes too long to die
# sleep to prevent multiple publishers error between tests
zmq_sleep(3)
Expand Down Expand Up @@ -156,8 +154,4 @@ def test_send(self):
if capnp:
msg.clear_write_flag()
msg = msg.to_bytes()
self.assertEqual(msg, recvd, i)


if __name__ == "__main__":
unittest.main()
assert msg == recvd, i
13 changes: 4 additions & 9 deletions cereal/messaging/tests/test_services.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
#!/usr/bin/env python3
import os
import tempfile
from typing import Dict
import unittest
from parameterized import parameterized

import cereal.services as services
from cereal.services import SERVICE_LIST


class TestServices(unittest.TestCase):
class TestServices:

@parameterized.expand(SERVICE_LIST.keys())
def test_services(self, s):
service = SERVICE_LIST[s]
self.assertTrue(service.frequency <= 104)
self.assertTrue(service.decimation != 0)
assert service.frequency <= 104
assert service.decimation != 0

def test_generated_header(self):
with tempfile.NamedTemporaryFile(suffix=".h") as f:
ret = os.system(f"python3 {services.__file__} > {f.name} && clang++ {f.name}")
self.assertEqual(ret, 0, "generated services header is not valid C")

if __name__ == "__main__":
unittest.main()
assert ret == 0, "generated services header is not valid C"
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ packages = [ "." ]

[tool.pytest.ini_options]
minversion = "6.0"
addopts = "--ignore=openpilot/ --ignore=cereal/ --ignore=opendbc/ --ignore=panda/ --ignore=rednose_repo/ --ignore=tinygrad_repo/ --ignore=teleoprtc_repo/ --ignore=msgq/ -Werror --strict-config --strict-markers --durations=10 -n auto --dist=loadgroup"
addopts = "--ignore=openpilot/ --ignore=opendbc/ --ignore=panda/ --ignore=rednose_repo/ --ignore=tinygrad_repo/ --ignore=teleoprtc_repo/ --ignore=msgq/ -Werror --strict-config --strict-markers --durations=10 -n auto --dist=loadgroup"
cpp_files = "test_*"
cpp_harness = "selfdrive/test/cpp_harness.py"
python_files = "test_*.py"
Expand Down

0 comments on commit 133f25e

Please sign in to comment.