#!/usr/bin/env python3 """ Offline unit tests for decoded (KEY) .sub identification against the signature DB. A Flipper KEY file carries no RAW pulses but *names* a decoded protocol (e.g. ``Protocol: Princeton``). When that name is in our signature database this is the strongest possible identification -- the capture device already demodulated and named the protocol and we recognise it -- so it must: - resolve to the REAL catalog signature (true manufacturer + category), not the category router's coarser guess (Princeton is a Garage Door Opener in the catalog, which the router previously mislabelled "Remote Control"), and - score with high, frequency-aware confidence (exact database match), not the ~0.45 fuzzy-guess confidence it used to get. Unknown protocol names must still fall back to category routing so an upload is identified by family rather than silently dropped ("accept any capture -> identify the device"). Offline / author-time: bare ``pytest`` runs this with no docker, no server, no network. KEY .sub content is embedded and written to ``tmp_path``. """ import pytest from src.parser.sub_parser import SubFileParser from src.matcher.engine import SignatureMatcher from src.matcher.pattern_decoder import PatternDecoder from src.matcher.protocol_database import get_protocol_database def _key_sub(protocol: str, frequency: int, bit: int = 24, key: str = "00 00 00 00 00 95 D5 D4") -> str: return "\n".join([ "Filetype: Flipper SubGhz Key File", "Version: 1", f"Frequency: {frequency}", "Preset: FuriHalSubGhzPresetOok270Async", f"Protocol: {protocol}", f"Bit: {bit}", f"Key: {key}", "TE: 400", "", ]) def _write(tmp_path, content, name="k.sub"): p = tmp_path / name p.write_text(content) return str(p) def _identify(tmp_path, content): meta = SubFileParser().parse(_write(tmp_path, content)) return meta, SignatureMatcher().match(meta, max_results=5) # A known catalog protocol and a known one that carries a manufacturer, both # taken from the DB itself so the tests stay correct if the catalog changes. _DB = get_protocol_database() _KNOWN = next(p for p in _DB.get_all()) _KNOWN_WITH_MFR = next((p for p in _DB.get_all() if p.manufacturer), None) _UNKNOWN_NAME = "ZZ_NOT_A_REAL_PROTOCOL_9999" class TestKnownProtocolExactMatch: def test_named_protocol_resolves_to_catalog_signature(self, tmp_path): meta, matches = _identify(tmp_path, _key_sub(_KNOWN.name, _KNOWN.frequency)) assert meta.file_format == "KEY" assert meta.is_decoded is True assert len(matches) >= 1 top = matches[0] assert top.device_name == _KNOWN.name assert top.match_method == "decoded_key_exact" # Category comes from the catalog, not the router's guess. assert top.match_details.get("device_category") == _KNOWN.category def test_exact_match_scores_high_when_frequency_consistent(self, tmp_path): _, matches = _identify(tmp_path, _key_sub(_KNOWN.name, _KNOWN.frequency)) assert matches[0].confidence == pytest.approx(0.95) assert matches[0].match_details.get("frequency_match") is True def test_exact_match_still_high_but_lower_on_frequency_mismatch(self, tmp_path): # Same known name, but a frequency far outside its band. off_band = _KNOWN.frequency + 50_000_000 _, matches = _identify(tmp_path, _key_sub(_KNOWN.name, off_band)) top = matches[0] assert top.match_method == "decoded_key_exact" assert top.confidence == pytest.approx(0.90) assert top.match_details.get("frequency_match") is False @pytest.mark.skipif(_KNOWN_WITH_MFR is None, reason="no catalog protocol carries a manufacturer") def test_real_manufacturer_is_surfaced(self, tmp_path): _, matches = _identify( tmp_path, _key_sub(_KNOWN_WITH_MFR.name, _KNOWN_WITH_MFR.frequency, bit=66) ) assert matches[0].manufacturer == _KNOWN_WITH_MFR.manufacturer class TestUnknownProtocolFallback: def test_unknown_name_falls_back_to_router(self, tmp_path): _, matches = _identify(tmp_path, _key_sub(_UNKNOWN_NAME, 433920000)) assert len(matches) >= 1, "unknown decoded protocol must still be identified by family" top = matches[0] assert top.match_method == "decoded_key" # A routed guess must not masquerade as an exact database match. assert top.confidence < 0.90 def test_unknown_name_still_names_the_protocol(self, tmp_path): _, matches = _identify(tmp_path, _key_sub(_UNKNOWN_NAME, 433920000)) assert matches[0].device_name == _UNKNOWN_NAME class TestLookupHelper: def test_known_name_case_insensitive(self): dec = PatternDecoder() sig = dec._lookup_known_protocol(_KNOWN.name.upper(), _KNOWN.frequency) assert sig is not None assert sig.name == _KNOWN.name def test_unknown_name_returns_none(self): assert PatternDecoder()._lookup_known_protocol(_UNKNOWN_NAME, 433920000) is None def test_empty_name_returns_none(self): assert PatternDecoder()._lookup_known_protocol(None, 433920000) is None def test_prefers_frequency_matching_candidate(self): """If duplicate-named entries exist, the frequency-consistent one wins.""" dec = PatternDecoder() sig = dec._lookup_known_protocol(_KNOWN.name, _KNOWN.frequency) assert sig is not None assert sig.matches_frequency(_KNOWN.frequency)