test(Microphone): removing unittest.TestCase class architecture to add tests parametrization on multiprocessing/multithreading use

This commit is contained in:
CarolinePascal
2025-08-09 01:09:17 +02:00
parent ef8f40c21b
commit 6d73f5bfe6
+409 -325
View File
@@ -13,11 +13,11 @@
# limitations under the License. # limitations under the License.
import os import os
import unittest
from copy import deepcopy from copy import deepcopy
from pathlib import Path from pathlib import Path
import numpy as np import numpy as np
import pytest
from soundfile import read from soundfile import read
from lerobot.microphones.portaudio.configuration_portaudio import PortAudioMicrophoneConfig from lerobot.microphones.portaudio.configuration_portaudio import PortAudioMicrophoneConfig
@@ -43,386 +43,470 @@ LEROBOT_USE_REAL_PORTAUDIO_MICROPHONE_TESTS = (
) )
class TestPortAudioMicrophoneConfiguration(unittest.TestCase): @pytest.fixture
"""Test the PortAudioMicrophone configuration and initialization.""" def test_sdk():
"""Fixture to provide either real or fake SDK based on environment variable."""
def test_config_creation(self): if LEROBOT_USE_REAL_PORTAUDIO_MICROPHONE_TESTS:
"""Test creating a valid configuration.""" return SounddeviceSDKAdapter()
config = PortAudioMicrophoneConfig(microphone_index=0, sample_rate=48000, channels=[1, 2]) else:
self.assertEqual(config.microphone_index, 0) return FakeSounddeviceSDKAdapter()
self.assertEqual(config.sample_rate, 48000)
self.assertEqual(config.channels, [1, 2])
def test_config_creation_missing_microphone_index(self):
"""Test creating a configuration with missing microphone index."""
with self.assertRaises(TypeError):
PortAudioMicrophoneConfig(sample_rate=48000, channels=[1, 2])
def test_config_creation_missing_sample_rate(self):
"""Test creating a configuration with missing sample rate."""
config = PortAudioMicrophoneConfig(microphone_index=0, channels=[1, 2])
self.assertIsNone(config.sample_rate)
def test_config_creation_missing_channels(self):
"""Test creating a configuration with missing channels."""
config = PortAudioMicrophoneConfig(microphone_index=0, sample_rate=48000)
self.assertIsNone(config.channels)
class TestPortAudioMicrophoneDeviceValidation(unittest.TestCase): # Configuration Tests
"""Test device validation and configuration."""
def setUp(self):
if LEROBOT_USE_REAL_PORTAUDIO_MICROPHONE_TESTS:
self.test_sdk = SounddeviceSDKAdapter()
else:
self.test_sdk = FakeSounddeviceSDKAdapter()
def _create_config( def test_config_creation():
device: int | str | None = None, kind: str | None = None """Test creating a valid configuration."""
) -> PortAudioMicrophoneConfig: config = PortAudioMicrophoneConfig(microphone_index=0, sample_rate=48000, channels=[1, 2])
device_info = self.test_sdk.query_devices(device, kind) assert config.microphone_index == 0
config = PortAudioMicrophoneConfig( assert config.sample_rate == 48000
microphone_index=device_info["index"], assert config.channels == [1, 2]
sample_rate=device_info["default_samplerate"],
channels=np.arange(device_info["max_input_channels"]) + 1,
)
return config
self._create_config = _create_config
self.default_config = self._create_config(kind="input") def test_config_creation_missing_microphone_index():
"""Test creating a configuration with missing microphone index."""
with pytest.raises(TypeError):
PortAudioMicrophoneConfig(sample_rate=48000, channels=[1, 2])
def test_find_microphones(self):
microphones = PortAudioMicrophone.find_microphones(sounddevice_sdk=self.test_sdk)
for microphone in microphones: def test_config_creation_missing_sample_rate():
self.assertIsInstance(microphone["index"], int) """Test creating a configuration with missing sample rate."""
self.assertIsInstance(microphone["name"], str) config = PortAudioMicrophoneConfig(microphone_index=0, channels=[1, 2])
self.assertIsInstance(microphone["sample_rate"], int) assert config.sample_rate is None
self.assertIsInstance(microphone["channels"], np.ndarray)
self.assertGreater(len(microphone["channels"]), 0)
def test_init_defaults(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk)
device_info = self.test_sdk.query_devices(kind="input") def test_config_creation_missing_channels():
self.assertIsNotNone(microphone) """Test creating a configuration with missing channels."""
self.assertEqual(microphone.microphone_index, device_info["index"]) config = PortAudioMicrophoneConfig(microphone_index=0, sample_rate=48000)
self.assertEqual(microphone.sample_rate, device_info["default_samplerate"]) assert config.channels is None
np.testing.assert_array_equal(microphone.channels, np.arange(device_info["max_input_channels"]) + 1)
self.assertFalse(microphone.is_connected)
self.assertFalse(microphone.is_recording)
def test_connect_success(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) @pytest.fixture
def default_config(test_sdk):
"""Fixture to provide a default configuration for input devices."""
device_info = test_sdk.query_devices(kind="input")
return PortAudioMicrophoneConfig(
microphone_index=device_info["index"],
sample_rate=device_info["default_samplerate"],
channels=np.arange(device_info["max_input_channels"]) + 1,
)
# Microphone Tests
def test_find_microphones(test_sdk):
"""Test finding microphones."""
microphones = PortAudioMicrophone.find_microphones(sounddevice_sdk=test_sdk)
for microphone in microphones:
assert isinstance(microphone["index"], int)
assert isinstance(microphone["name"], str)
assert isinstance(microphone["sample_rate"], int)
assert isinstance(microphone["channels"], np.ndarray)
assert len(microphone["channels"]) > 0
def test_init_defaults(default_config, test_sdk):
"""Test microphone initialization with defaults."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
device_info = test_sdk.query_devices(kind="input")
assert microphone is not None
assert microphone.microphone_index == device_info["index"]
assert microphone.sample_rate == device_info["default_samplerate"]
np.testing.assert_array_equal(microphone.channels, np.arange(device_info["max_input_channels"]) + 1)
assert not microphone.is_connected
assert not microphone.is_recording
def test_connect_success(default_config, test_sdk):
"""Test successful connection."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect()
assert microphone.is_connected
assert not microphone.is_recording
assert not microphone.is_writing
def test_connect_empty_config(default_config, test_sdk):
"""Test connection with empty config values."""
config = deepcopy(default_config)
config.sample_rate = None
config.channels = None
microphone = PortAudioMicrophone(config, sounddevice_sdk=test_sdk)
microphone.connect()
device_info = test_sdk.query_devices(kind="input")
assert microphone.sample_rate == device_info["default_samplerate"]
np.testing.assert_array_equal(microphone.channels, np.arange(device_info["max_input_channels"]) + 1)
def test_connect_already_connected(default_config, test_sdk):
"""Test connecting when already connected."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect()
with pytest.raises(DeviceAlreadyConnectedError):
microphone.connect() microphone.connect()
self.assertTrue(microphone.is_connected)
self.assertFalse(microphone.is_recording)
self.assertFalse(microphone.is_writing)
def test_connect_empty_config(self): def test_connect_invalid_device(test_sdk):
config = deepcopy(self.default_config) """Test connecting with invalid device (output device)."""
config.sample_rate = None device_info = test_sdk.query_devices(kind="output")
config.channels = None config = PortAudioMicrophoneConfig(
microphone = PortAudioMicrophone(config, sounddevice_sdk=self.test_sdk) microphone_index=device_info["index"],
sample_rate=device_info["default_samplerate"],
channels=np.arange(device_info["max_input_channels"]) + 1,
)
microphone = PortAudioMicrophone(config, sounddevice_sdk=test_sdk)
with pytest.raises(RuntimeError):
microphone.connect() microphone.connect()
device_info = self.test_sdk.query_devices(kind="input")
self.assertEqual(microphone.sample_rate, device_info["default_samplerate"])
np.testing.assert_array_equal(microphone.channels, np.arange(device_info["max_input_channels"]) + 1)
def test_connect_already_connected(self): def test_connect_invalid_index(default_config, test_sdk):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) """Test connecting with invalid device index."""
config = deepcopy(default_config)
config.microphone_index = -1
microphone = PortAudioMicrophone(config, sounddevice_sdk=test_sdk)
with pytest.raises(RuntimeError):
microphone.connect() microphone.connect()
with self.assertRaises(DeviceAlreadyConnectedError):
microphone.connect()
def test_connect_invalid_device(self): def test_connect_invalid_sample_rate(default_config, test_sdk):
config = self._create_config(kind="output") """Test connecting with invalid sample rate."""
microphone = PortAudioMicrophone(config, sounddevice_sdk=self.test_sdk) config = deepcopy(default_config)
config.sample_rate = -1
microphone = PortAudioMicrophone(config, sounddevice_sdk=test_sdk)
with self.assertRaises(RuntimeError): with pytest.raises(RuntimeError):
microphone.connect()
def test_connect_invalid_index(self):
config = deepcopy(self.default_config)
config.microphone_index = -1
microphone = PortAudioMicrophone(config, sounddevice_sdk=self.test_sdk)
with self.assertRaises(RuntimeError):
microphone.connect()
def test_connect_invalid_sample_rate(self):
config = deepcopy(self.default_config)
config.sample_rate = -1
microphone = PortAudioMicrophone(config, sounddevice_sdk=self.test_sdk)
with self.assertRaises(RuntimeError):
microphone.connect()
def test_connect_float_sample_rate(self):
config = deepcopy(self.default_config)
config.sample_rate = int(config.sample_rate) - 0.5
microphone = PortAudioMicrophone(config, sounddevice_sdk=self.test_sdk)
microphone.connect() microphone.connect()
self.assertIsInstance(microphone.sample_rate, int)
self.assertEqual(microphone.sample_rate, int(config.sample_rate))
def test_connect_lower_sample_rate(self): def test_connect_float_sample_rate(default_config, test_sdk):
config = deepcopy(self.default_config) """Test connecting with float sample rate."""
config.sample_rate = 1000 # Lowest possible sample rate config = deepcopy(default_config)
microphone = PortAudioMicrophone(config, sounddevice_sdk=self.test_sdk) config.sample_rate = int(config.sample_rate) - 0.5
microphone = PortAudioMicrophone(config, sounddevice_sdk=test_sdk)
microphone.connect()
assert isinstance(microphone.sample_rate, int)
assert microphone.sample_rate == int(config.sample_rate)
def test_connect_lower_sample_rate(default_config, test_sdk):
"""Test connecting with lower sample rate."""
config = deepcopy(default_config)
config.sample_rate = 1000 # Lowest possible sample rate
microphone = PortAudioMicrophone(config, sounddevice_sdk=test_sdk)
microphone.connect()
assert microphone.sample_rate == 1000
def test_connect_invalid_channels(default_config, test_sdk):
"""Test connecting with invalid channels."""
config = deepcopy(default_config)
config.channels = np.append(default_config.channels, -1)
microphone = PortAudioMicrophone(config, sounddevice_sdk=test_sdk)
with pytest.raises(RuntimeError):
microphone.connect() microphone.connect()
self.assertEqual(microphone.sample_rate, 1000)
def test_connect_invalid_channels(self):
config = deepcopy(self.default_config)
config.channels = np.append(self.default_config.channels, -1)
microphone = PortAudioMicrophone(config, sounddevice_sdk=self.test_sdk)
with self.assertRaises(RuntimeError): def test_disconnect_success(default_config, test_sdk):
microphone.connect() """Test successful disconnection."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect()
microphone.disconnect()
def test_disconnect_success(self): assert not microphone.is_connected
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) assert not microphone.is_recording
microphone.connect() assert not microphone.is_writing
def test_disconnect_not_connected(default_config, test_sdk):
"""Test disconnecting when not connected."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
with pytest.raises(DeviceNotConnectedError):
microphone.disconnect() microphone.disconnect()
self.assertFalse(microphone.is_connected)
self.assertFalse(microphone.is_recording)
self.assertFalse(microphone.is_writing)
def test_disconnect_not_connected(self): @pytest.mark.parametrize("multiprocessing", [True, False])
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) def test_start_recording_success(default_config, test_sdk, multiprocessing):
"""Test successful recording start."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect()
microphone.start_recording(multiprocessing=multiprocessing)
with self.assertRaises(DeviceNotConnectedError): assert microphone.is_recording
microphone.disconnect() assert microphone.is_connected
assert not microphone.is_writing
def test_start_recording_success(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk)
microphone.connect()
microphone.start_recording()
self.assertTrue(microphone.is_recording) @pytest.mark.parametrize("multiprocessing", [True, False])
self.assertTrue(microphone.is_connected) def test_recording_not_connected(default_config, test_sdk, multiprocessing):
self.assertFalse(microphone.is_writing) """Test starting recording when not connected."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
def test_recoring_not_connected(self): with pytest.raises(DeviceNotConnectedError):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) microphone.start_recording(multiprocessing=multiprocessing)
with self.assertRaises(DeviceNotConnectedError):
microphone.start_recording()
def test_start_recording_already_recording(self): @pytest.mark.parametrize("multiprocessing", [True, False])
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) def test_start_recording_already_recording(default_config, test_sdk, multiprocessing):
microphone.connect() """Test starting recording when already recording."""
microphone.start_recording() microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect()
microphone.start_recording(multiprocessing=multiprocessing)
with self.assertRaises(DeviceAlreadyRecordingError): with pytest.raises(DeviceAlreadyRecordingError):
microphone.start_recording() microphone.start_recording(multiprocessing=multiprocessing)
def test_start_writing_success(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk)
microphone.connect()
microphone.start_recording(output_file="test.wav")
self.assertTrue(microphone.is_recording) @pytest.mark.parametrize("multiprocessing", [True, False])
self.assertTrue(microphone.is_connected) def test_start_writing_success(tmp_path, default_config, test_sdk, multiprocessing):
self.assertTrue(microphone.is_writing) """Test successful writing start."""
self.assertTrue(Path("test.wav").exists()) microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect()
microphone.start_recording(output_file=tmp_path / "test.wav", multiprocessing=multiprocessing)
def test_stop_recording_success(self): assert microphone.is_recording
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) assert microphone.is_connected
microphone.connect() assert microphone.is_writing
microphone.start_recording() assert (tmp_path / "test.wav").exists()
busy_wait(RECORDING_DURATION)
microphone.stop_recording()
self.assertFalse(microphone.is_recording)
self.assertTrue(microphone.is_connected)
self.assertFalse(microphone.is_writing)
def test_stop_writing_success(self): @pytest.mark.parametrize("multiprocessing", [True, False])
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) def test_start_writing_file_already_exists_no_overwrite(tmp_path, default_config, test_sdk, multiprocessing):
microphone.connect() """Test writing with file that already exists."""
microphone.start_recording(output_file="test.wav") (tmp_path / "test.wav").touch()
busy_wait(RECORDING_DURATION) microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.stop_recording() microphone.connect()
self.assertFalse(microphone.is_recording) with pytest.raises(FileExistsError):
self.assertTrue(microphone.is_connected) microphone.start_recording(
self.assertFalse(microphone.is_writing) output_file=tmp_path / "test.wav", multiprocessing=multiprocessing, overwrite=False
self.assertTrue(Path("test.wav").exists())
def test_stop_recording_not_connected(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk)
with self.assertRaises(DeviceNotConnectedError):
microphone.stop_recording()
def test_stop_recording_not_recording(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk)
microphone.connect()
with self.assertRaises(DeviceNotRecordingError):
microphone.stop_recording()
def test_disconnect_while_recording(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk)
microphone.connect()
microphone.start_recording()
busy_wait(RECORDING_DURATION)
microphone.disconnect()
self.assertFalse(microphone.is_connected)
self.assertFalse(microphone.is_recording)
self.assertFalse(microphone.is_writing)
def test_disconnect_while_writing(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk)
microphone.connect()
microphone.start_recording(output_file="test.wav")
busy_wait(RECORDING_DURATION)
microphone.disconnect()
self.assertFalse(microphone.is_connected)
self.assertFalse(microphone.is_recording)
self.assertFalse(microphone.is_writing)
self.assertTrue(Path("test.wav").exists())
def test_read_success(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk)
microphone.connect()
microphone.start_recording()
busy_wait(RECORDING_DURATION)
data = microphone.read()
device_info = self.test_sdk.query_devices(kind="input")
self.assertIsNotNone(data)
self.assertEqual(data.shape[1], len(self.default_config.channels))
self.assertAlmostEqual(
data.shape[0],
RECORDING_DURATION * self.default_config.sample_rate,
delta=2 * self.default_config.sample_rate * device_info["default_low_input_latency"],
) )
def test_writing_success(self): (tmp_path / "test.wav").unlink()
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk)
microphone.connect()
microphone.start_recording(output_file="test.wav")
busy_wait(RECORDING_DURATION)
@pytest.mark.parametrize("multiprocessing", [True, False])
def test_stop_recording_success(default_config, test_sdk, multiprocessing):
"""Test successful recording stop."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect()
microphone.start_recording(multiprocessing=multiprocessing)
busy_wait(RECORDING_DURATION)
microphone.stop_recording()
assert not microphone.is_recording
assert microphone.is_connected
assert not microphone.is_writing
@pytest.mark.parametrize("multiprocessing", [True, False])
def test_stop_writing_success(tmp_path, default_config, test_sdk, multiprocessing):
"""Test successful writing stop."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect()
microphone.start_recording(output_file=tmp_path / "test.wav", multiprocessing=multiprocessing)
busy_wait(RECORDING_DURATION)
microphone.stop_recording()
assert not microphone.is_recording
assert microphone.is_connected
assert not microphone.is_writing
assert (tmp_path / "test.wav").exists()
def test_stop_recording_not_connected(default_config, test_sdk):
"""Test stopping recording when not connected."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
with pytest.raises(DeviceNotConnectedError):
microphone.stop_recording() microphone.stop_recording()
data, samplerate = read("test.wav")
device_info = self.test_sdk.query_devices(kind="input") def test_stop_recording_not_recording(default_config, test_sdk):
self.assertEqual(samplerate, self.default_config.sample_rate) """Test stopping recording when not recording."""
self.assertEqual(data.shape[1], len(self.default_config.channels)) microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
self.assertAlmostEqual( microphone.connect()
data.shape[0],
RECORDING_DURATION * self.default_config.sample_rate,
delta=2 * self.default_config.sample_rate * device_info["default_low_input_latency"],
)
def test_read_while_writing(self): with pytest.raises(DeviceNotRecordingError):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk)
microphone.connect()
microphone.start_recording(output_file="test.wav")
busy_wait(RECORDING_DURATION)
read_data = microphone.read()
microphone.stop_recording() microphone.stop_recording()
writing_data, _ = read("test.wav")
device_info = self.test_sdk.query_devices(kind="input") @pytest.mark.parametrize("multiprocessing", [True, False])
self.assertAlmostEqual( def test_disconnect_while_recording(default_config, test_sdk, multiprocessing):
writing_data.shape[0], """Test disconnecting while recording."""
RECORDING_DURATION * self.default_config.sample_rate, microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
delta=2 * self.default_config.sample_rate * device_info["default_low_input_latency"], microphone.connect()
) microphone.start_recording(multiprocessing=multiprocessing)
self.assertAlmostEqual( busy_wait(RECORDING_DURATION)
read_data.shape[0], microphone.disconnect()
RECORDING_DURATION * self.default_config.sample_rate,
delta=2 * self.default_config.sample_rate * device_info["default_low_input_latency"],
)
def test_async_start_recording(self): assert not microphone.is_connected
microphones = { assert not microphone.is_recording
"microphone_1": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk), assert not microphone.is_writing
"microphone_2": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk),
}
for microphone in microphones.values():
microphone.connect()
async_microphones_start_recording(microphones)
for microphone in microphones.values():
self.assertTrue(microphone.is_recording)
self.assertTrue(microphone.is_connected)
self.assertFalse(microphone.is_writing)
def test_async_start_writing(self):
microphones = {
"microphone_1": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk),
"microphone_2": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk),
}
for microphone in microphones.values():
microphone.connect()
async_microphones_start_recording(microphones, output_files=["test_1.wav", "test_2.wav"])
for microphone in microphones.values():
self.assertTrue(microphone.is_recording)
self.assertTrue(microphone.is_connected)
self.assertTrue(microphone.is_writing)
self.assertTrue(Path("test_1.wav").exists())
self.assertTrue(Path("test_2.wav").exists())
def test_async_stop_recording(self):
microphones = {
"microphone_1": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk),
"microphone_2": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk),
}
for microphone in microphones.values():
microphone.connect()
async_microphones_start_recording(microphones)
async_microphones_stop_recording(microphones)
for microphone in microphones.values():
self.assertFalse(microphone.is_recording)
self.assertTrue(microphone.is_connected)
self.assertFalse(microphone.is_writing)
def test_async_stop_writing(self):
microphones = {
"microphone_1": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk),
"microphone_2": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk),
}
for microphone in microphones.values():
microphone.connect()
async_microphones_start_recording(microphones, output_files=["test_1.wav", "test_2.wav"])
async_microphones_stop_recording(microphones)
for microphone in microphones.values():
self.assertFalse(microphone.is_recording)
self.assertTrue(microphone.is_connected)
self.assertFalse(microphone.is_writing)
self.assertTrue(Path("test_1.wav").exists())
self.assertTrue(Path("test_2.wav").exists())
if __name__ == "__main__": @pytest.mark.parametrize("multiprocessing", [True, False])
unittest.main(argv=["first-arg-is-ignored"], exit=False) def test_disconnect_while_writing(tmp_path, default_config, test_sdk, multiprocessing):
"""Test disconnecting while writing."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect()
microphone.start_recording(output_file=tmp_path / "test.wav", multiprocessing=multiprocessing)
busy_wait(RECORDING_DURATION)
microphone.disconnect()
assert not microphone.is_connected
assert not microphone.is_recording
assert not microphone.is_writing
assert Path("test.wav").exists()
@pytest.mark.parametrize("multiprocessing", [True, False])
def test_read_success(default_config, test_sdk, multiprocessing):
"""Test successful reading of audio data."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect()
microphone.start_recording(multiprocessing=multiprocessing)
busy_wait(RECORDING_DURATION)
data = microphone.read()
device_info = test_sdk.query_devices(kind="input")
assert data is not None
assert data.shape[1] == len(default_config.channels)
assert (
abs(data.shape[0] - RECORDING_DURATION * default_config.sample_rate)
<= 2 * default_config.sample_rate * device_info["default_low_input_latency"]
)
@pytest.mark.parametrize("multiprocessing", [True, False])
def test_writing_success(tmp_path, default_config, test_sdk, multiprocessing):
"""Test successful writing to file."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect()
microphone.start_recording(output_file=tmp_path / "test.wav", multiprocessing=multiprocessing)
busy_wait(RECORDING_DURATION)
microphone.stop_recording()
data, samplerate = read(tmp_path / "test.wav")
device_info = test_sdk.query_devices(kind="input")
assert samplerate == default_config.sample_rate
assert data.shape[1] == len(default_config.channels)
assert (
abs(data.shape[0] - RECORDING_DURATION * default_config.sample_rate)
<= 2 * default_config.sample_rate * device_info["default_low_input_latency"]
)
@pytest.mark.parametrize("multiprocessing", [True, False])
def test_read_while_writing(tmp_path, default_config, test_sdk, multiprocessing):
"""Test reading while writing."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect()
microphone.start_recording(output_file=tmp_path / "test.wav", multiprocessing=multiprocessing)
busy_wait(RECORDING_DURATION)
read_data = microphone.read()
microphone.stop_recording()
writing_data, _ = read(tmp_path / "test.wav")
device_info = test_sdk.query_devices(kind="input")
assert (
abs(writing_data.shape[0] - RECORDING_DURATION * default_config.sample_rate)
<= 2 * default_config.sample_rate * device_info["default_low_input_latency"]
)
assert (
abs(read_data.shape[0] - RECORDING_DURATION * default_config.sample_rate)
<= 2 * default_config.sample_rate * device_info["default_low_input_latency"]
)
def test_async_start_recording(default_config, test_sdk):
"""Test async recording start."""
microphones = {
"microphone_1": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
"microphone_2": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
}
for microphone in microphones.values():
microphone.connect()
async_microphones_start_recording(microphones)
for microphone in microphones.values():
assert microphone.is_recording
assert microphone.is_connected
assert not microphone.is_writing
def test_async_start_writing(default_config, test_sdk):
"""Test async writing start."""
microphones = {
"microphone_1": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
"microphone_2": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
}
for microphone in microphones.values():
microphone.connect()
async_microphones_start_recording(microphones, output_files=["test_1.wav", "test_2.wav"])
for microphone in microphones.values():
assert microphone.is_recording
assert microphone.is_connected
assert microphone.is_writing
assert Path("test_1.wav").exists()
assert Path("test_2.wav").exists()
def test_async_stop_recording(default_config, test_sdk):
"""Test async recording stop."""
microphones = {
"microphone_1": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
"microphone_2": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
}
for microphone in microphones.values():
microphone.connect()
async_microphones_start_recording(microphones)
async_microphones_stop_recording(microphones)
for microphone in microphones.values():
assert not microphone.is_recording
assert microphone.is_connected
assert not microphone.is_writing
def test_async_stop_writing(default_config, test_sdk):
"""Test async writing stop."""
microphones = {
"microphone_1": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
"microphone_2": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
}
for microphone in microphones.values():
microphone.connect()
async_microphones_start_recording(microphones, output_files=["test_1.wav", "test_2.wav"])
async_microphones_stop_recording(microphones)
for microphone in microphones.values():
assert not microphone.is_recording
assert microphone.is_connected
assert not microphone.is_writing
assert Path("test_1.wav").exists()
assert Path("test_2.wav").exists()