Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 37 additions & 28 deletions opendbc/car/ford/tests/test_ford.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import random
from collections.abc import Iterable

from hypothesis import settings, given, strategies as st
from parameterized import parameterized

from opendbc.car.structs import CarParams
from opendbc.car.fw_versions import build_fw_dict
from opendbc.car.ford.values import CAR, FW_QUERY_CONFIG, FW_PATTERN, get_platform_codes
from opendbc.car.ford.fingerprints import FW_VERSIONS

Ecu = CarParams.Ecu

Expand Down Expand Up @@ -42,39 +35,50 @@

class TestFordFW:
def test_fw_query_config(self):
from opendbc.car.ford.values import FW_QUERY_CONFIG
for (ecu, addr, subaddr) in FW_QUERY_CONFIG.extra_ecus:
assert ecu in ECU_ADDRESSES, "Unknown ECU"
assert addr == ECU_ADDRESSES[ecu], "ECU address mismatch"
assert subaddr is None, "Unexpected ECU subaddress"

@parameterized.expand(FW_VERSIONS.items())
def test_fw_versions(self, car_model: str, fw_versions: dict[tuple[int, int, int | None], Iterable[bytes]]):
for (ecu, addr, subaddr), fws in fw_versions.items():
assert ecu in ECU_PART_NUMBER, "Unexpected ECU"
assert addr == ECU_ADDRESSES[ecu], "ECU address mismatch"
assert subaddr is None, "Unexpected ECU subaddress"
def test_fw_versions(self):
from opendbc.car.ford.values import FW_PATTERN, get_platform_codes
from opendbc.car.ford.fingerprints import FW_VERSIONS

for _car_model, fw_versions in FW_VERSIONS.items():
for (ecu, addr, subaddr), fws in fw_versions.items():
assert ecu in ECU_PART_NUMBER, "Unexpected ECU"
assert addr == ECU_ADDRESSES[ecu], "ECU address mismatch"
assert subaddr is None, "Unexpected ECU subaddress"

for fw in fws:
assert len(fw) == 24, "Expected ECU response to be 24 bytes"
for fw in fws:
assert len(fw) == 24, "Expected ECU response to be 24 bytes"

match = FW_PATTERN.match(fw)
assert match is not None, f"Unable to parse FW: {fw!r}"
if match:
part_number = match.group("part_number")
assert part_number in ECU_PART_NUMBER[ecu], f"Unexpected part number for {fw!r}"
match = FW_PATTERN.match(fw)
assert match is not None, f"Unable to parse FW: {fw!r}"
if match:
part_number = match.group("part_number")
assert part_number in ECU_PART_NUMBER[ecu], f"Unexpected part number for {fw!r}"

codes = get_platform_codes([fw])
assert 1 == len(codes), f"Unable to parse FW: {fw!r}"
codes = get_platform_codes([fw])
assert 1 == len(codes), f"Unable to parse FW: {fw!r}"

@settings(max_examples=100)
@given(data=st.data())
def test_platform_codes_fuzzy_fw(self, data):
def test_platform_codes_fuzzy_fw(self):
"""Ensure function doesn't raise an exception"""
fw_strategy = st.lists(st.binary())
fws = data.draw(fw_strategy)
get_platform_codes(fws)
from hypothesis import settings, given, strategies as st

@settings(max_examples=100)
@given(data=st.data())
def _test_impl(data):
from opendbc.car.ford.values import get_platform_codes
fw_strategy = st.lists(st.binary())
fws = data.draw(fw_strategy)
get_platform_codes(fws)

_test_impl()

def test_platform_codes_spot_check(self):
from opendbc.car.ford.values import get_platform_codes
# Asserts basic platform code parsing behavior for a few cases
results = get_platform_codes([
b"JX6A-14C204-BPL\x00\x00\x00\x00\x00\x00\x00\x00\x00",
Expand All @@ -85,6 +89,10 @@ def test_platform_codes_spot_check(self):
assert results == {(b"X6A", b"J"), (b"Z6T", b"N"), (b"J6T", b"P"), (b"B5A", b"L")}

def test_fuzzy_match(self):
from opendbc.car.ford.values import FW_QUERY_CONFIG
from opendbc.car.fw_versions import build_fw_dict
from opendbc.car.ford.fingerprints import FW_VERSIONS

for platform, fw_by_addr in FW_VERSIONS.items():
# Ensure there's no overlaps in platform codes
for _ in range(20):
Expand All @@ -100,6 +108,7 @@ def test_fuzzy_match(self):
assert matches == {platform}

def test_match_fw_fuzzy(self):
from opendbc.car.ford.values import CAR, FW_QUERY_CONFIG
offline_fw = {
(Ecu.eps, 0x730, None): [
b"L1MC-14D003-AJ\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
Expand Down
42 changes: 30 additions & 12 deletions opendbc/car/hyundai/tests/test_hyundai.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
from hypothesis import settings, given, strategies as st

import pytest

from opendbc.car import gen_empty_fingerprint
from opendbc.car.structs import CarParams
from opendbc.car.fw_versions import build_fw_dict
from opendbc.car.hyundai.interface import CarInterface
from opendbc.car.hyundai.hyundaicanfd import CanBus
from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR
from opendbc.car.hyundai.values import CAMERA_SCC_CAR, CANFD_CAR, CAN_GEARS, CAR, CHECKSUM, DATE_FW_ECUS, \
HYBRID_CAR, EV_CAR, FW_QUERY_CONFIG, LEGACY_SAFETY_MODE_CAR, CANFD_FUZZY_WHITELIST, \
UNSUPPORTED_LONGITUDINAL_CAR, PLATFORM_CODE_ECUS, HYUNDAI_VERSION_REQUEST_LONG, \
HyundaiFlags, get_platform_codes, HyundaiSafetyFlags
from opendbc.car.hyundai.fingerprints import FW_VERSIONS

Ecu = CarParams.Ecu

Expand Down Expand Up @@ -45,6 +39,8 @@

class TestHyundaiFingerprint:
def test_feature_detection(self):
from opendbc.car.hyundai.interface import CarInterface
from opendbc.car.hyundai.radar_interface import RADAR_START_ADDR
# LKA steering
for lka_steering in (True, False):
fingerprint = gen_empty_fingerprint()
Expand All @@ -63,6 +59,8 @@ def test_feature_detection(self):
assert CP.radarUnavailable != radar

def test_alternate_limits(self):
from opendbc.car.hyundai.interface import CarInterface

# Alternate lateral control limits, for high torque cars, verify Panda safety mode flag is set
fingerprint = gen_empty_fingerprint()
for car_model in CAR:
Expand All @@ -83,6 +81,8 @@ def test_hybrid_ev_sets(self):
assert CANFD_CAR & HYBRID_CAR == set(), "Hard coding CAN FD cars as hybrid is no longer supported"

def test_canfd_ecu_whitelist(self):
from opendbc.car.hyundai.fingerprints import FW_VERSIONS

# Asserts only expected Ecus can exist in database for CAN-FD cars
for car_model in CANFD_CAR:
ecus = {fw[0] for fw in FW_VERSIONS[car_model].keys()}
Expand All @@ -92,6 +92,8 @@ def test_canfd_ecu_whitelist(self):
f"{car_model}: Car model has unexpected ECUs: {ecu_strings}"

def test_blacklisted_parts(self, subtests):
from opendbc.car.hyundai.fingerprints import FW_VERSIONS

# Asserts no ECUs known to be shared across platforms exist in the database.
# Tucson having Santa Cruz camera and EPS for example
for car_model, ecus in FW_VERSIONS.items():
Expand All @@ -106,6 +108,8 @@ def test_blacklisted_parts(self, subtests):
assert not part.startswith(b'CW'), "Car has bad part number"

def test_correct_ecu_response_database(self, subtests):
from opendbc.car.hyundai.fingerprints import FW_VERSIONS

"""
Assert standard responses for certain ECUs, since they can
respond to multiple queries with different data
Expand All @@ -117,15 +121,22 @@ def test_correct_ecu_response_database(self, subtests):
assert all(fw.startswith(expected_fw_prefix) for fw in fws), \
f"FW from unexpected request in database: {(ecu, fws)}"

@settings(max_examples=100)
@given(data=st.data())
def test_platform_codes_fuzzy_fw(self, data):
def test_platform_codes_fuzzy_fw(self):
from hypothesis import settings, given, strategies as st

"""Ensure function doesn't raise an exception"""
fw_strategy = st.lists(st.binary())
fws = data.draw(fw_strategy)
get_platform_codes(fws)
@settings(max_examples=100)
@given(data=st.data())
def _test_impl(data):
fw_strategy = st.lists(st.binary())
fws = data.draw(fw_strategy)
get_platform_codes(fws)

_test_impl()

def test_expected_platform_codes(self, subtests):
from opendbc.car.hyundai.fingerprints import FW_VERSIONS

# Ensures we don't accidentally add multiple platform codes for a car unless it is intentional
for car_model, ecus in FW_VERSIONS.items():
with subtests.test(car_model=car_model.value):
Expand All @@ -145,6 +156,8 @@ def test_expected_platform_codes(self, subtests):
# Tests for platform codes, part numbers, and FW dates which Hyundai will use to fuzzy
# fingerprint in the absence of full FW matches:
def test_platform_code_ecus_available(self, subtests):
from opendbc.car.hyundai.fingerprints import FW_VERSIONS

# TODO: add queries for these non-CAN FD cars to get EPS
no_eps_platforms = CANFD_CAR | {CAR.KIA_SORENTO, CAR.KIA_OPTIMA_G4, CAR.KIA_OPTIMA_G4_FL, CAR.KIA_OPTIMA_H,
CAR.KIA_OPTIMA_H_G4_FL, CAR.HYUNDAI_SONATA_LF, CAR.HYUNDAI_TUCSON, CAR.GENESIS_G90, CAR.GENESIS_G80, CAR.HYUNDAI_ELANTRA}
Expand All @@ -160,6 +173,8 @@ def test_platform_code_ecus_available(self, subtests):
assert platform_code_ecu in [e[0] for e in ecus]

def test_fw_format(self, subtests):
from opendbc.car.hyundai.fingerprints import FW_VERSIONS

# Asserts:
# - every supported ECU FW version returns one platform code
# - every supported ECU FW version has a part number
Expand Down Expand Up @@ -219,6 +234,9 @@ def test_platform_codes_spot_check(self):
(b"ON-S9100", b"190405"), (b"ON-S9100", b"190720")}

def test_fuzzy_excluded_platforms(self):
from opendbc.car.hyundai.fingerprints import FW_VERSIONS
from opendbc.car.fw_versions import build_fw_dict

# Asserts a list of platforms that will not fuzzy fingerprint with platform codes due to them being shared.
# This list can be shrunk as we combine platforms and detect features
excluded_platforms = {
Expand Down
29 changes: 15 additions & 14 deletions opendbc/car/tests/test_can_fingerprint.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
import pytest
from opendbc.car.can_definitions import CanData
from opendbc.car.car_helpers import FRAME_FINGERPRINT, can_fingerprint
from opendbc.car.fingerprints import _FINGERPRINTS as FINGERPRINTS


class TestCanFingerprint:
@pytest.mark.parametrize("car_model, fingerprints", FINGERPRINTS.items())
def test_can_fingerprint(self, car_model, fingerprints):
def test_can_fingerprint(self):
"""Tests online fingerprinting function on offline fingerprints"""
from opendbc.car.car_helpers import can_fingerprint
from opendbc.car.fingerprints import _FINGERPRINTS as FINGERPRINTS

for fingerprint in fingerprints: # can have multiple fingerprints for each platform
can = [CanData(address=address, dat=b'\x00' * length, src=src)
for address, length in fingerprint.items() for src in (0, 1)]
for car_model, fingerprints in FINGERPRINTS.items():
for fingerprint in fingerprints: # can have multiple fingerprints for each platform
can = [CanData(address=address, dat=b'\x00' * length, src=src)
for address, length in fingerprint.items() for src in (0, 1)]

fingerprint_iter = iter([can])
car_fingerprint, finger = can_fingerprint(lambda **kwargs: [next(fingerprint_iter, [])]) # noqa: B023
fingerprint_iter = iter([can])
car_fingerprint, finger = can_fingerprint(lambda **kwargs: [next(fingerprint_iter, [])]) # noqa: B023

assert car_fingerprint == car_model
assert finger[0] == fingerprint
assert finger[1] == fingerprint
assert finger[2] == {}
assert car_fingerprint == car_model
assert finger[0] == fingerprint
assert finger[1] == fingerprint
assert finger[2] == {}

def test_timing(self, subtests):
from opendbc.car.car_helpers import FRAME_FINGERPRINT, can_fingerprint
from opendbc.car.fingerprints import _FINGERPRINTS as FINGERPRINTS
# just pick any CAN fingerprinting car
car_model = "CHEVROLET_BOLT_EUV"
fingerprint = FINGERPRINTS[car_model][0]
Expand Down
Loading