mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 22:59:59 -07:00
163 lines
5.4 KiB
Python
163 lines
5.4 KiB
Python
"""Tests for SDR detection parsing (RTL-SDR and HackRF)."""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
import utils.sdr.detection as detection_mod
|
|
from utils.sdr.base import SDRType
|
|
from utils.sdr.detection import detect_hackrf_devices, detect_rtlsdr_devices
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _clear_detection_caches():
|
|
"""Reset detection caches before each test."""
|
|
detection_mod._hackrf_cache = []
|
|
detection_mod._hackrf_cache_ts = 0.0
|
|
yield
|
|
|
|
|
|
@patch('utils.sdr.detection.get_tool_path', return_value='/usr/bin/rtl_test')
|
|
@patch('utils.sdr.detection.subprocess.run')
|
|
def test_detect_rtlsdr_devices_filters_empty_serial_entries(mock_run, _mock_tool_path):
|
|
"""Ignore malformed rtl_test rows that have an empty SN field."""
|
|
mock_result = MagicMock()
|
|
mock_result.stdout = ""
|
|
mock_result.stderr = (
|
|
"Found 3 device(s):\n"
|
|
" 0: ??C?, , SN:\n"
|
|
" 1: ??C?, , SN:\n"
|
|
" 2: RTLSDRBlog, Blog V4, SN: 1\n"
|
|
)
|
|
mock_run.return_value = mock_result
|
|
|
|
devices = detect_rtlsdr_devices()
|
|
|
|
assert len(devices) == 1
|
|
assert devices[0].sdr_type == SDRType.RTL_SDR
|
|
assert devices[0].index == 2
|
|
assert devices[0].name == "RTLSDRBlog, Blog V4"
|
|
assert devices[0].serial == "1"
|
|
|
|
|
|
@patch('utils.sdr.detection.get_tool_path', return_value='/usr/bin/rtl_test')
|
|
@patch('utils.sdr.detection.subprocess.run')
|
|
def test_detect_rtlsdr_devices_uses_replace_decode_mode(mock_run, _mock_tool_path):
|
|
"""Run rtl_test with tolerant decoding for malformed output bytes."""
|
|
mock_result = MagicMock()
|
|
mock_result.stdout = ""
|
|
mock_result.stderr = "Found 0 device(s):"
|
|
mock_run.return_value = mock_result
|
|
|
|
detect_rtlsdr_devices()
|
|
|
|
_, kwargs = mock_run.call_args
|
|
assert kwargs["text"] is True
|
|
assert kwargs["encoding"] == "utf-8"
|
|
assert kwargs["errors"] == "replace"
|
|
|
|
|
|
# ---- HackRF detection tests ----
|
|
|
|
HACKRF_INFO_OUTPUT = (
|
|
"hackrf_info version: 2024.02.1\n"
|
|
"libhackrf version: 2024.02.1 (0.9)\n"
|
|
"Found HackRF\n"
|
|
"Index: 0\n"
|
|
"Serial number: 0000000000000000a06063c8234e925f\n"
|
|
"Board ID Number: 2 (HackRF One)\n"
|
|
"Firmware Version: 2024.02.1 (API:1.08)\n"
|
|
"Part ID Number: 0xa000cb3c 0x00614764\n"
|
|
"Hardware Revision: r9\n"
|
|
"Hardware supported by installed firmware:\n"
|
|
" HackRF One\n"
|
|
)
|
|
|
|
|
|
@patch('utils.sdr.detection.get_tool_path', return_value='/usr/bin/hackrf_info')
|
|
@patch('utils.sdr.detection.subprocess.run')
|
|
def test_detect_hackrf_from_stdout(mock_run, _mock_tool_path):
|
|
"""Parse HackRF device info from stdout."""
|
|
mock_result = MagicMock()
|
|
mock_result.stdout = HACKRF_INFO_OUTPUT
|
|
mock_result.stderr = ""
|
|
mock_run.return_value = mock_result
|
|
|
|
devices = detect_hackrf_devices()
|
|
|
|
assert len(devices) == 1
|
|
assert devices[0].sdr_type == SDRType.HACKRF
|
|
assert devices[0].name == "HackRF One"
|
|
assert devices[0].serial == "0000000000000000a06063c8234e925f"
|
|
assert devices[0].index == 0
|
|
|
|
|
|
@patch('utils.sdr.detection.get_tool_path', return_value='/usr/bin/hackrf_info')
|
|
@patch('utils.sdr.detection.subprocess.run')
|
|
def test_detect_hackrf_from_stderr(mock_run, _mock_tool_path):
|
|
"""Parse HackRF device info when output goes to stderr (newer firmware)."""
|
|
mock_result = MagicMock()
|
|
mock_result.stdout = ""
|
|
mock_result.stderr = HACKRF_INFO_OUTPUT
|
|
mock_run.return_value = mock_result
|
|
|
|
devices = detect_hackrf_devices()
|
|
|
|
assert len(devices) == 1
|
|
assert devices[0].sdr_type == SDRType.HACKRF
|
|
assert devices[0].name == "HackRF One"
|
|
assert devices[0].serial == "0000000000000000a06063c8234e925f"
|
|
|
|
|
|
@patch('utils.sdr.detection.get_tool_path', return_value='/usr/bin/hackrf_info')
|
|
@patch('utils.sdr.detection.subprocess.run')
|
|
def test_detect_hackrf_nonzero_exit_with_valid_output(mock_run, _mock_tool_path):
|
|
"""Parse HackRF info even when hackrf_info exits non-zero (device busy)."""
|
|
mock_result = MagicMock()
|
|
mock_result.returncode = 1
|
|
mock_result.stdout = ""
|
|
mock_result.stderr = HACKRF_INFO_OUTPUT
|
|
mock_run.return_value = mock_result
|
|
|
|
devices = detect_hackrf_devices()
|
|
|
|
assert len(devices) == 1
|
|
assert devices[0].name == "HackRF One"
|
|
|
|
|
|
@patch('utils.sdr.detection.get_tool_path', return_value='/usr/bin/hackrf_info')
|
|
@patch('utils.sdr.detection.subprocess.run')
|
|
def test_detect_hackrf_fallback_no_serial(mock_run, _mock_tool_path):
|
|
"""Fallback detection when serial is missing but 'Found HackRF' present."""
|
|
mock_result = MagicMock()
|
|
mock_result.stdout = "Found HackRF\nBoard ID Number: 2 (HackRF One)\n"
|
|
mock_result.stderr = ""
|
|
mock_run.return_value = mock_result
|
|
|
|
devices = detect_hackrf_devices()
|
|
|
|
assert len(devices) == 1
|
|
assert devices[0].name == "HackRF One"
|
|
assert devices[0].serial == "Unknown"
|
|
|
|
|
|
@patch('utils.sdr.detection.get_tool_path', return_value='/usr/bin/hackrf_info')
|
|
@patch('utils.sdr.detection.subprocess.run')
|
|
def test_detect_hackrf_parses_legacy_serial_format(mock_run, _mock_tool_path):
|
|
"""Accept legacy 'Serial Number' casing and spaced hex format."""
|
|
mock_result = MagicMock()
|
|
mock_result.stdout = (
|
|
"Found HackRF\n"
|
|
"Index: 0\n"
|
|
"Serial Number: 0x00000000 00000000 a06063c8 234e925f\n"
|
|
"Board ID Number: 3 (HackRF Pro)\n"
|
|
)
|
|
mock_result.stderr = ""
|
|
mock_run.return_value = mock_result
|
|
|
|
devices = detect_hackrf_devices()
|
|
|
|
assert len(devices) == 1
|
|
assert devices[0].name == "HackRF Pro"
|
|
assert devices[0].serial == "0000000000000000a06063c8234e925f"
|