mirror of
https://github.com/MoreTore/openpilot.git
synced 2026-08-05 16:26:14 +08:00
test: car interface fuzzy testing + generating capnp structs (#28530)
* random car control * format * struct generation * math * staying real * really staying real * move * split this * format * Revert "format" This reverts commit a70a73952ee3833c4ae839d7b2729ee2a1e1a85b. * Revert "split this" This reverts commit ae96be63cbfbee230101e69a0f84c874f321fafa. * space
This commit is contained in:
@@ -290,6 +290,7 @@ selfdrive/thermald/power_monitoring.py
|
||||
selfdrive/thermald/fan_controller.py
|
||||
|
||||
selfdrive/test/__init__.py
|
||||
selfdrive/test/fuzzy_generation.py
|
||||
selfdrive/test/helpers.py
|
||||
selfdrive/test/setup_device_ci.sh
|
||||
selfdrive/test/test_onroad.py
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
import math
|
||||
import unittest
|
||||
from hypothesis import given, settings
|
||||
import importlib
|
||||
from parameterized import parameterized
|
||||
|
||||
@@ -8,12 +9,15 @@ from cereal import car
|
||||
from selfdrive.car import gen_empty_fingerprint
|
||||
from selfdrive.car.car_helpers import interfaces
|
||||
from selfdrive.car.fingerprints import _FINGERPRINTS as FINGERPRINTS, all_known_cars
|
||||
from selfdrive.test.fuzzy_generation import get_random_msg
|
||||
|
||||
|
||||
class TestCarInterfaces(unittest.TestCase):
|
||||
|
||||
@parameterized.expand([(car,) for car in all_known_cars()])
|
||||
def test_car_interfaces(self, car_name):
|
||||
@settings(max_examples=5)
|
||||
@given(cc_msg=get_random_msg(car.CarControl, real_floats=True))
|
||||
def test_car_interfaces(self, car_name, cc_msg):
|
||||
if car_name in FINGERPRINTS:
|
||||
fingerprint = FINGERPRINTS[car_name][0]
|
||||
else:
|
||||
@@ -57,13 +61,13 @@ class TestCarInterfaces(unittest.TestCase):
|
||||
self.assertTrue(len(tune.indi.outerLoopGainV))
|
||||
|
||||
# Run car interface
|
||||
CC = car.CarControl.new_message()
|
||||
CC = car.CarControl.new_message(**cc_msg)
|
||||
for _ in range(10):
|
||||
car_interface.update(CC, [])
|
||||
car_interface.apply(CC, 0)
|
||||
car_interface.apply(CC, 0)
|
||||
|
||||
CC = car.CarControl.new_message()
|
||||
CC = car.CarControl.new_message(**cc_msg)
|
||||
CC.enabled = True
|
||||
for _ in range(10):
|
||||
car_interface.update(CC, [])
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import hypothesis.strategies as st
|
||||
import random
|
||||
|
||||
class FuzzyGenerator:
|
||||
def __init__(self, real_floats):
|
||||
self.real_floats=real_floats
|
||||
|
||||
def generate_native_type(self, field):
|
||||
def floats(**kwargs):
|
||||
allow_nan = not self.real_floats
|
||||
allow_infinity = not self.real_floats
|
||||
return st.floats(**kwargs, allow_nan=allow_nan, allow_infinity=allow_infinity)
|
||||
|
||||
if field == 'bool':
|
||||
return st.booleans()
|
||||
elif field == 'int8':
|
||||
return st.integers(min_value=-2**7, max_value=2**7-1)
|
||||
elif field == 'int16':
|
||||
return st.integers(min_value=-2**15, max_value=2**15-1)
|
||||
elif field == 'int32':
|
||||
return st.integers(min_value=-2**31, max_value=2**31-1)
|
||||
elif field == 'int64':
|
||||
return st.integers(min_value=-2**63, max_value=2**63-1)
|
||||
elif field == 'uint8':
|
||||
return st.integers(min_value=0, max_value=2**8-1)
|
||||
elif field == 'uint16':
|
||||
return st.integers(min_value=0, max_value=2**16-1)
|
||||
elif field == 'uint32':
|
||||
return st.integers(min_value=0, max_value=2**32-1)
|
||||
elif field == 'uint64':
|
||||
return st.integers(min_value=0, max_value=2**64-1)
|
||||
elif field == 'float32':
|
||||
return floats(width=32)
|
||||
elif field == 'float64':
|
||||
return floats(width=64)
|
||||
elif field == 'text':
|
||||
return st.text(max_size=1000)
|
||||
elif field == 'data':
|
||||
return st.text(max_size=1000)
|
||||
elif field == 'anyPointer':
|
||||
return st.text()
|
||||
else:
|
||||
raise NotImplementedError(f'Invalid type : {field}')
|
||||
|
||||
def generate_field(self, field):
|
||||
def rec(field_type):
|
||||
if field_type.which() == 'struct':
|
||||
return self.generate_struct(field.schema.elementType if base_type == 'list' else field.schema)
|
||||
elif field_type.which() == 'list':
|
||||
return st.lists(rec(field_type.list.elementType))
|
||||
elif field_type.which() == 'enum':
|
||||
schema = field.schema.elementType if base_type == 'list' else field.schema
|
||||
return st.sampled_from(list(schema.enumerants.keys()))
|
||||
else:
|
||||
return self.generate_native_type(field_type.which())
|
||||
|
||||
if 'slot' in field.proto.to_dict():
|
||||
base_type = field.proto.slot.type.which()
|
||||
return rec(field.proto.slot.type)
|
||||
else:
|
||||
return self.generate_struct(field.schema)
|
||||
|
||||
def generate_struct(self, schema):
|
||||
full_fill = list(schema.non_union_fields) if schema.non_union_fields else []
|
||||
single_fill = [random.choice(schema.union_fields)] if schema.union_fields else []
|
||||
return st.fixed_dictionaries(dict((field, self.generate_field(schema.fields[field])) for field in full_fill + single_fill))
|
||||
|
||||
def get_random_msg(struct, real_floats=False):
|
||||
return FuzzyGenerator(real_floats=real_floats).generate_struct(struct.schema)
|
||||
Reference in New Issue
Block a user