@mytec: feat: Phase 3.0 Architecture Refactor ✅
Major refactoring of RFCP backend: - Modular propagation models (8 models) - SharedMemoryManager for terrain data - ProcessPoolExecutor parallel processing - WebSocket progress streaming - Building filtering pipeline (351k → 15k) - 82 unit tests Performance: Standard preset 38s → 5s (7.6x speedup) Known issue: Detailed preset timeout (fix in 3.1.0)
This commit is contained in:
90
backend/tests/test_models/test_cost231.py
Normal file
90
backend/tests/test_models/test_cost231.py
Normal file
@@ -0,0 +1,90 @@
|
||||
"""
|
||||
Unit tests for COST-231 Hata and COST-231 Walfisch-Ikegami models.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
from app.propagation.base import PropagationInput
|
||||
from app.propagation.cost231_hata import Cost231HataModel
|
||||
from app.propagation.cost231_wi import Cost231WIModel
|
||||
|
||||
|
||||
def make_input(**kwargs) -> PropagationInput:
|
||||
defaults = {
|
||||
"frequency_mhz": 1800,
|
||||
"distance_m": 5000,
|
||||
"tx_height_m": 30,
|
||||
"rx_height_m": 1.5,
|
||||
"environment": "urban",
|
||||
}
|
||||
defaults.update(kwargs)
|
||||
return PropagationInput(**defaults)
|
||||
|
||||
|
||||
class TestCost231Hata:
|
||||
def test_typical_range(self):
|
||||
model = Cost231HataModel()
|
||||
out = model.calculate(make_input())
|
||||
assert 130 < out.path_loss_db < 170
|
||||
|
||||
def test_model_name(self):
|
||||
model = Cost231HataModel()
|
||||
assert model.name == "COST-231-Hata"
|
||||
|
||||
def test_frequency_range(self):
|
||||
model = Cost231HataModel()
|
||||
assert model.is_valid_for(make_input(frequency_mhz=1500))
|
||||
assert model.is_valid_for(make_input(frequency_mhz=2000))
|
||||
assert not model.is_valid_for(make_input(frequency_mhz=900))
|
||||
|
||||
def test_distance_increases_loss(self):
|
||||
model = Cost231HataModel()
|
||||
loss_2 = model.calculate(make_input(distance_m=2000)).path_loss_db
|
||||
loss_10 = model.calculate(make_input(distance_m=10000)).path_loss_db
|
||||
assert loss_10 > loss_2
|
||||
|
||||
def test_urban_vs_suburban(self):
|
||||
model = Cost231HataModel()
|
||||
urban = model.calculate(make_input(environment="urban")).path_loss_db
|
||||
suburban = model.calculate(make_input(environment="suburban")).path_loss_db
|
||||
assert suburban < urban
|
||||
|
||||
|
||||
class TestCost231WI:
|
||||
def test_typical_range(self):
|
||||
model = Cost231WIModel()
|
||||
out = model.calculate(make_input(distance_m=500))
|
||||
assert 80 < out.path_loss_db < 160
|
||||
|
||||
def test_model_name(self):
|
||||
model = Cost231WIModel()
|
||||
assert model.name == "COST-231-WI"
|
||||
|
||||
def test_distance_increases_loss(self):
|
||||
model = Cost231WIModel()
|
||||
loss_200 = model.calculate(make_input(distance_m=200)).path_loss_db
|
||||
loss_1000 = model.calculate(make_input(distance_m=1000)).path_loss_db
|
||||
assert loss_1000 > loss_200
|
||||
|
||||
def test_frequency_range(self):
|
||||
model = Cost231WIModel()
|
||||
assert model.is_valid_for(make_input(frequency_mhz=800))
|
||||
assert model.is_valid_for(make_input(frequency_mhz=2000))
|
||||
assert not model.is_valid_for(make_input(frequency_mhz=400))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for cls_name, cls in [("COST231Hata", TestCost231Hata), ("COST231WI", TestCost231WI)]:
|
||||
instance = cls()
|
||||
for method_name in [m for m in dir(instance) if m.startswith("test_")]:
|
||||
try:
|
||||
getattr(instance, method_name)()
|
||||
print(f" PASS {cls_name}.{method_name}")
|
||||
except AssertionError as e:
|
||||
print(f" FAIL {cls_name}.{method_name}: {e}")
|
||||
except Exception as e:
|
||||
print(f" ERROR {cls_name}.{method_name}: {e}")
|
||||
print("\nAll tests completed.")
|
||||
Reference in New Issue
Block a user