Files

134 lines
4.0 KiB
Python

import atexit
import os
import signal
import unittest
from panda import Panda, PandaDFU, PandaJungle
from panda.tests.hitl.helpers import clear_can_buffers
SPEED_NORMAL = 500
BUS_SPEEDS = [(0, SPEED_NORMAL), (1, SPEED_NORMAL), (2, SPEED_NORMAL)]
# test options
NO_JUNGLE = os.environ.get("NO_JUNGLE", "0") == "1"
# Find all pandas connected
_panda_jungle = None
_panda_type = None
_panda_serial = None
def init_devices():
if not NO_JUNGLE:
global _panda_jungle
_panda_jungle = PandaJungle()
_panda_jungle.set_panda_power(True)
with Panda(serial=None, claim=False) as p:
global _panda_type
global _panda_serial
_panda_serial = p.get_usb_serial()
_panda_type = bytes(p.get_type())
assert _panda_serial is not None, "No panda found!"
def init_jungle():
if _panda_jungle is None:
return
clear_can_buffers(_panda_jungle)
_panda_jungle.set_panda_power(True)
_panda_jungle.set_can_loopback(False)
_panda_jungle.set_obd(False)
_panda_jungle.set_harness_orientation(PandaJungle.HARNESS_ORIENTATION_1)
for bus, speed in BUS_SPEEDS:
_panda_jungle.set_can_speed_kbps(bus, speed)
def panda_test(**options):
def decorate(test):
test.panda_options = {**getattr(test, "panda_options", {}), **options}
return test
return decorate
@unittest.skipUnless(os.environ.get("HITL") == "1", "hardware tests require HITL=1")
class PandaTestCase(unittest.TestCase):
panda_types = None
@classmethod
def setUpClass(cls):
if _panda_serial is None:
init_devices()
if _panda_jungle is not None:
atexit.register(_panda_jungle.close)
# init jungle
init_jungle()
# init panda
assert Panda.wait_for_panda(_panda_serial, timeout=10), "panda not found"
p = Panda(serial=_panda_serial)
cls.addClassCleanup(p.close)
cls.p = p
p.reset(reconnect=True)
p.set_can_loopback(False)
p.set_power_save(False)
for bus, speed in BUS_SPEEDS:
p.set_can_speed_kbps(bus, speed)
clear_can_buffers(p)
p.set_power_save(False)
def setUp(self):
self.options = getattr(getattr(self, self._testMethodName), "panda_options", {})
panda_types = self.options.get("panda_types", self.panda_types)
if panda_types is not None and _panda_type not in panda_types:
self.skipTest(f"Not applicable, {panda_types} pandas only")
if self.options.get("needs_jungle", False) and NO_JUNGLE:
self.skipTest("skipping tests that require a jungle")
previous_handler = signal.signal(signal.SIGALRM, self._timeout)
self.addCleanup(signal.signal, signal.SIGALRM, previous_handler)
self.addCleanup(signal.setitimer, signal.ITIMER_REAL, 0)
signal.setitimer(signal.ITIMER_REAL, self.options.get("timeout", 60))
if self.options.get("needs_jungle", False):
init_jungle()
self.panda_jungle = _panda_jungle
self.p.reset()
assert self.p.up_to_date()
def _timeout(self, signum, frame):
raise TimeoutError(f"{self.id()} exceeded its test timeout")
def tearDown(self):
p = self.p
# reconnect
if p.get_dfu_serial() in PandaDFU.list():
PandaDFU(p.get_dfu_serial()).reset()
p.reconnect()
if not p.connected:
p.reconnect()
if p.bootstub:
p.reset()
assert not p.bootstub
# Check for faults
assert p.health()['faults'] == 0
assert p.health()['fault_status'] == 0
# Check for SPI errors
#assert p.health()['spi_error_count'] == 0
# Check health of each CAN core after test, normal to fail for test_gen2_loopback on OBD bus, so skipping
if not self.options.get("expect_can_error", False):
for i in range(3):
can_health = p.can_health(i)
assert can_health['bus_off_cnt'] == 0
assert can_health['receive_error_cnt'] < 127
assert can_health['transmit_error_cnt'] < 255
assert can_health['error_passive'] == 0
assert can_health['error_warning'] == 0
assert can_health['total_rx_lost_cnt'] == 0
assert can_health['total_tx_lost_cnt'] == 0
assert can_health['total_error_cnt'] == 0
assert can_health['total_tx_checksum_error_cnt'] == 0