mirror of
https://github.com/huggingface/lerobot.git
synced 2026-05-21 19:49:49 +00:00
test(Microphone): removing unittest.TestCase class architecture to add tests parametrization on multiprocessing/multithreading use
This commit is contained in:
+409
-325
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user