Перенесено из коммита 985804607d40b84f985eb04fc1f1b0a0dcfbe795 ветки 167. Конфликты разрешены с сохранением обработчиков проекта 166.
32 lines
4.5 KiB
Python
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) # Зависание и нарушенное условие делают сценарий неуспешным.
|