Skip to content

Commit 94d29d8

Browse files
Add test_audiomanager.py
1 parent 52972b4 commit 94d29d8

File tree

1 file changed

+225
-0
lines changed

1 file changed

+225
-0
lines changed

tests/test_audiomanager.py

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# Unit tests for AudioManager service
2+
import unittest
3+
import sys
4+
5+
# Import centralized mocks
6+
from mpos.testing import (
7+
MockMachine,
8+
MockPWM,
9+
MockPin,
10+
MockThread,
11+
inject_mocks,
12+
)
13+
14+
# Inject mocks before importing AudioManager
15+
inject_mocks({
16+
'machine': MockMachine(),
17+
'_thread': MockThread,
18+
})
19+
20+
# Now import the module to test
21+
from mpos.audio.audiomanager import AudioManager
22+
23+
24+
class TestAudioManager(unittest.TestCase):
25+
"""Test cases for AudioManager service."""
26+
27+
def setUp(self):
28+
"""Initialize AudioManager before each test."""
29+
self.buzzer = MockPWM(MockPin(46))
30+
self.i2s_pins = {'sck': 2, 'ws': 47, 'sd': 16}
31+
32+
# Reset singleton instance for each test
33+
AudioManager._instance = None
34+
35+
AudioManager(
36+
i2s_pins=self.i2s_pins,
37+
buzzer_instance=self.buzzer
38+
)
39+
40+
# Reset volume to default after creating instance
41+
AudioManager.set_volume(70)
42+
43+
def tearDown(self):
44+
"""Clean up after each test."""
45+
AudioManager.stop()
46+
47+
def test_initialization(self):
48+
"""Test that AudioManager initializes correctly."""
49+
am = AudioManager.get()
50+
self.assertEqual(am._i2s_pins, self.i2s_pins)
51+
self.assertEqual(am._buzzer_instance, self.buzzer)
52+
53+
def test_has_i2s(self):
54+
"""Test has_i2s() returns correct value."""
55+
# With I2S configured
56+
AudioManager._instance = None
57+
AudioManager(i2s_pins=self.i2s_pins, buzzer_instance=None)
58+
self.assertTrue(AudioManager.has_i2s())
59+
60+
# Without I2S configured
61+
AudioManager._instance = None
62+
AudioManager(i2s_pins=None, buzzer_instance=self.buzzer)
63+
self.assertFalse(AudioManager.has_i2s())
64+
65+
def test_has_buzzer(self):
66+
"""Test has_buzzer() returns correct value."""
67+
# With buzzer configured
68+
AudioManager._instance = None
69+
AudioManager(i2s_pins=None, buzzer_instance=self.buzzer)
70+
self.assertTrue(AudioManager.has_buzzer())
71+
72+
# Without buzzer configured
73+
AudioManager._instance = None
74+
AudioManager(i2s_pins=self.i2s_pins, buzzer_instance=None)
75+
self.assertFalse(AudioManager.has_buzzer())
76+
77+
def test_stream_types(self):
78+
"""Test stream type constants and priority order."""
79+
self.assertEqual(AudioManager.STREAM_MUSIC, 0)
80+
self.assertEqual(AudioManager.STREAM_NOTIFICATION, 1)
81+
self.assertEqual(AudioManager.STREAM_ALARM, 2)
82+
83+
# Higher number = higher priority
84+
self.assertTrue(AudioManager.STREAM_MUSIC < AudioManager.STREAM_NOTIFICATION)
85+
self.assertTrue(AudioManager.STREAM_NOTIFICATION < AudioManager.STREAM_ALARM)
86+
87+
def test_volume_control(self):
88+
"""Test volume get/set operations."""
89+
# Set volume
90+
AudioManager.set_volume(50)
91+
self.assertEqual(AudioManager.get_volume(), 50)
92+
93+
# Test clamping to 0-100 range
94+
AudioManager.set_volume(150)
95+
self.assertEqual(AudioManager.get_volume(), 100)
96+
97+
AudioManager.set_volume(-10)
98+
self.assertEqual(AudioManager.get_volume(), 0)
99+
100+
def test_no_hardware_rejects_playback(self):
101+
"""Test that no hardware rejects all playback requests."""
102+
# Re-initialize with no hardware
103+
AudioManager._instance = None
104+
AudioManager(i2s_pins=None, buzzer_instance=None)
105+
106+
# WAV should be rejected (no I2S)
107+
result = AudioManager.play_wav("test.wav")
108+
self.assertFalse(result)
109+
110+
# RTTTL should be rejected (no buzzer)
111+
result = AudioManager.play_rtttl("Test:d=4,o=5,b=120:c")
112+
self.assertFalse(result)
113+
114+
def test_i2s_only_rejects_rtttl(self):
115+
"""Test that I2S-only config rejects buzzer playback."""
116+
# Re-initialize with I2S only
117+
AudioManager._instance = None
118+
AudioManager(i2s_pins=self.i2s_pins, buzzer_instance=None)
119+
120+
# RTTTL should be rejected (no buzzer)
121+
result = AudioManager.play_rtttl("Test:d=4,o=5,b=120:c")
122+
self.assertFalse(result)
123+
124+
def test_buzzer_only_rejects_wav(self):
125+
"""Test that buzzer-only config rejects I2S playback."""
126+
# Re-initialize with buzzer only
127+
AudioManager._instance = None
128+
AudioManager(i2s_pins=None, buzzer_instance=self.buzzer)
129+
130+
# WAV should be rejected (no I2S)
131+
result = AudioManager.play_wav("test.wav")
132+
self.assertFalse(result)
133+
134+
def test_is_playing_initially_false(self):
135+
"""Test that is_playing() returns False initially."""
136+
# Reset to ensure clean state
137+
AudioManager._instance = None
138+
AudioManager(i2s_pins=self.i2s_pins, buzzer_instance=self.buzzer)
139+
self.assertFalse(AudioManager.is_playing())
140+
141+
def test_stop_with_no_playback(self):
142+
"""Test that stop() can be called when nothing is playing."""
143+
# Should not raise exception
144+
AudioManager.stop()
145+
self.assertFalse(AudioManager.is_playing())
146+
147+
def test_audio_focus_check_no_current_stream(self):
148+
"""Test audio focus allows playback when no stream is active."""
149+
am = AudioManager.get()
150+
result = am._check_audio_focus(AudioManager.STREAM_MUSIC)
151+
self.assertTrue(result)
152+
153+
def test_volume_default_value(self):
154+
"""Test that default volume is reasonable."""
155+
# After init, volume should be at default (70)
156+
AudioManager(i2s_pins=None, buzzer_instance=None)
157+
self.assertEqual(AudioManager.get_volume(), 70)
158+
159+
160+
class TestAudioManagerRecording(unittest.TestCase):
161+
"""Test cases for AudioManager recording functionality."""
162+
163+
def setUp(self):
164+
"""Initialize AudioManager with microphone before each test."""
165+
self.buzzer = MockPWM(MockPin(46))
166+
# I2S pins with microphone input
167+
self.i2s_pins_with_mic = {'sck': 2, 'ws': 47, 'sd': 16, 'sd_in': 15}
168+
# I2S pins without microphone input
169+
self.i2s_pins_no_mic = {'sck': 2, 'ws': 47, 'sd': 16}
170+
171+
# Reset singleton instance for each test
172+
AudioManager._instance = None
173+
174+
AudioManager(
175+
i2s_pins=self.i2s_pins_with_mic,
176+
buzzer_instance=self.buzzer
177+
)
178+
179+
# Reset volume to default after creating instance
180+
AudioManager.set_volume(70)
181+
182+
def tearDown(self):
183+
"""Clean up after each test."""
184+
AudioManager.stop()
185+
186+
def test_has_microphone_with_sd_in(self):
187+
"""Test has_microphone() returns True when sd_in pin is configured."""
188+
AudioManager._instance = None
189+
AudioManager(i2s_pins=self.i2s_pins_with_mic, buzzer_instance=None)
190+
self.assertTrue(AudioManager.has_microphone())
191+
192+
def test_has_microphone_without_sd_in(self):
193+
"""Test has_microphone() returns False when sd_in pin is not configured."""
194+
AudioManager._instance = None
195+
AudioManager(i2s_pins=self.i2s_pins_no_mic, buzzer_instance=None)
196+
self.assertFalse(AudioManager.has_microphone())
197+
198+
def test_has_microphone_no_i2s(self):
199+
"""Test has_microphone() returns False when no I2S is configured."""
200+
AudioManager._instance = None
201+
AudioManager(i2s_pins=None, buzzer_instance=self.buzzer)
202+
self.assertFalse(AudioManager.has_microphone())
203+
204+
def test_is_recording_initially_false(self):
205+
"""Test that is_recording() returns False initially."""
206+
self.assertFalse(AudioManager.is_recording())
207+
208+
def test_record_wav_no_microphone(self):
209+
"""Test that record_wav() fails when no microphone is configured."""
210+
AudioManager._instance = None
211+
AudioManager(i2s_pins=self.i2s_pins_no_mic, buzzer_instance=None)
212+
result = AudioManager.record_wav("test.wav")
213+
self.assertFalse(result, "record_wav() fails when no microphone is configured")
214+
215+
def test_record_wav_no_i2s(self):
216+
AudioManager._instance = None
217+
AudioManager(i2s_pins=None, buzzer_instance=self.buzzer)
218+
result = AudioManager.record_wav("test.wav")
219+
self.assertFalse(result, "record_wav() should fail when no I2S is configured")
220+
221+
def test_stop_with_no_recording(self):
222+
"""Test that stop() can be called when nothing is recording."""
223+
# Should not raise exception
224+
AudioManager.stop()
225+
self.assertFalse(AudioManager.is_recording())

0 commit comments

Comments
 (0)