-
Notifications
You must be signed in to change notification settings - Fork 70
Expand file tree
/
Copy pathtest_audiomanager.py
More file actions
259 lines (208 loc) · 9.59 KB
/
test_audiomanager.py
File metadata and controls
259 lines (208 loc) · 9.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
# Unit tests for AudioManager service
import unittest
import sys
# Import centralized mocks
from mpos.testing import (
MockMachine,
MockThread,
inject_mocks,
)
from mpos.testing.mocks import MockSharedPreferences, create_mock_module
# Inject mocks before importing AudioManager
inject_mocks({
"machine": MockMachine(),
"_thread": MockThread,
"mpos.config": create_mock_module("mpos.config", SharedPreferences=MockSharedPreferences),
})
# Now import the module to test
from mpos.audio.audiomanager import AudioManager
class TestAudioManager(unittest.TestCase):
"""Test cases for AudioManager service."""
def setUp(self):
"""Initialize AudioManager before each test."""
self.buzzer_pin = 46
self.i2s_pins = {"sck": 2, "ws": 47, "sd": 16}
# Reset singleton instance for each test
AudioManager._instance = None
MockSharedPreferences.reset_all()
AudioManager()
AudioManager.add(AudioManager.Output("speaker", "i2s", i2s_pins=self.i2s_pins))
AudioManager.add(AudioManager.Output("buzzer", "buzzer", buzzer_pin=self.buzzer_pin))
# Reset volume to default after creating instance
AudioManager.set_volume(70)
def tearDown(self):
"""Clean up after each test."""
AudioManager.stop()
def test_initialization(self):
"""Test that AudioManager initializes correctly."""
am = AudioManager.get()
self.assertEqual(len(am._outputs), 2)
self.assertEqual(am._outputs[0].i2s_pins, self.i2s_pins)
self.assertEqual(am._outputs[1].buzzer_pin, self.buzzer_pin)
def test_get_outputs(self):
"""Test that get_outputs() returns configured outputs."""
outputs = AudioManager.get_outputs()
self.assertEqual(len(outputs), 2)
self.assertEqual(outputs[0].kind, "i2s")
self.assertEqual(outputs[1].kind, "buzzer")
def test_default_output(self):
"""Test default output selection."""
default_output = AudioManager.get_default_output()
self.assertIsNotNone(default_output)
self.assertEqual(default_output.kind, "i2s")
def test_stream_types(self):
"""Test stream type constants and priority order."""
self.assertEqual(AudioManager.STREAM_MUSIC, 0)
self.assertEqual(AudioManager.STREAM_NOTIFICATION, 1)
self.assertEqual(AudioManager.STREAM_ALARM, 2)
# Higher number = higher priority
self.assertTrue(AudioManager.STREAM_MUSIC < AudioManager.STREAM_NOTIFICATION)
self.assertTrue(AudioManager.STREAM_NOTIFICATION < AudioManager.STREAM_ALARM)
def test_volume_control(self):
"""Test volume get/set operations."""
# Set volume
AudioManager.set_volume(50)
self.assertEqual(AudioManager.get_volume(), 50)
# Test clamping to 0-100 range
AudioManager.set_volume(150)
self.assertEqual(AudioManager.get_volume(), 100)
AudioManager.set_volume(-10)
self.assertEqual(AudioManager.get_volume(), 0)
def test_no_hardware_rejects_playback(self):
"""Test that no hardware rejects all playback requests."""
# Re-initialize with no hardware
AudioManager._instance = None
AudioManager()
with self.assertRaises(ValueError):
AudioManager.player(file_path="test.wav").start()
with self.assertRaises(ValueError):
AudioManager.player(rtttl="Test:d=4,o=5,b=120:c").start()
def test_i2s_only_rejects_rtttl(self):
"""Test that I2S-only config rejects buzzer playback."""
# Re-initialize with I2S only
AudioManager._instance = None
AudioManager()
AudioManager.add(AudioManager.Output("speaker", "i2s", i2s_pins=self.i2s_pins))
with self.assertRaises(ValueError):
AudioManager.player(rtttl="Test:d=4,o=5,b=120:c").start()
def test_buzzer_only_rejects_wav(self):
"""Test that buzzer-only config rejects I2S playback."""
# Re-initialize with buzzer only
AudioManager._instance = None
AudioManager()
AudioManager.add(AudioManager.Output("buzzer", "buzzer", buzzer_pin=self.buzzer_pin))
with self.assertRaises(ValueError):
AudioManager.player(file_path="test.wav").start()
def test_is_playing_initially_false(self):
"""Test that is_playing() returns False initially."""
# Reset to ensure clean state
AudioManager._instance = None
AudioManager()
AudioManager.add(AudioManager.Output("speaker", "i2s", i2s_pins=self.i2s_pins))
self.assertFalse(AudioManager.player(file_path="test.wav").is_playing())
def test_stop_with_no_playback(self):
"""Test that stop() can be called when nothing is playing."""
# Should not raise exception
AudioManager.stop()
def test_volume_default_value(self):
"""Test that default volume is reasonable."""
# After init, volume should be at default (50)
AudioManager._instance = None
AudioManager()
self.assertEqual(AudioManager.get_volume(), 50)
class TestAudioManagerRecording(unittest.TestCase):
"""Test cases for AudioManager recording functionality."""
def setUp(self):
"""Initialize AudioManager with microphone before each test."""
# I2S pins with microphone input
self.i2s_pins_with_mic = {"sck": 2, "ws": 47, "sd_in": 15}
self.pdm_pins_with_mic = {"sck_in": 44, "sd_in": 47}
# Reset singleton instance for each test
AudioManager._instance = None
MockSharedPreferences.reset_all()
AudioManager()
AudioManager.add(AudioManager.Input("mic", "i2s", i2s_pins=self.i2s_pins_with_mic))
# Reset volume to default after creating instance
AudioManager.set_volume(70)
def tearDown(self):
"""Clean up after each test."""
AudioManager.stop()
def test_get_inputs(self):
"""Test get_inputs() returns configured inputs."""
inputs = AudioManager.get_inputs()
self.assertEqual(len(inputs), 1)
self.assertEqual(inputs[0].kind, "i2s")
def test_add_pdm_input(self):
AudioManager.add(AudioManager.Input("pdm_mic", "pdm", pdm_pins=self.pdm_pins_with_mic))
inputs = AudioManager.get_inputs()
self.assertEqual(len(inputs), 2)
self.assertEqual(inputs[1].kind, "pdm")
def test_default_input(self):
"""Test default input selection."""
default_input = AudioManager.get_default_input()
self.assertIsNotNone(default_input)
self.assertEqual(default_input.kind, "i2s")
def test_is_recording_initially_false(self):
"""Test that is_recording() returns False initially."""
recorder = AudioManager.recorder(file_path="test.wav")
self.assertFalse(recorder.is_recording())
def test_record_wav_no_microphone(self):
"""Test that recorder() fails when no microphone is configured."""
AudioManager._instance = None
AudioManager()
with self.assertRaises(ValueError):
AudioManager.recorder(file_path="test.wav").start()
def test_record_wav_no_i2s(self):
AudioManager._instance = None
AudioManager()
AudioManager.add(AudioManager.Input("mic", "adc", adc_mic_pin=4))
recorder = AudioManager.recorder(file_path="test.wav")
self.assertFalse(recorder.is_recording())
def test_stop_with_no_recording(self):
"""Test that stop() can be called when nothing is recording."""
# Should not raise exception
AudioManager.stop()
class TestAudioManagerPreferences(unittest.TestCase):
"""Test cases for AudioManager preference-based selection."""
def setUp(self):
self.i2s_pins = {"sck": 2, "ws": 47, "sd": 16}
self.i2s_pins_with_mic = {"sck": 2, "ws": 47, "sd_in": 15}
AudioManager._instance = None
MockSharedPreferences.reset_all()
AudioManager()
AudioManager.add(AudioManager.Output("speaker", "i2s", i2s_pins=self.i2s_pins))
AudioManager.add(AudioManager.Output("buzzer", "buzzer", buzzer_pin=46))
AudioManager.add(AudioManager.Input("mic", "i2s", i2s_pins=self.i2s_pins_with_mic))
AudioManager.add(AudioManager.Input("mic2", "adc", adc_mic_pin=4))
self._prefs = MockSharedPreferences("com.micropythonos.settings.audio")
AudioManager.get()._audio_prefs = self._prefs
def tearDown(self):
AudioManager.stop()
def test_pref_selects_output(self):
editor = self._prefs.edit()
editor.put_string("output_device", "buzzer")
editor.commit()
default_output = AudioManager.get_default_output()
self.assertIsNotNone(default_output)
self.assertEqual(default_output.name, "buzzer")
def test_pref_selects_input(self):
editor = self._prefs.edit()
editor.put_string("input_device", "mic2")
editor.commit()
default_input = AudioManager.get_default_input()
self.assertIsNotNone(default_input)
self.assertEqual(default_input.name, "mic2")
def test_pref_missing_output_falls_back(self):
editor = self._prefs.edit()
editor.put_string("output_device", "missing")
editor.commit()
default_output = AudioManager.get_default_output()
self.assertIsNotNone(default_output)
self.assertEqual(default_output.name, "speaker")
def test_pref_missing_input_falls_back(self):
editor = self._prefs.edit()
editor.put_string("input_device", "missing")
editor.commit()
default_input = AudioManager.get_default_input()
self.assertIsNotNone(default_input)
self.assertEqual(default_input.name, "mic")