From aeba310800a09bff473913c3f38c6f7c352d9d48 Mon Sep 17 00:00:00 2001 From: deanlee Date: Sun, 7 Nov 2021 01:20:43 +0800 Subject: [PATCH] Revert "remove old test files" This reverts commit 12e8bfefae1ae7f8649d3eda1e4126ff8eb1fe17. --- Jenkinsfile | 1 + selfdrive/ui/SConscript | 2 + selfdrive/ui/tests/.gitignore | 1 + selfdrive/ui/tests/playsound.cc | 30 ++++++++++ selfdrive/ui/tests/test_sound_stability.py | 49 +++++++++++++++ selfdrive/ui/tests/test_soundd.py | 69 ++++++++++++++++++++++ 6 files changed, 152 insertions(+) create mode 100644 selfdrive/ui/tests/playsound.cc create mode 100755 selfdrive/ui/tests/test_sound_stability.py create mode 100755 selfdrive/ui/tests/test_soundd.py diff --git a/Jenkinsfile b/Jenkinsfile index 58ca92fb67..29edbeb172 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -170,6 +170,7 @@ pipeline { steps { phone_steps("eon", [ ["build", "cd selfdrive/manager && ./build.py"], + ["test sounds", "python selfdrive/ui/tests/test_soundd.py"], ["test boardd loopback", "python selfdrive/boardd/tests/test_boardd_loopback.py"], ["test loggerd", "python selfdrive/loggerd/tests/test_loggerd.py"], ["test encoder", "python selfdrive/loggerd/tests/test_encoder.py"], diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 1bb8a203c6..943c072a69 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -45,6 +45,8 @@ asset_obj = qt_env.Object("assets", assets) # build soundd qt_env.Program("_soundd", "soundd.cc", LIBS=qt_libs) +if GetOption('test'): + qt_env.Program("tests/playsound", "tests/playsound.cc", LIBS=base_libs) qt_env.SharedLibrary("qt/python_helpers", ["qt/qt_window.cc"], LIBS=qt_libs) diff --git a/selfdrive/ui/tests/.gitignore b/selfdrive/ui/tests/.gitignore index 9daeafb986..b80a5b49ce 100644 --- a/selfdrive/ui/tests/.gitignore +++ b/selfdrive/ui/tests/.gitignore @@ -1 +1,2 @@ test +play_sound diff --git a/selfdrive/ui/tests/playsound.cc b/selfdrive/ui/tests/playsound.cc new file mode 100644 index 0000000000..6487d04790 --- /dev/null +++ b/selfdrive/ui/tests/playsound.cc @@ -0,0 +1,30 @@ +#include +#include +#include +#include + +int main(int argc, char **argv) { + + QApplication a(argc, argv); + + QTimer::singleShot(0, [=]{ + QSoundEffect s; + const char *vol = getenv("VOLUME"); + s.setVolume(vol ? atof(vol) : 1.0); + for (int i = 1; i < argc; i++) { + QString fn = argv[i]; + qDebug() << "playing" << fn; + + QEventLoop loop; + s.setSource(QUrl::fromLocalFile(fn)); + QEventLoop::connect(&s, &QSoundEffect::loadedChanged, &loop, &QEventLoop::quit); + loop.exec(); + s.play(); + QEventLoop::connect(&s, &QSoundEffect::playingChanged, &loop, &QEventLoop::quit); + loop.exec(); + } + QCoreApplication::exit(); + }); + + return a.exec(); +} diff --git a/selfdrive/ui/tests/test_sound_stability.py b/selfdrive/ui/tests/test_sound_stability.py new file mode 100755 index 0000000000..f0d51ec960 --- /dev/null +++ b/selfdrive/ui/tests/test_sound_stability.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +import os +import random +import subprocess +import time +from pathlib import Path +from common.basedir import BASEDIR + +os.environ["LD_LIBRARY_PATH"] = "" + +# pull this from the provisioning tests +play_sound = os.path.join(BASEDIR, "selfdrive/ui/test/play_sound") +waste = os.path.join(BASEDIR, "scripts/waste") +sound_path = Path(os.path.join(BASEDIR, "selfdrive/assets/sounds")) + +def sound_test(): + + # max volume + vol = 15 + sound_files = [p.absolute() for p in sound_path.iterdir() if str(p).endswith(".wav")] + + # start waste + p = subprocess.Popen([waste], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + + start_time = time.monotonic() + frame = 0 + while True: + # start a few processes + procs = [] + for _ in range(random.randint(5, 20)): + sound = random.choice(sound_files) + p = subprocess.Popen([play_sound, str(sound), str(vol)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + procs.append(p) + time.sleep(random.uniform(0, 0.75)) + + # and kill them + time.sleep(random.uniform(0, 5)) + for p in procs: + p.terminate() + + # write stats + stats = f"running time {time.monotonic() - start_time}s, cycle {frame}" + with open("/tmp/sound_stats.txt", "a") as f: + f.write(stats) + print(stats) + frame +=1 + +if __name__ == "__main__": + sound_test() diff --git a/selfdrive/ui/tests/test_soundd.py b/selfdrive/ui/tests/test_soundd.py new file mode 100755 index 0000000000..80302faa9a --- /dev/null +++ b/selfdrive/ui/tests/test_soundd.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +import subprocess +import time +import unittest + +from cereal import log, car +import cereal.messaging as messaging +from selfdrive.test.helpers import phone_only, with_processes +# TODO: rewrite for unittest +from common.realtime import DT_CTRL +from selfdrive.hardware import HARDWARE + +AudibleAlert = car.CarControl.HUDControl.AudibleAlert + +SOUNDS = { + # sound: total writes + AudibleAlert.none: 0, + AudibleAlert.chimeEngage: 173, + AudibleAlert.chimeDisengage: 173, + AudibleAlert.chimeError: 173, + AudibleAlert.chimePrompt: 173, + AudibleAlert.chimeWarning1: 163, + AudibleAlert.chimeWarning2: 216, + AudibleAlert.chimeWarning2Repeat: 470, + AudibleAlert.chimeWarningRepeat: 468, +} + +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]) + +class TestSoundd(unittest.TestCase): + def test_sound_card_init(self): + assert HARDWARE.get_sound_card_online() + + @phone_only + @with_processes(['soundd']) + def test_alert_sounds(self): + pm = messaging.PubMaster(['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(1) + + 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.alertSound = sound + msg.controlsState.alertType = str(sound) + msg.controlsState.alertText1 = "Testing Sounds" + msg.controlsState.alertText2 = f"playing {alert_sounds[sound]}" + msg.controlsState.alertSize = log.ControlsState.AlertSize.mid + pm.send('controlsState', msg) + time.sleep(DT_CTRL) + + tolerance = (expected_writes % 100) * 2 + actual_writes = get_total_writes() - start_writes + assert abs(expected_writes - actual_writes) <= tolerance, f"{alert_sounds[sound]}: expected {expected_writes} writes, got {actual_writes}" + +if __name__ == "__main__": + unittest.main()