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
+314 -230
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,324 +43,406 @@ 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."""
if LEROBOT_USE_REAL_PORTAUDIO_MICROPHONE_TESTS:
return SounddeviceSDKAdapter()
else:
return FakeSounddeviceSDKAdapter()
def test_config_creation(self):
# Configuration Tests
def test_config_creation():
"""Test creating a valid configuration.""" """Test creating a valid configuration."""
config = PortAudioMicrophoneConfig(microphone_index=0, sample_rate=48000, channels=[1, 2]) config = PortAudioMicrophoneConfig(microphone_index=0, sample_rate=48000, channels=[1, 2])
self.assertEqual(config.microphone_index, 0) assert config.microphone_index == 0
self.assertEqual(config.sample_rate, 48000) assert config.sample_rate == 48000
self.assertEqual(config.channels, [1, 2]) assert config.channels == [1, 2]
def test_config_creation_missing_microphone_index(self):
def test_config_creation_missing_microphone_index():
"""Test creating a configuration with missing microphone index.""" """Test creating a configuration with missing microphone index."""
with self.assertRaises(TypeError): with pytest.raises(TypeError):
PortAudioMicrophoneConfig(sample_rate=48000, channels=[1, 2]) PortAudioMicrophoneConfig(sample_rate=48000, channels=[1, 2])
def test_config_creation_missing_sample_rate(self):
def test_config_creation_missing_sample_rate():
"""Test creating a configuration with missing sample rate.""" """Test creating a configuration with missing sample rate."""
config = PortAudioMicrophoneConfig(microphone_index=0, channels=[1, 2]) config = PortAudioMicrophoneConfig(microphone_index=0, channels=[1, 2])
self.assertIsNone(config.sample_rate) assert config.sample_rate is None
def test_config_creation_missing_channels(self):
def test_config_creation_missing_channels():
"""Test creating a configuration with missing channels.""" """Test creating a configuration with missing channels."""
config = PortAudioMicrophoneConfig(microphone_index=0, sample_rate=48000) config = PortAudioMicrophoneConfig(microphone_index=0, sample_rate=48000)
self.assertIsNone(config.channels) assert config.channels is None
class TestPortAudioMicrophoneDeviceValidation(unittest.TestCase): @pytest.fixture
"""Test device validation and configuration.""" 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,
)
def setUp(self):
if LEROBOT_USE_REAL_PORTAUDIO_MICROPHONE_TESTS:
self.test_sdk = SounddeviceSDKAdapter()
else:
self.test_sdk = FakeSounddeviceSDKAdapter()
def _create_config( # Microphone Tests
device: int | str | None = None, kind: str | None = None
) -> PortAudioMicrophoneConfig:
device_info = self.test_sdk.query_devices(device, kind) 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()
def test_connect_invalid_device(test_sdk):
"""Test connecting with invalid device (output device)."""
device_info = test_sdk.query_devices(kind="output")
config = PortAudioMicrophoneConfig( config = PortAudioMicrophoneConfig(
microphone_index=device_info["index"], microphone_index=device_info["index"],
sample_rate=device_info["default_samplerate"], sample_rate=device_info["default_samplerate"],
channels=np.arange(device_info["max_input_channels"]) + 1, channels=np.arange(device_info["max_input_channels"]) + 1,
) )
return config microphone = PortAudioMicrophone(config, sounddevice_sdk=test_sdk)
self._create_config = _create_config with pytest.raises(RuntimeError):
self.default_config = self._create_config(kind="input")
def test_find_microphones(self):
microphones = PortAudioMicrophone.find_microphones(sounddevice_sdk=self.test_sdk)
for microphone in microphones:
self.assertIsInstance(microphone["index"], int)
self.assertIsInstance(microphone["name"], str)
self.assertIsInstance(microphone["sample_rate"], int)
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")
self.assertIsNotNone(microphone)
self.assertEqual(microphone.microphone_index, device_info["index"])
self.assertEqual(microphone.sample_rate, device_info["default_samplerate"])
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)
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_index(default_config, test_sdk):
config = deepcopy(self.default_config) """Test connecting with invalid device index."""
config.sample_rate = None config = deepcopy(default_config)
config.channels = None
microphone = PortAudioMicrophone(config, sounddevice_sdk=self.test_sdk)
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):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk)
microphone.connect()
with self.assertRaises(DeviceAlreadyConnectedError):
microphone.connect()
def test_connect_invalid_device(self):
config = self._create_config(kind="output")
microphone = PortAudioMicrophone(config, sounddevice_sdk=self.test_sdk)
with self.assertRaises(RuntimeError):
microphone.connect()
def test_connect_invalid_index(self):
config = deepcopy(self.default_config)
config.microphone_index = -1 config.microphone_index = -1
microphone = PortAudioMicrophone(config, sounddevice_sdk=self.test_sdk) microphone = PortAudioMicrophone(config, sounddevice_sdk=test_sdk)
with self.assertRaises(RuntimeError): with pytest.raises(RuntimeError):
microphone.connect() microphone.connect()
def test_connect_invalid_sample_rate(self):
config = deepcopy(self.default_config) def test_connect_invalid_sample_rate(default_config, test_sdk):
"""Test connecting with invalid sample rate."""
config = deepcopy(default_config)
config.sample_rate = -1 config.sample_rate = -1
microphone = PortAudioMicrophone(config, sounddevice_sdk=self.test_sdk) microphone = PortAudioMicrophone(config, sounddevice_sdk=test_sdk)
with self.assertRaises(RuntimeError): with pytest.raises(RuntimeError):
microphone.connect() microphone.connect()
def test_connect_float_sample_rate(self):
config = deepcopy(self.default_config) def test_connect_float_sample_rate(default_config, test_sdk):
"""Test connecting with float sample rate."""
config = deepcopy(default_config)
config.sample_rate = int(config.sample_rate) - 0.5 config.sample_rate = int(config.sample_rate) - 0.5
microphone = PortAudioMicrophone(config, sounddevice_sdk=self.test_sdk) microphone = PortAudioMicrophone(config, sounddevice_sdk=test_sdk)
microphone.connect() microphone.connect()
self.assertIsInstance(microphone.sample_rate, int) assert isinstance(microphone.sample_rate, int)
self.assertEqual(microphone.sample_rate, int(config.sample_rate)) assert microphone.sample_rate == int(config.sample_rate)
def test_connect_lower_sample_rate(self):
config = deepcopy(self.default_config) 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 config.sample_rate = 1000 # Lowest possible sample rate
microphone = PortAudioMicrophone(config, sounddevice_sdk=self.test_sdk) microphone = PortAudioMicrophone(config, sounddevice_sdk=test_sdk)
microphone.connect() microphone.connect()
self.assertEqual(microphone.sample_rate, 1000) assert 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_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()
def test_disconnect_success(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) def test_disconnect_success(default_config, test_sdk):
"""Test successful disconnection."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect() microphone.connect()
microphone.disconnect() microphone.disconnect()
self.assertFalse(microphone.is_connected) assert not microphone.is_connected
self.assertFalse(microphone.is_recording) assert not microphone.is_recording
self.assertFalse(microphone.is_writing) assert not microphone.is_writing
def test_disconnect_not_connected(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk)
with self.assertRaises(DeviceNotConnectedError): 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()
def test_start_recording_success(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) @pytest.mark.parametrize("multiprocessing", [True, False])
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.connect()
microphone.start_recording() microphone.start_recording(multiprocessing=multiprocessing)
self.assertTrue(microphone.is_recording) assert microphone.is_recording
self.assertTrue(microphone.is_connected) assert microphone.is_connected
self.assertFalse(microphone.is_writing) assert not microphone.is_writing
def test_recoring_not_connected(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk)
with self.assertRaises(DeviceNotConnectedError): @pytest.mark.parametrize("multiprocessing", [True, False])
microphone.start_recording() def test_recording_not_connected(default_config, test_sdk, multiprocessing):
"""Test starting recording when not connected."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
def test_start_recording_already_recording(self): with pytest.raises(DeviceNotConnectedError):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) microphone.start_recording(multiprocessing=multiprocessing)
@pytest.mark.parametrize("multiprocessing", [True, False])
def test_start_recording_already_recording(default_config, test_sdk, multiprocessing):
"""Test starting recording when already recording."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect() microphone.connect()
microphone.start_recording() 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) @pytest.mark.parametrize("multiprocessing", [True, False])
def test_start_writing_success(tmp_path, default_config, test_sdk, multiprocessing):
"""Test successful writing start."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect() microphone.connect()
microphone.start_recording(output_file="test.wav") microphone.start_recording(output_file=tmp_path / "test.wav", multiprocessing=multiprocessing)
self.assertTrue(microphone.is_recording) assert microphone.is_recording
self.assertTrue(microphone.is_connected) assert microphone.is_connected
self.assertTrue(microphone.is_writing) assert microphone.is_writing
self.assertTrue(Path("test.wav").exists()) assert (tmp_path / "test.wav").exists()
def test_stop_recording_success(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) @pytest.mark.parametrize("multiprocessing", [True, False])
def test_start_writing_file_already_exists_no_overwrite(tmp_path, default_config, test_sdk, multiprocessing):
"""Test writing with file that already exists."""
(tmp_path / "test.wav").touch()
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect() microphone.connect()
microphone.start_recording()
with pytest.raises(FileExistsError):
microphone.start_recording(
output_file=tmp_path / "test.wav", multiprocessing=multiprocessing, overwrite=False
)
(tmp_path / "test.wav").unlink()
@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) busy_wait(RECORDING_DURATION)
microphone.stop_recording() microphone.stop_recording()
self.assertFalse(microphone.is_recording) assert not microphone.is_recording
self.assertTrue(microphone.is_connected) assert microphone.is_connected
self.assertFalse(microphone.is_writing) assert not microphone.is_writing
def test_stop_writing_success(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) @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.connect()
microphone.start_recording(output_file="test.wav") microphone.start_recording(output_file=tmp_path / "test.wav", multiprocessing=multiprocessing)
busy_wait(RECORDING_DURATION) busy_wait(RECORDING_DURATION)
microphone.stop_recording() microphone.stop_recording()
self.assertFalse(microphone.is_recording) assert not microphone.is_recording
self.assertTrue(microphone.is_connected) assert microphone.is_connected
self.assertFalse(microphone.is_writing) assert not microphone.is_writing
self.assertTrue(Path("test.wav").exists()) assert (tmp_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): 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()
def test_stop_recording_not_recording(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) def test_stop_recording_not_recording(default_config, test_sdk):
"""Test stopping recording when not recording."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect() microphone.connect()
with self.assertRaises(DeviceNotRecordingError): with pytest.raises(DeviceNotRecordingError):
microphone.stop_recording() microphone.stop_recording()
def test_disconnect_while_recording(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) @pytest.mark.parametrize("multiprocessing", [True, False])
def test_disconnect_while_recording(default_config, test_sdk, multiprocessing):
"""Test disconnecting while recording."""
microphone = PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk)
microphone.connect() microphone.connect()
microphone.start_recording() microphone.start_recording(multiprocessing=multiprocessing)
busy_wait(RECORDING_DURATION) busy_wait(RECORDING_DURATION)
microphone.disconnect() microphone.disconnect()
self.assertFalse(microphone.is_connected) assert not microphone.is_connected
self.assertFalse(microphone.is_recording) assert not microphone.is_recording
self.assertFalse(microphone.is_writing) assert not microphone.is_writing
def test_disconnect_while_writing(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) @pytest.mark.parametrize("multiprocessing", [True, 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.connect()
microphone.start_recording(output_file="test.wav") microphone.start_recording(output_file=tmp_path / "test.wav", multiprocessing=multiprocessing)
busy_wait(RECORDING_DURATION) busy_wait(RECORDING_DURATION)
microphone.disconnect() microphone.disconnect()
self.assertFalse(microphone.is_connected) assert not microphone.is_connected
self.assertFalse(microphone.is_recording) assert not microphone.is_recording
self.assertFalse(microphone.is_writing) assert not microphone.is_writing
self.assertTrue(Path("test.wav").exists()) assert Path("test.wav").exists()
def test_read_success(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) @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.connect()
microphone.start_recording() microphone.start_recording(multiprocessing=multiprocessing)
busy_wait(RECORDING_DURATION) busy_wait(RECORDING_DURATION)
data = microphone.read() data = microphone.read()
device_info = self.test_sdk.query_devices(kind="input") device_info = test_sdk.query_devices(kind="input")
self.assertIsNotNone(data) assert data is not None
self.assertEqual(data.shape[1], len(self.default_config.channels)) assert data.shape[1] == len(default_config.channels)
self.assertAlmostEqual( assert (
data.shape[0], abs(data.shape[0] - RECORDING_DURATION * default_config.sample_rate)
RECORDING_DURATION * self.default_config.sample_rate, <= 2 * default_config.sample_rate * device_info["default_low_input_latency"]
delta=2 * self.default_config.sample_rate * device_info["default_low_input_latency"],
) )
def test_writing_success(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) @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.connect()
microphone.start_recording(output_file="test.wav") microphone.start_recording(output_file=tmp_path / "test.wav", multiprocessing=multiprocessing)
busy_wait(RECORDING_DURATION) busy_wait(RECORDING_DURATION)
microphone.stop_recording() microphone.stop_recording()
data, samplerate = read("test.wav") data, samplerate = read(tmp_path / "test.wav")
device_info = self.test_sdk.query_devices(kind="input") device_info = test_sdk.query_devices(kind="input")
self.assertEqual(samplerate, self.default_config.sample_rate) assert samplerate == default_config.sample_rate
self.assertEqual(data.shape[1], len(self.default_config.channels)) assert data.shape[1] == len(default_config.channels)
self.assertAlmostEqual( assert (
data.shape[0], abs(data.shape[0] - RECORDING_DURATION * default_config.sample_rate)
RECORDING_DURATION * self.default_config.sample_rate, <= 2 * default_config.sample_rate * device_info["default_low_input_latency"]
delta=2 * self.default_config.sample_rate * device_info["default_low_input_latency"],
) )
def test_read_while_writing(self):
microphone = PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk) @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.connect()
microphone.start_recording(output_file="test.wav") microphone.start_recording(output_file=tmp_path / "test.wav", multiprocessing=multiprocessing)
busy_wait(RECORDING_DURATION) busy_wait(RECORDING_DURATION)
read_data = microphone.read() read_data = microphone.read()
microphone.stop_recording() microphone.stop_recording()
writing_data, _ = read("test.wav") writing_data, _ = read(tmp_path / "test.wav")
device_info = self.test_sdk.query_devices(kind="input") device_info = test_sdk.query_devices(kind="input")
self.assertAlmostEqual( assert (
writing_data.shape[0], abs(writing_data.shape[0] - RECORDING_DURATION * default_config.sample_rate)
RECORDING_DURATION * self.default_config.sample_rate, <= 2 * default_config.sample_rate * device_info["default_low_input_latency"]
delta=2 * self.default_config.sample_rate * device_info["default_low_input_latency"],
) )
self.assertAlmostEqual( assert (
read_data.shape[0], abs(read_data.shape[0] - RECORDING_DURATION * default_config.sample_rate)
RECORDING_DURATION * self.default_config.sample_rate, <= 2 * default_config.sample_rate * device_info["default_low_input_latency"]
delta=2 * self.default_config.sample_rate * device_info["default_low_input_latency"],
) )
def test_async_start_recording(self):
def test_async_start_recording(default_config, test_sdk):
"""Test async recording start."""
microphones = { microphones = {
"microphone_1": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk), "microphone_1": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
"microphone_2": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk), "microphone_2": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
} }
for microphone in microphones.values(): for microphone in microphones.values():
microphone.connect() microphone.connect()
@@ -368,14 +450,16 @@ class TestPortAudioMicrophoneDeviceValidation(unittest.TestCase):
async_microphones_start_recording(microphones) async_microphones_start_recording(microphones)
for microphone in microphones.values(): for microphone in microphones.values():
self.assertTrue(microphone.is_recording) assert microphone.is_recording
self.assertTrue(microphone.is_connected) assert microphone.is_connected
self.assertFalse(microphone.is_writing) assert not microphone.is_writing
def test_async_start_writing(self):
def test_async_start_writing(default_config, test_sdk):
"""Test async writing start."""
microphones = { microphones = {
"microphone_1": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk), "microphone_1": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
"microphone_2": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk), "microphone_2": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
} }
for microphone in microphones.values(): for microphone in microphones.values():
microphone.connect() microphone.connect()
@@ -383,16 +467,18 @@ class TestPortAudioMicrophoneDeviceValidation(unittest.TestCase):
async_microphones_start_recording(microphones, output_files=["test_1.wav", "test_2.wav"]) async_microphones_start_recording(microphones, output_files=["test_1.wav", "test_2.wav"])
for microphone in microphones.values(): for microphone in microphones.values():
self.assertTrue(microphone.is_recording) assert microphone.is_recording
self.assertTrue(microphone.is_connected) assert microphone.is_connected
self.assertTrue(microphone.is_writing) assert microphone.is_writing
self.assertTrue(Path("test_1.wav").exists()) assert Path("test_1.wav").exists()
self.assertTrue(Path("test_2.wav").exists()) assert Path("test_2.wav").exists()
def test_async_stop_recording(self):
def test_async_stop_recording(default_config, test_sdk):
"""Test async recording stop."""
microphones = { microphones = {
"microphone_1": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk), "microphone_1": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
"microphone_2": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk), "microphone_2": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
} }
for microphone in microphones.values(): for microphone in microphones.values():
microphone.connect() microphone.connect()
@@ -401,14 +487,16 @@ class TestPortAudioMicrophoneDeviceValidation(unittest.TestCase):
async_microphones_stop_recording(microphones) async_microphones_stop_recording(microphones)
for microphone in microphones.values(): for microphone in microphones.values():
self.assertFalse(microphone.is_recording) assert not microphone.is_recording
self.assertTrue(microphone.is_connected) assert microphone.is_connected
self.assertFalse(microphone.is_writing) assert not microphone.is_writing
def test_async_stop_writing(self):
def test_async_stop_writing(default_config, test_sdk):
"""Test async writing stop."""
microphones = { microphones = {
"microphone_1": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk), "microphone_1": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
"microphone_2": PortAudioMicrophone(self.default_config, sounddevice_sdk=self.test_sdk), "microphone_2": PortAudioMicrophone(default_config, sounddevice_sdk=test_sdk),
} }
for microphone in microphones.values(): for microphone in microphones.values():
microphone.connect() microphone.connect()
@@ -417,12 +505,8 @@ class TestPortAudioMicrophoneDeviceValidation(unittest.TestCase):
async_microphones_stop_recording(microphones) async_microphones_stop_recording(microphones)
for microphone in microphones.values(): for microphone in microphones.values():
self.assertFalse(microphone.is_recording) assert not microphone.is_recording
self.assertTrue(microphone.is_connected) assert microphone.is_connected
self.assertFalse(microphone.is_writing) assert not microphone.is_writing
self.assertTrue(Path("test_1.wav").exists()) assert Path("test_1.wav").exists()
self.assertTrue(Path("test_2.wav").exists()) assert Path("test_2.wav").exists()
if __name__ == "__main__":
unittest.main(argv=["first-arg-is-ignored"], exit=False)