From 0de7e0591c71eaecea2d4343d96fbd0117ab7c2c Mon Sep 17 00:00:00 2001 From: Repinoid Date: Sat, 6 Jun 2026 07:21:36 +0400 Subject: [PATCH] =?UTF-8?q?fix:=20Rsp.identify=20NO=20DATA=20(=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D0=B1=D0=B5=D0=BB);=20119=20=D1=82=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- obd/state.py | 5 +- tests/test_all.py | 114 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) diff --git a/obd/state.py b/obd/state.py index 6936819..939769c 100644 --- a/obd/state.py +++ b/obd/state.py @@ -53,7 +53,10 @@ class Rsp: def identify(cls, raw: str) -> str: """Определяет тип ответа по сырой строке.""" u = raw.upper().strip() - for tag in (cls.SEARCHING, cls.NODATA, cls.ERROR, cls.UNABLE, + # NO DATA бывает как "NODATA" так и "NO DATA" + if u.replace(" ", "") == "NODATA": + return cls.NODATA + for tag in (cls.SEARCHING, cls.ERROR, cls.UNABLE, cls.BUS_BUSY, cls.BUS_ERROR, cls.CAN_ERROR, cls.BUS_INIT, cls.STOPPED, cls.DATA_ERROR, cls.BUFFER_FULL, cls.RX_ERROR, cls.OK): diff --git a/tests/test_all.py b/tests/test_all.py index e978969..ee35233 100644 --- a/tests/test_all.py +++ b/tests/test_all.py @@ -490,3 +490,117 @@ test("5 разных request_id — все сохранены", db3.close() if TEST_DB.exists(): TEST_DB.unlink() + +# ── 14. DTC модуль (api/dtc.py) ───────────────────────────────── +print("\n═══ 14. DTC модуль ═══") + +# Загрузка словаря из dtc.py +dtc = _load_dtc_dict() +test("dtc загружен из dtc.py", len(dtc) > 100) + +# Известные коды +test("P0301 = пропуски", "Пропуски" in dtc.get("P0301", "")) +test("P0420 = катализатор", "катализатор" in dtc.get("P0420", "").lower()) + +# Протокольные коды +test("ABS C0000", "ABS" in dtc.get("C0000", "")) +test("шина U0000", "шина" in dtc.get("U0000", "").lower()) +test("кузов B0000", "кузов" in dtc.get("B0000", "").lower()) + +# ── 15. Ping модуль (api/ping.py) ──────────────────────────────── +print("\n═══ 15. Ping модуль ═══") + +from api.ping import _ping_llm_cache + +# Изначально кэш пуст +test("ping кэш пуст изначально", _ping_llm_cache == {}) + +# ── 16. State модуль (obd/state.py) ───────────────────────────── +print("\n═══ 16. State ELM ═══") + +from obd.state import State, Rsp + +# State enum +test("State.UNDEFINED", State.UNDEFINED is not None) +test("State.READY", State.READY is not None) +test("State.ERROR", State.ERROR is not None) + +# Rsp.identify +test("Rsp.identify OK", Rsp.identify("OK") == Rsp.OK) +test("Rsp.identify BUS ERROR", Rsp.identify("BUS ERROR") == Rsp.BUS_ERROR) +test("Rsp.identify SEARCHING", Rsp.identify("SEARCHING...") == Rsp.SEARCHING) +test("Rsp.identify NO DATA", Rsp.identify("NO DATA") == Rsp.NODATA) +test("Rsp.identify CAN ERROR", Rsp.identify("CAN ERROR") == Rsp.CAN_ERROR) +test("Rsp.identify BUFFER FULL", Rsp.identify("BUFFER FULL") == Rsp.BUFFER_FULL) +test("Rsp.identify STOPPED", Rsp.identify("STOPPED") == Rsp.STOPPED) +test("Rsp.identify UNABLE", Rsp.identify("UNABLE TO CONNECT") == Rsp.UNABLE) +test("Rsp.identify RX ERROR", Rsp.identify("RX ERROR") == Rsp.RX_ERROR) +test("Rsp.identify DATA ERROR", Rsp.identify("DATA ERROR") == Rsp.DATA_ERROR) + +# PROMPT +test("Rsp.identify PROMPT >", Rsp.identify(">") == Rsp.PROMPT) + +# UNKNOWN — данные +test("Rsp.identify UNKNOWN — данные", Rsp.identify("41 0C 0C A8") == Rsp.UNKNOWN) +test("Rsp.identify UNKNOWN — VIN", Rsp.identify("49 02 01 57 56") == Rsp.UNKNOWN) + +# Нижний регистр, пробелы +test("Rsp.identify lower case", Rsp.identify("bus error") == Rsp.BUS_ERROR) +test("Rsp.identify пробелы", Rsp.identify(" OK ") == Rsp.OK) + +# ── 17. Timing модуль (obd/timing.py) ──────────────────────────── +print("\n═══ 17. Timing ELM ═══") + +from obd.timing import AdaptiveTiming + +t = AdaptiveTiming() +test("timing DEFAULT", t.ms == 500) +test("atst = ms/4", t.atst == 125) + +t.increase() +test("timing увеличился", t.ms > 500) + +t.decrease() +test("timing уменьшился", t.ms >= 500) + +t.reset() +test("timing сброс", t.ms == 500) + +# Многократное увеличение +for _ in range(100): + t.increase() +test("timing MAX не превышен", t.ms <= 2000) + +# Многократное уменьшение +for _ in range(100): + t.decrease() +test("timing MIN не превышен", t.ms >= 50) + +t2 = AdaptiveTiming() +t2.increase() +t2.increase() +v = t2.ms +t2.reset() +test("reset после increase", t2.ms == 500 and v > 500) + +atst = t2.atst +test("atst >= 1", atst >= 1) + +# ── 18. DB — только sessions (мёртвые таблицы удалены) ────────── +print("\n═══ 18. DB чистота ═══") + +import sqlite3 +db4 = Database(TEST_DB) +tables = [r[0] for r in db4.conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()] +test("нет мёртвых таблиц (cars,tokens)", not any(t in tables for t in ["cars", "diagnostic_tokens", "dtc_codes"])) + +# Убедиться что мёртвые методы вызывают AttributeError +test("get_or_create_car удалён", not hasattr(db4, "get_or_create_car")) +test("create_token удалён", not hasattr(db4, "create_token")) +test("add_llm_message удалён", not hasattr(db4, "add_llm_message")) +test("add_parameter удалён", not hasattr(db4, "add_parameter")) +test("add_dtc удалён", not hasattr(db4, "add_dtc")) + +db4.close() +if TEST_DB.exists(): + TEST_DB.unlink()