Major changes: - Organized Python v1 implementation into v1/ subdirectory - Created Rust workspace with 9 modular crates: - wifi-densepose-core: Core types, traits, errors - wifi-densepose-signal: CSI processing, phase sanitization, FFT - wifi-densepose-nn: Neural network inference (ONNX/Candle/tch) - wifi-densepose-api: Axum-based REST/WebSocket API - wifi-densepose-db: SQLx database layer - wifi-densepose-config: Configuration management - wifi-densepose-hardware: Hardware abstraction - wifi-densepose-wasm: WebAssembly bindings - wifi-densepose-cli: Command-line interface Documentation: - ADR-001: Workspace structure - ADR-002: Signal processing library selection - ADR-003: Neural network inference strategy - DDD domain model with bounded contexts Testing: - 69 tests passing across all crates - Signal processing: 45 tests - Neural networks: 21 tests - Core: 3 doc tests Performance targets: - 10x faster CSI processing (~0.5ms vs ~5ms) - 5x lower memory usage (~100MB vs ~500MB) - WASM support for browser deployment
107 lines
4.0 KiB
Python
107 lines
4.0 KiB
Python
import pytest
|
|
import numpy as np
|
|
from unittest.mock import Mock, patch
|
|
from src.core.phase_sanitizer import PhaseSanitizer
|
|
|
|
|
|
class TestPhaseSanitizer:
|
|
"""Test suite for Phase Sanitizer following London School TDD principles"""
|
|
|
|
@pytest.fixture
|
|
def mock_phase_data(self):
|
|
"""Generate synthetic phase data for testing"""
|
|
# Phase data with unwrapping issues and outliers
|
|
return np.array([
|
|
[0.1, 0.2, 6.0, 0.4, 0.5], # Contains phase jump at index 2
|
|
[-3.0, -0.1, 0.0, 0.1, 0.2], # Contains wrapped phase at index 0
|
|
[0.0, 0.1, 0.2, 0.3, 0.4] # Clean phase data
|
|
])
|
|
|
|
@pytest.fixture
|
|
def phase_sanitizer(self):
|
|
"""Create Phase Sanitizer instance for testing"""
|
|
return PhaseSanitizer()
|
|
|
|
def test_unwrap_phase_removes_discontinuities(self, phase_sanitizer, mock_phase_data):
|
|
"""Test that phase unwrapping removes 2π discontinuities"""
|
|
# Act
|
|
result = phase_sanitizer.unwrap_phase(mock_phase_data)
|
|
|
|
# Assert
|
|
assert result is not None
|
|
assert isinstance(result, np.ndarray)
|
|
assert result.shape == mock_phase_data.shape
|
|
|
|
# Check that large jumps are reduced
|
|
for i in range(result.shape[0]):
|
|
phase_diffs = np.abs(np.diff(result[i]))
|
|
assert np.all(phase_diffs < np.pi) # No jumps larger than π
|
|
|
|
def test_remove_outliers_filters_anomalous_values(self, phase_sanitizer, mock_phase_data):
|
|
"""Test that outlier removal filters anomalous phase values"""
|
|
# Arrange - Add clear outliers
|
|
outlier_data = mock_phase_data.copy()
|
|
outlier_data[0, 2] = 100.0 # Clear outlier
|
|
|
|
# Act
|
|
result = phase_sanitizer.remove_outliers(outlier_data)
|
|
|
|
# Assert
|
|
assert result is not None
|
|
assert isinstance(result, np.ndarray)
|
|
assert result.shape == outlier_data.shape
|
|
assert np.abs(result[0, 2]) < 10.0 # Outlier should be corrected
|
|
|
|
def test_smooth_phase_reduces_noise(self, phase_sanitizer, mock_phase_data):
|
|
"""Test that phase smoothing reduces noise while preserving trends"""
|
|
# Arrange - Add noise
|
|
noisy_data = mock_phase_data + np.random.normal(0, 0.1, mock_phase_data.shape)
|
|
|
|
# Act
|
|
result = phase_sanitizer.smooth_phase(noisy_data)
|
|
|
|
# Assert
|
|
assert result is not None
|
|
assert isinstance(result, np.ndarray)
|
|
assert result.shape == noisy_data.shape
|
|
|
|
# Smoothed data should have lower variance
|
|
original_variance = np.var(noisy_data)
|
|
smoothed_variance = np.var(result)
|
|
assert smoothed_variance <= original_variance
|
|
|
|
def test_sanitize_handles_empty_input(self, phase_sanitizer):
|
|
"""Test that sanitizer handles empty input gracefully"""
|
|
# Arrange
|
|
empty_data = np.array([])
|
|
|
|
# Act & Assert
|
|
with pytest.raises(ValueError, match="Phase data cannot be empty"):
|
|
phase_sanitizer.sanitize(empty_data)
|
|
|
|
def test_sanitize_full_pipeline_integration(self, phase_sanitizer, mock_phase_data):
|
|
"""Test that full sanitization pipeline works correctly"""
|
|
# Act
|
|
result = phase_sanitizer.sanitize(mock_phase_data)
|
|
|
|
# Assert
|
|
assert result is not None
|
|
assert isinstance(result, np.ndarray)
|
|
assert result.shape == mock_phase_data.shape
|
|
|
|
# Result should be within reasonable phase bounds
|
|
assert np.all(result >= -2*np.pi)
|
|
assert np.all(result <= 2*np.pi)
|
|
|
|
def test_sanitize_performance_requirement(self, phase_sanitizer, mock_phase_data):
|
|
"""Test that phase sanitization meets performance requirements (<5ms)"""
|
|
import time
|
|
|
|
# Act
|
|
start_time = time.time()
|
|
result = phase_sanitizer.sanitize(mock_phase_data)
|
|
processing_time = time.time() - start_time
|
|
|
|
# Assert
|
|
assert processing_time < 0.005 # <5ms requirement
|
|
assert result is not None |