* WIP sound test

* it does something

* refactor

* phone only

* update release files

* test sound card init

* add to CI

* check writes

* increase time

* unused

* only build cereal

* small tolerance

Co-authored-by: Comma Device <device@comma.ai>
old-commit-hash: f1afb3e3ae
commatwo_master
Adeeb Shihadeh 5 years ago committed by GitHub
parent 06516d0f4c
commit 0d79150606
  1. 13
      Jenkinsfile
  2. 4
      common/android.py
  3. 8
      common/testing.py
  4. 2
      release/files_common
  5. 5
      selfdrive/controls/controlsd.py
  6. 56
      selfdrive/test/helpers.py
  7. 2
      selfdrive/test/phone_ci.py
  8. 59
      selfdrive/test/test_openpilot.py
  9. 68
      selfdrive/test/test_sounds.py

13
Jenkinsfile vendored

@ -40,6 +40,19 @@ pipeline {
} }
} }
stage('Sound Test') {
steps {
lock(resource: "", label: 'eon', inversePrecedence: true, variable: 'eon_ip', quantity: 1){
timeout(time: 30, unit: 'MINUTES') {
dir(path: 'selfdrive/test') {
sh 'pip install paramiko'
sh 'python phone_ci.py "SCONS_CACHE=1 scons -j3 cereal/ && cd selfdrive/test && nosetests -s test_sounds.py"'
}
}
}
}
}
} }
} }
} }

@ -12,6 +12,10 @@ NetworkStrength = log.ThermalData.NetworkStrength
ANDROID = os.path.isfile('/EON') ANDROID = os.path.isfile('/EON')
def get_sound_card_online():
return (os.path.isfile('/proc/asound/card0/state') and
open('/proc/asound/card0/state').read().strip() == 'ONLINE')
def getprop(key): def getprop(key):
if not ANDROID: if not ANDROID:
return "" return ""

@ -1,8 +0,0 @@
import os
from nose.tools import nottest
def phone_only(x):
if os.path.isfile("/init.qcom.rc"):
return x
else:
return nottest(x)

@ -26,7 +26,6 @@ common/numpy_fast.py
common/params.py common/params.py
common/xattr.py common/xattr.py
common/profiler.py common/profiler.py
common/testing.py
common/basedir.py common/basedir.py
common/filter_simple.py common/filter_simple.py
common/stat_live.py common/stat_live.py
@ -316,6 +315,7 @@ selfdrive/thermald/thermald.py
selfdrive/thermald/power_monitoring.py selfdrive/thermald/power_monitoring.py
selfdrive/test/__init__.py selfdrive/test/__init__.py
selfdrive/test/helpers.py
selfdrive/test/test_openpilot.py selfdrive/test/test_openpilot.py
selfdrive/test/test_fingerprints.py selfdrive/test/test_fingerprints.py
selfdrive/test/test_car_models.py selfdrive/test/test_car_models.py

@ -2,7 +2,7 @@
import os import os
import gc import gc
from cereal import car, log from cereal import car, log
from common.android import ANDROID from common.android import ANDROID, get_sound_card_online
from common.numpy_fast import clip from common.numpy_fast import clip
from common.realtime import sec_since_boot, set_realtime_priority, set_core_affinity, Ratekeeper, DT_CTRL from common.realtime import sec_since_boot, set_realtime_priority, set_core_affinity, Ratekeeper, DT_CTRL
from common.profiler import Profiler from common.profiler import Profiler
@ -79,8 +79,7 @@ class Controls:
internet_needed or not openpilot_enabled_toggle internet_needed or not openpilot_enabled_toggle
# detect sound card presence and ensure successful init # detect sound card presence and ensure successful init
sounds_available = (not ANDROID or (os.path.isfile('/proc/asound/card0/state') and sounds_available = not ANDROID or get_sound_card_online()
open('/proc/asound/card0/state').read().strip() == 'ONLINE'))
car_recognized = self.CP.carName != 'mock' car_recognized = self.CP.carName != 'mock'
# If stock camera is disconnected, we loaded car controls and it's not dashcam mode # If stock camera is disconnected, we loaded car controls and it's not dashcam mode

@ -0,0 +1,56 @@
import subprocess
from functools import wraps
from nose.tools import nottest
from common.android import ANDROID
from common.apk import update_apks, start_offroad, pm_apply_packages, android_packages
from selfdrive.manager import start_managed_process, kill_managed_process, get_running
def phone_only(x):
if ANDROID:
return x
else:
return nottest(x)
def with_processes(processes):
def wrapper(func):
@wraps(func)
def wrap():
# start and assert started
[start_managed_process(p) for p in processes]
assert all(get_running()[name].exitcode is None for name in processes)
# call the function
try:
func()
# assert processes are still started
assert all(get_running()[name].exitcode is None for name in processes)
finally:
# kill and assert all stopped
[kill_managed_process(p) for p in processes]
assert len(get_running()) == 0
return wrap
return wrapper
def with_apks():
def wrapper(func):
@wraps(func)
def wrap():
update_apks()
pm_apply_packages('enable')
start_offroad()
func()
try:
for package in android_packages:
apk_is_running = (subprocess.call(["pidof", package]) == 0)
assert apk_is_running, package
finally:
pm_apply_packages('disable')
for package in android_packages:
apk_is_not_running = (subprocess.call(["pidof", package]) == 1)
assert apk_is_not_running, package
return wrap
return wrapper

@ -69,7 +69,7 @@ def run_on_phone(test_cmd):
conn.send("exit\n") conn.send("exit\n")
dat = b"" dat = b""
conn.settimeout(150) conn.settimeout(240)
while True: while True:
try: try:

@ -2,77 +2,22 @@
import os import os
os.environ['FAKEUPLOAD'] = "1" os.environ['FAKEUPLOAD'] = "1"
from common.apk import update_apks, start_offroad, pm_apply_packages, android_packages
from common.params import Params from common.params import Params
from common.realtime import sec_since_boot from common.realtime import sec_since_boot
from common.testing import phone_only from selfdrive.manager import manager_init, manager_prepare, start_daemon_process
from selfdrive.manager import manager_init, manager_prepare from selfdrive.test.helpers import phone_only, with_processes
from selfdrive.manager import start_managed_process, kill_managed_process, get_running
from selfdrive.manager import start_daemon_process
from functools import wraps
import json import json
import requests import requests
import signal import signal
import subprocess import subprocess
import time import time
DID_INIT = False
# must run first # must run first
@phone_only @phone_only
def test_manager_prepare(): def test_manager_prepare():
global DID_INIT
manager_init() manager_init()
manager_prepare() manager_prepare()
DID_INIT = True
def with_processes(processes):
def wrapper(func):
@wraps(func)
def wrap():
if not DID_INIT:
test_manager_prepare()
# start and assert started
[start_managed_process(p) for p in processes]
assert all(get_running()[name].exitcode is None for name in processes)
# call the function
try:
func()
# assert processes are still started
assert all(get_running()[name].exitcode is None for name in processes)
finally:
# kill and assert all stopped
[kill_managed_process(p) for p in processes]
assert len(get_running()) == 0
return wrap
return wrapper
def with_apks():
def wrapper(func):
@wraps(func)
def wrap():
if not DID_INIT:
test_manager_prepare()
update_apks()
pm_apply_packages('enable')
start_offroad()
func()
try:
for package in android_packages:
apk_is_running = (subprocess.call(["pidof", package]) == 0)
assert apk_is_running, package
finally:
pm_apply_packages('disable')
for package in android_packages:
apk_is_not_running = (subprocess.call(["pidof", package]) == 1)
assert apk_is_not_running, package
return wrap
return wrapper
@phone_only @phone_only
@with_processes(['loggerd', 'logmessaged', 'tombstoned', 'proclogd', 'logcatd']) @with_processes(['loggerd', 'logmessaged', 'tombstoned', 'proclogd', 'logcatd'])

@ -0,0 +1,68 @@
#!/usr/bin/env python3
import time
import subprocess
from cereal import car
import cereal.messaging as messaging
from selfdrive.test.helpers import phone_only, with_processes
from common.android import get_sound_card_online
from common.realtime import DT_CTRL
AudibleAlert = car.CarControl.HUDControl.AudibleAlert
SOUNDS = {
# sound: total writes
AudibleAlert.none: 0,
AudibleAlert.chimeEngage: 85,
AudibleAlert.chimeDisengage: 85,
AudibleAlert.chimeError: 85,
AudibleAlert.chimePrompt: 85,
AudibleAlert.chimeWarning1: 80,
AudibleAlert.chimeWarning2: 107,
AudibleAlert.chimeWarningRepeat: 134,
AudibleAlert.chimeWarning2Repeat: 177,
}
def get_total_writes():
audio_flinger = subprocess.check_output('dumpsys media.audio_flinger', shell=True, encoding='utf-8').strip()
write_lines = [l for l in audio_flinger.split('\n') if l.strip().startswith('Total writes')]
return sum([int(l.split(':')[1]) for l in write_lines])
@phone_only
def test_sound_card_init():
assert get_sound_card_online()
@phone_only
@with_processes(['ui', 'camerad'])
def test_alert_sounds():
pm = messaging.PubMaster(['thermal', 'controlsState'])
# make sure they're all defined
alert_sounds = {v: k for k, v in car.CarControl.HUDControl.AudibleAlert.schema.enumerants.items()}
diff = set(SOUNDS.keys()).symmetric_difference(alert_sounds.keys())
assert len(diff) == 0, f"not all sounds defined in test: {diff}"
# wait for procs to init
time.sleep(5)
msg = messaging.new_message('thermal')
msg.thermal.started = True
pm.send('thermal', msg)
for sound, expected_writes in SOUNDS.items():
print(f"testing {alert_sounds[sound]}")
start_writes = get_total_writes()
for _ in range(int(9 / DT_CTRL)):
msg = messaging.new_message('controlsState')
msg.controlsState.enabled = True
msg.controlsState.active = True
msg.controlsState.alertSound = sound
msg.controlsState.alertType = str(sound)
pm.send('controlsState', msg)
time.sleep(DT_CTRL)
actual_writes = get_total_writes() - start_writes
assert abs(expected_writes - actual_writes) <= 2, f"{alert_sounds[sound]}: expected {expected_writes} writes, got {actual_writes}"
Loading…
Cancel
Save