Source code for selfdrive.controls.tests.test_alerts

#!/usr/bin/env python3
import copy
import json
import os
import unittest
import random
from PIL import Image, ImageDraw, ImageFont

from cereal import log, car
from cereal.messaging import SubMaster
from openpilot.common.basedir import BASEDIR
from openpilot.common.params import Params
from openpilot.selfdrive.controls.lib.events import Alert, EVENTS, ET
from openpilot.selfdrive.controls.lib.alertmanager import set_offroad_alert
from openpilot.selfdrive.test.process_replay.process_replay import CONFIGS

AlertSize = log.ControlsState.AlertSize

OFFROAD_ALERTS_PATH = os.path.join(BASEDIR, "selfdrive/controls/lib/alerts_offroad.json")

# TODO: add callback alerts
ALERTS = []
for event_types in EVENTS.values():
  for alert in event_types.values():
    ALERTS.append(alert)


[docs] class TestAlerts(unittest.TestCase):
[docs] @classmethod def setUpClass(cls): with open(OFFROAD_ALERTS_PATH) as f: cls.offroad_alerts = json.loads(f.read()) # Create fake objects for callback cls.CS = car.CarState.new_message() cls.CP = car.CarParams.new_message() cfg = [c for c in CONFIGS if c.proc_name == 'controlsd'][0] cls.sm = SubMaster(cfg.pubs)
[docs] def test_events_defined(self): # Ensure all events in capnp schema are defined in events.py events = car.CarEvent.EventName.schema.enumerants for name, e in events.items(): if not name.endswith("DEPRECATED"): fail_msg = "%s @%d not in EVENTS" % (name, e) self.assertTrue(e in EVENTS.keys(), msg=fail_msg)
# ensure alert text doesn't exceed allowed width
[docs] def test_alert_text_length(self): font_path = os.path.join(BASEDIR, "selfdrive/assets/fonts") regular_font_path = os.path.join(font_path, "Inter-SemiBold.ttf") bold_font_path = os.path.join(font_path, "Inter-Bold.ttf") semibold_font_path = os.path.join(font_path, "Inter-SemiBold.ttf") max_text_width = 2160 - 300 # full screen width is usable, minus sidebar draw = ImageDraw.Draw(Image.new('RGB', (0, 0))) fonts = { AlertSize.small: [ImageFont.truetype(semibold_font_path, 74)], AlertSize.mid: [ImageFont.truetype(bold_font_path, 88), ImageFont.truetype(regular_font_path, 66)], } for alert in ALERTS: if not isinstance(alert, Alert): alert = alert(self.CP, self.CS, self.sm, metric=False, soft_disable_time=100) # for full size alerts, both text fields wrap the text, # so it's unlikely that they would go past the max width if alert.alert_size in (AlertSize.none, AlertSize.full): continue for i, txt in enumerate([alert.alert_text_1, alert.alert_text_2]): if i >= len(fonts[alert.alert_size]): break font = fonts[alert.alert_size][i] left, _, right, _ = draw.textbbox((0, 0), txt, font) width = right - left msg = f"type: {alert.alert_type} msg: {txt}" self.assertLessEqual(width, max_text_width, msg=msg)
[docs] def test_alert_sanity_check(self): for event_types in EVENTS.values(): for event_type, a in event_types.items(): # TODO: add callback alerts if not isinstance(a, Alert): continue if a.alert_size == AlertSize.none: self.assertEqual(len(a.alert_text_1), 0) self.assertEqual(len(a.alert_text_2), 0) elif a.alert_size == AlertSize.small: self.assertGreater(len(a.alert_text_1), 0) self.assertEqual(len(a.alert_text_2), 0) elif a.alert_size == AlertSize.mid: self.assertGreater(len(a.alert_text_1), 0) self.assertGreater(len(a.alert_text_2), 0) else: self.assertGreater(len(a.alert_text_1), 0) self.assertGreaterEqual(a.duration, 0.) if event_type not in (ET.WARNING, ET.PERMANENT, ET.PRE_ENABLE): self.assertEqual(a.creation_delay, 0.)
[docs] def test_offroad_alerts(self): params = Params() for a in self.offroad_alerts: # set the alert alert = copy.copy(self.offroad_alerts[a]) set_offroad_alert(a, True) alert['extra'] = '' self.assertTrue(json.dumps(alert) == params.get(a, encoding='utf8')) # then delete it set_offroad_alert(a, False) self.assertTrue(params.get(a) is None)
[docs] def test_offroad_alerts_extra_text(self): params = Params() for i in range(50): # set the alert a = random.choice(list(self.offroad_alerts)) alert = self.offroad_alerts[a] set_offroad_alert(a, True, extra_text="a"*i) written_alert = json.loads(params.get(a, encoding='utf8')) self.assertTrue("a"*i == written_alert['extra']) self.assertTrue(alert["text"] == written_alert['text'])
if __name__ == "__main__": unittest.main()