diff --git a/tests/safety/common.py b/tests/safety/common.py index 71bdff0e..91da3da4 100644 --- a/tests/safety/common.py +++ b/tests/safety/common.py @@ -131,6 +131,40 @@ class InterceptorSafetyTest(PandaSafetyTestBase): self.assertEqual(send, self._tx(self._interceptor_gas_cmd(gas))) +class LongitudinalAccelSafetyTest(PandaSafetyTestBase, abc.ABC): + + MIN_ACCEL: float = 2.0 + MAX_ACCEL: float = -3.5 + INACTIVE_ACCEL: float = 0.0 + + @classmethod + def setUpClass(cls): + if cls.__name__ == "LongitudinalAccelSafetyTest": + cls.safety = None + raise unittest.SkipTest + + @abc.abstractmethod + def _accel_msg(self, accel: float): + pass + + def test_accel_actuation_limits(self, stock_longitudinal=False): + limits = ((self.MIN_ACCEL, self.MAX_ACCEL, ALTERNATIVE_EXPERIENCE.DEFAULT), + (self.MIN_ACCEL, self.MAX_ACCEL, ALTERNATIVE_EXPERIENCE.RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX)) + + for min_accel, max_accel, alternative_experience in limits: + for accel in np.arange(min_accel - 1, max_accel + 1, 0.05): + for controls_allowed in [True, False]: + self.safety.set_controls_allowed(controls_allowed) + self.safety.set_alternative_experience(alternative_experience) + if stock_longitudinal: + should_tx = False + elif controls_allowed: + should_tx = int(min_accel * 1000) <= int(accel * 1000) <= int(max_accel * 1000) + else: + should_tx = np.isclose(accel, self.INACTIVE_ACCEL, atol=0.0001) + self.assertEqual(should_tx, self._tx(self._accel_msg(accel))) + + class TorqueSteeringSafetyTestBase(PandaSafetyTestBase, abc.ABC): MAX_RATE_UP = 0 diff --git a/tests/safety/hyundai_common.py b/tests/safety/hyundai_common.py index dac8e4a6..fde8f77c 100644 --- a/tests/safety/hyundai_common.py +++ b/tests/safety/hyundai_common.py @@ -1,6 +1,7 @@ -import numpy as np from typing import Tuple +import unittest +import panda.tests.safety.common as common from panda.tests.libpanda import libpanda_py from panda.tests.safety.common import make_msg @@ -12,8 +13,6 @@ class Buttons: CANCEL = 4 -MAX_ACCEL = 2.0 -MIN_ACCEL = -3.5 PREV_BUTTON_SAMPLES = 8 ENABLE_BUTTONS = (Buttons.RESUME, Buttons.SET, Buttons.CANCEL) @@ -75,12 +74,18 @@ class HyundaiButtonBase: self._rx(self._button_msg(Buttons.NONE)) -class HyundaiLongitudinalBase: +class HyundaiLongitudinalBase(common.LongitudinalAccelSafetyTest): # pylint: disable=no-member,abstract-method DISABLED_ECU_UDS_MSG: Tuple[int, int] DISABLED_ECU_ACTUATION_MSG: Tuple[int, int] + @classmethod + def setUpClass(cls): + if cls.__name__ == "HyundaiLongitudinalBase": + cls.safety = None + raise unittest.SkipTest + # override these tests from PandaSafetyTest, hyundai longitudinal uses button enable def test_disable_control_allowed_from_cruise(self): pass @@ -128,14 +133,6 @@ class HyundaiLongitudinalBase: self._rx(self._button_msg(Buttons.CANCEL)) self.assertFalse(self.safety.get_controls_allowed()) - def test_accel_safety_check(self): - for controls_allowed in [True, False]: - for accel in np.arange(MIN_ACCEL - 1, MAX_ACCEL + 1, 0.01): - accel = round(accel, 2) # floats might not hit exact boundary conditions without rounding - self.safety.set_controls_allowed(controls_allowed) - send = MIN_ACCEL <= accel <= MAX_ACCEL if controls_allowed else accel == 0 - self.assertEqual(send, self._tx(self._accel_msg(accel)), (controls_allowed, accel)) - def test_tester_present_allowed(self): """ Ensure tester present diagnostic message is allowed to keep ECU knocked out diff --git a/tests/safety/test_toyota.py b/tests/safety/test_toyota.py index 0a6cb873..a0ba9c11 100755 --- a/tests/safety/test_toyota.py +++ b/tests/safety/test_toyota.py @@ -7,10 +7,7 @@ import itertools from panda import Panda from panda.tests.libpanda import libpanda_py import panda.tests.safety.common as common -from panda.tests.safety.common import CANPackerPanda, make_msg, ALTERNATIVE_EXPERIENCE - -MAX_ACCEL = 2.0 -MIN_ACCEL = -3.5 +from panda.tests.safety.common import CANPackerPanda, make_msg def interceptor_msg(gas, addr): @@ -22,7 +19,8 @@ def interceptor_msg(gas, addr): return to_send -class TestToyotaSafetyBase(common.PandaSafetyTest, common.InterceptorSafetyTest): +class TestToyotaSafetyBase(common.PandaSafetyTest, common.InterceptorSafetyTest, + common.LongitudinalAccelSafetyTest): TX_MSGS = [[0x283, 0], [0x2E6, 0], [0x2E7, 0], [0x33E, 0], [0x344, 0], [0x365, 0], [0x366, 0], [0x4CB, 0], # DSU bus 0 [0x128, 1], [0x141, 1], [0x160, 1], [0x161, 1], [0x470, 1], # DSU bus 1 @@ -94,23 +92,6 @@ class TestToyotaSafetyBase(common.PandaSafetyTest, common.InterceptorSafetyTest) msg = libpanda_py.make_CANPacket(0x283, 0, bytes(dat)) self.assertEqual(not bad, self._tx(msg)) - def test_accel_actuation_limits(self, stock_longitudinal=False): - limits = ((MIN_ACCEL, MAX_ACCEL, ALTERNATIVE_EXPERIENCE.DEFAULT), - (MIN_ACCEL, MAX_ACCEL, ALTERNATIVE_EXPERIENCE.RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX)) - - for min_accel, max_accel, alternative_experience in limits: - for accel in np.arange(min_accel - 1, max_accel + 1, 0.1): - for controls_allowed in [True, False]: - self.safety.set_controls_allowed(controls_allowed) - self.safety.set_alternative_experience(alternative_experience) - if stock_longitudinal: - should_tx = False - elif controls_allowed: - should_tx = int(min_accel * 1000) <= int(accel * 1000) <= int(max_accel * 1000) - else: - should_tx = np.isclose(accel, 0, atol=0.0001) - self.assertEqual(should_tx, self._tx(self._accel_msg(accel))) - # Only allow LTA msgs with no actuation def test_lta_steer_cmd(self): for engaged, req, req2, setme_x64, angle in itertools.product([True, False], @@ -211,7 +192,7 @@ class TestToyotaStockLongitudinalBase(TestToyotaSafetyBase): """ for controls_allowed in [True, False]: self.safety.set_controls_allowed(controls_allowed) - for accel in np.arange(MIN_ACCEL - 1, MAX_ACCEL + 1, 0.1): + for accel in np.arange(self.MIN_ACCEL - 1, self.MAX_ACCEL + 1, 0.1): self.assertFalse(self._tx(self._accel_msg(accel))) should_tx = np.isclose(accel, 0, atol=0.0001) self.assertEqual(should_tx, self._tx(self._accel_msg(accel, cancel_req=1))) diff --git a/tests/safety/test_volkswagen_pq.py b/tests/safety/test_volkswagen_pq.py index 563d5de0..be71f1d7 100755 --- a/tests/safety/test_volkswagen_pq.py +++ b/tests/safety/test_volkswagen_pq.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -import numpy as np import unittest from panda import Panda from panda.tests.libpanda import libpanda_py @@ -17,8 +16,6 @@ MSG_MOTOR_5 = 0x480 # RX from ECU, for ACC main switch state MSG_ACC_GRA_ANZEIGE = 0x56A # TX by OP, ACC HUD MSG_LDW_1 = 0x5BE # TX by OP, Lane line recognition and text alerts -MAX_ACCEL = 2.0 -MIN_ACCEL = -3.5 class TestVolkswagenPqSafety(common.PandaSafetyTest, common.DriverTorqueSteeringSafetyTest): cruise_engaged = False @@ -142,7 +139,7 @@ class TestVolkswagenPqStockSafety(TestVolkswagenPqSafety): self.assertTrue(self._tx(self._button_msg(resume=True))) -class TestVolkswagenPqLongSafety(TestVolkswagenPqSafety): +class TestVolkswagenPqLongSafety(TestVolkswagenPqSafety, common.LongitudinalAccelSafetyTest): TX_MSGS = [[MSG_HCA_1, 0], [MSG_LDW_1, 0], [MSG_ACC_SYSTEM, 0], [MSG_ACC_GRA_ANZEIGE, 0]] FWD_BLACKLISTED_ADDRS = {2: [MSG_HCA_1, MSG_LDW_1, MSG_ACC_SYSTEM, MSG_ACC_GRA_ANZEIGE]} FWD_BUS_LOOKUP = {0: 2, 2: 0} @@ -192,15 +189,6 @@ class TestVolkswagenPqLongSafety(TestVolkswagenPqSafety): self._rx(self._motor_5_msg(main_switch=False)) self.assertFalse(self.safety.get_controls_allowed(), "controls allowed after ACC main switch off") - def test_accel_safety_check(self): - for controls_allowed in [True, False]: - for accel in np.arange(MIN_ACCEL - 2, MAX_ACCEL + 2, 0.005): - accel = round(accel, 2) # floats might not hit exact boundary conditions without rounding - send = MIN_ACCEL <= accel <= MAX_ACCEL if controls_allowed else accel == self.INACTIVE_ACCEL - self.safety.set_controls_allowed(controls_allowed) - # primary accel request used by ECU - self.assertEqual(send, self._tx(self._accel_msg(accel)), (controls_allowed, accel)) - if __name__ == "__main__": unittest.main()