#!/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.engage: 197,
  AudibleAlert.disengage: 230,
  AudibleAlert.refuse: 189,
  AudibleAlert.prompt: 217,
  AudibleAlert.promptRepeat: 475,
  AudibleAlert.promptDistracted: 187,
  AudibleAlert.warningSoft: 477,
  AudibleAlert.warningImmediate: 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(['deviceState', '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('deviceState')
        msg.deviceState.started = True
        pm.send('deviceState', msg)

        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 / 10
      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()

@@ -525,10 +525,10 @@
 t: 2
-17
+30
 ,%0A  
@@ -559,10 +559,10 @@
 t: 4
-75
+68
 ,%0A  
@@ -630,10 +630,10 @@
 t: 4
-77
+99
 ,%0A  
@@ -664,18 +664,18 @@
 diate: 4
+9
 6
-8
 ,%0A%7D%0A%0Adef
@@ -1691,17 +1691,17 @@
     for 
-_
+i
  in rang
@@ -1706,17 +1706,18 @@
 nge(int(
-9
+10
  / DT_CT
@@ -1894,32 +1894,67 @@
 controlsState')%0A
+        if i %3C int(6 / DT_CTRL):%0A  
         msg.cont
@@ -1982,16 +1982,18 @@
 = sound%0A
+  
         
@@ -2033,32 +2033,34 @@
 (sound)%0A        
+  
 msg.controlsStat
@@ -2091,16 +2091,18 @@
 Sounds%22%0A
+  
         
@@ -2165,16 +2165,18 @@
 ound%5D%7D%22%0A
+  
         
@@ -2399,16 +2399,90 @@
 _writes%0A
+      #print(f%22  expected %7Bexpected_writes%7D writes, got %7Bactual_writes%7D%22)%0A
       as

fix test for repeating soundsgitt status (#23110)