Files
Balsam_167/tests/can_timeout/run_test.py
T
Andrey 9ddea2d99a Исправить восстановление CAN после тайм-аута передачи в 166
Перенесено из коммита 985804607d40b84f985eb04fc1f1b0a0dcfbe795 ветки 167.
Конфликты разрешены с сохранением обработчиков проекта 166.
2026-10-13 19:19:57 +03:00

32 lines
4.5 KiB
Python

"""Проверяет тайм-аут штатного CAN на настоящих функциях проекта 166.""" # Модель не заменяет испытание аппаратного eCAN.
import os # Передаёт пути библиотек GCC только дочерним процессам.
from pathlib import Path # Находит исходники независимо от рабочего каталога запуска.
import re # Удаляет комментарии и выделяет тела проверяемых функций.
import subprocess # Запускает компилятор и проверяет поведенческий результат модели.
import tempfile # Изолирует сгенерированные файлы от образов прошивки пользователя.
import sys # Позволяет проверить отдельно сохранённую старую версию ecan.c.
root = Path(__file__).resolve().parents[2] # Корень проекта содержит рабочую реализацию eCAN.
source_path = Path(sys.argv[1]) if len(sys.argv) > 1 else root / "Source/Internal/ecan.c" # Дополнительный путь нужен для проверки чувствительности к старой ошибке.
source = re.sub(r"/\*.*?\*/|//[^\n]*", "", source_path.read_text(encoding="utf-8"), flags=re.S) # Комментарии со скобками не участвуют в выделении функций.
def function(name): # Извлекает текущий алгоритм вместо поддержания отдельной копии в тесте.
match = re.search(r"(?:interrupt\s+)?void\s+" + name + r"\([^;{}]*\)\s*\{", source) # Отличает определение от вызова и объявления.
assert match is not None, name # Исчезновение функции должно останавливать проверку.
end, depth = match.end(), 1 # Начинает поиск после открывающей скобки тела.
while depth: # Учитывает вложенные условия до закрытия внешнего блока.
depth += (source[end] == "{") - (source[end] == "}") # Считает только скобки кода без комментариев.
end += 1 # Включает найденную закрывающую скобку в результат.
return source[match.start():end] # Сохраняет все реальные обращения к регистрам и условия.
model = Path(__file__).with_name("model.inc").read_text(encoding="utf-8") # Загружает минимальное аппаратное окружение и сценарии потери ACK.
model = model.replace("/* PRODUCTION */", function("CAN_send") + "\n" + function("CANa_reset_err")) # Проверяет передачу и прерывание в одном жизненном цикле.
compiler = os.environ.get("CC", "C:/msys64/mingw64/bin/gcc.exe") # Использует установленный GCC, сохраняя возможность явно выбрать другой.
environment = dict(os.environ) # Не меняет окружение приложения и проекта CCS.
environment["PATH"] = str(Path(compiler).parent) + os.pathsep + environment.get("PATH", "") # Делает библиотеки MinGW доступными тестовой программе.
with tempfile.TemporaryDirectory(prefix="can-timeout-") as directory: # Удаляет только собственные временные файлы после завершения проверки.
c_path, exe_path = Path(directory) / "test.c", Path(directory) / "test.exe" # Разделяет исходник модели и исполняемый результат.
c_path.write_text(model, encoding="utf-8") # Сохраняет русские пояснения модели в требуемой кодировке.
subprocess.run([compiler, "-std=c99", "-O3", "-Wall", "-Wextra", "-Wno-unused-parameter", "-Werror", str(c_path), "-o", str(exe_path)], env=environment, check=True) # Проверяет реальные функции под оптимизацией и без скрытых предупреждений.
subprocess.run([str(exe_path)], env=environment, check=True, timeout=10) # Зависание и нарушенное условие делают сценарий неуспешным.