Spaces:
Sleeping
Sleeping
| """Tests for GradioAudioInput class.""" | |
| from unittest.mock import Mock | |
| import numpy as np | |
| import pytest | |
| from improvisation_lab.infrastructure.audio import WebAudioProcessor | |
| class TestGradioAudioInput: | |
| def init_module(self): | |
| """Initialize test module.""" | |
| self.sample_rate = 44100 | |
| self.buffer_duration = 0.2 | |
| self.audio_input = WebAudioProcessor( | |
| sample_rate=self.sample_rate, | |
| buffer_duration=self.buffer_duration, | |
| ) | |
| def test_initialization(self): | |
| """Test initialization of GradioAudioInput.""" | |
| assert self.audio_input.sample_rate == 44100 | |
| assert not self.audio_input.is_recording | |
| assert self.audio_input._callback is None | |
| assert len(self.audio_input._buffer) == 0 | |
| assert self.audio_input._buffer_size == int(44100 * 0.2) | |
| def test_start_recording(self): | |
| """Test recording start functionality.""" | |
| self.audio_input.start_recording() | |
| assert self.audio_input.is_recording | |
| def test_start_recording_when_already_recording(self): | |
| """Test that starting recording when already recording raises RuntimeError.""" | |
| self.audio_input.is_recording = True | |
| with pytest.raises(RuntimeError, match="Recording is already in progress"): | |
| self.audio_input.start_recording() | |
| def test_stop_recording(self): | |
| """Test recording stop functionality.""" | |
| self.audio_input.is_recording = True | |
| self.audio_input._buffer = np.array([0.1, 0.2], dtype=np.float32) | |
| self.audio_input.stop_recording() | |
| assert not self.audio_input.is_recording | |
| assert len(self.audio_input._buffer) == 0 | |
| def test_stop_recording_when_not_recording(self): | |
| """Test that stopping recording when not recording raises RuntimeError.""" | |
| with pytest.raises(RuntimeError, match="Recording is not in progress"): | |
| self.audio_input.stop_recording() | |
| def test_append_to_buffer(self): | |
| """Test appending data to the buffer.""" | |
| initial_data = np.array([0.1, 0.2], dtype=np.float32) | |
| new_data = np.array([0.3, 0.4], dtype=np.float32) | |
| expected_data = np.array([0.1, 0.2, 0.3, 0.4], dtype=np.float32) | |
| self.audio_input._buffer = initial_data | |
| self.audio_input._append_to_buffer(new_data) | |
| np.testing.assert_array_almost_equal(self.audio_input._buffer, expected_data) | |
| def test_process_buffer(self): | |
| """Test processing buffer when it reaches the desired size.""" | |
| # Setup buffer with more data than buffer_size | |
| buffer_size = self.audio_input._buffer_size | |
| test_data = np.array([0.1] * (buffer_size + 2), dtype=np.float32) | |
| self.audio_input._buffer = test_data | |
| # Setup mock callback | |
| mock_callback = Mock() | |
| self.audio_input._callback = mock_callback | |
| # Process buffer | |
| self.audio_input._process_buffer() | |
| # Verify callback was called with correct data | |
| np.testing.assert_array_almost_equal( | |
| mock_callback.call_args[0][0], test_data[:buffer_size] | |
| ) | |
| # Verify remaining data in buffer | |
| np.testing.assert_array_almost_equal( | |
| self.audio_input._buffer, test_data[buffer_size:] | |
| ) | |
| def test_resample_audio(self): | |
| """Test audio resampling functionality.""" | |
| # Create test data at 48000 Hz | |
| duration = 0.1 # seconds | |
| original_sr = 48000 | |
| target_sr = 44100 | |
| t = np.linspace(0, duration, int(original_sr * duration)) | |
| test_data = np.sin(2 * np.pi * 440 * t).astype(np.float32) # 440 Hz sine wave | |
| # Resample to 44100 Hz | |
| resampled_data = self.audio_input._resample_audio( | |
| test_data, original_sr, target_sr | |
| ) | |
| # Check that the length is correct | |
| expected_length = round(len(test_data) * float(target_sr) / original_sr) | |
| assert len(resampled_data) == expected_length | |
| # Check that the data is still a float32 array | |
| assert resampled_data.dtype == np.float32 | |
| # Check that the signal maintains similar characteristics | |
| assert np.allclose( | |
| np.mean(np.abs(test_data)), np.mean(np.abs(resampled_data)), rtol=0.1 | |
| ) | |
| def test_normalize_audio_normal_case(self): | |
| """Test audio normalization with non-zero data.""" | |
| # Test data with known max value | |
| test_data = np.array([0.5, -1.0, 0.25], dtype=np.float32) | |
| normalized_data = self.audio_input._normalize_audio(test_data) | |
| # Check that the maximum absolute value is 1.0 | |
| assert np.max(np.abs(normalized_data)) == 1.0 | |
| # Check that the relative relationships are preserved | |
| np.testing.assert_array_almost_equal( | |
| normalized_data, np.array([0.5, -1.0, 0.25], dtype=np.float32) | |
| ) | |
| def test_normalize_audio_empty_array(self): | |
| """Test audio normalization with empty array.""" | |
| test_data = np.array([], dtype=np.float32) | |
| normalized_data = self.audio_input._normalize_audio(test_data) | |
| assert len(normalized_data) == 0 | |
| assert normalized_data.dtype == np.float32 | |
| def test_normalize_audio_zero_array(self): | |
| """Test audio normalization with array of zeros.""" | |
| test_data = np.zeros(5, dtype=np.float32) | |
| normalized_data = self.audio_input._normalize_audio(test_data) | |
| # Should return the same array of zeros without division | |
| np.testing.assert_array_equal(normalized_data, test_data) | |
| assert normalized_data.dtype == np.float32 | |
| def test_remove_low_amplitude_noise_normal_case(self): | |
| """Test noise removal with mixed amplitude signals.""" | |
| # Test data with both high and low amplitude signals | |
| test_data = np.array([5.0, -25.0, 0.5, 30.0, 15.0, 1.0], dtype=np.float32) | |
| processed_data = self.audio_input._remove_low_amplitude_noise(test_data) | |
| # Values below threshold (20.0) should be zero | |
| expected_data = np.array([0.0, -25.0, 0.0, 30.0, 0.0, 0.0], dtype=np.float32) | |
| np.testing.assert_array_equal(processed_data, expected_data) | |
| def test_remove_low_amplitude_noise_all_below_threshold(self): | |
| """Test noise removal when all signals are below threshold.""" | |
| test_data = np.array([1.0, -5.0, 0.5, 10.0, 15.0], dtype=np.float32) | |
| processed_data = self.audio_input._remove_low_amplitude_noise(test_data) | |
| # All values should be zero as they're below threshold | |
| expected_data = np.zeros_like(test_data) | |
| np.testing.assert_array_equal(processed_data, expected_data) | |
| def test_remove_low_amplitude_noise_empty_array(self): | |
| """Test noise removal with empty array.""" | |
| test_data = np.array([], dtype=np.float32) | |
| processed_data = self.audio_input._remove_low_amplitude_noise(test_data) | |
| assert len(processed_data) == 0 | |
| assert processed_data.dtype == np.float32 | |