Sound test (#1820)

* 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>
pull/1825/head
Adeeb Shihadeh 2020-07-05 17:56:24 -07:00 committed by GitHub
parent 8cadecae8f
commit f1afb3e3ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 147 additions and 70 deletions

13
Jenkinsfile vendored
View File

@ -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"'
}
}
}
}
}
}
}
}

View File

@ -12,6 +12,10 @@ NetworkStrength = log.ThermalData.NetworkStrength
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):
if not ANDROID:
return ""

View File

@ -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)

View File

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

View File

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

View File

@ -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

View File

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

View File

@ -2,77 +2,22 @@
import os
os.environ['FAKEUPLOAD'] = "1"
from common.apk import update_apks, start_offroad, pm_apply_packages, android_packages
from common.params import Params
from common.realtime import sec_since_boot
from common.testing import phone_only
from selfdrive.manager import manager_init, manager_prepare
from selfdrive.manager import start_managed_process, kill_managed_process, get_running
from selfdrive.manager import start_daemon_process
from functools import wraps
from selfdrive.manager import manager_init, manager_prepare, start_daemon_process
from selfdrive.test.helpers import phone_only, with_processes
import json
import requests
import signal
import subprocess
import time
DID_INIT = False
# must run first
@phone_only
def test_manager_prepare():
global DID_INIT
manager_init()
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
@with_processes(['loggerd', 'logmessaged', 'tombstoned', 'proclogd', 'logcatd'])

View File

@ -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}"