Files
Balsam_167/tests/testlamp/run_test.py
Andrey b25e026dec Перенести исправления строба индикации из 167
Перенести настройки таймингов из 13e5bfe373
и относящуюся к индикации часть 04a9d989fb.
Сохранить текущую последовательность DATA/CLOCK ветки 167, удерживать
TESTLAMP при кратких провалах входа и исключить повторный вход Timer1.

Адаптировать тесты TESTLAMP к типу накопителя и кодировкам проекта 166.
Сохранить изменённые исходники и русские комментарии в UTF-8.

Проверка: поведенческие и мутационные тесты TESTLAMP, полная сборка
27 модулей TI C2000 21.12.0.STS и линковка, образ SCI8 30174 байта,
компиляция канала индикации с общей оптимизацией -O3.
2026-10-13 19:18:10 +03:00

52 lines
8.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Проверяет TESTLAMP на настоящих функциях прошивки без подключения платы."""
import argparse # Позволяет использовать доступный GCC для модели аппаратного окружения.
import os # Передаёт компилятору PATH с его вспомогательными программами и библиотеками.
from pathlib import Path # Разрешает исходники и модель относительно каталога этого теста.
import re # Извлекает функции, убирая комментарии, которые могут содержать примеры скобок.
import subprocess # Компилирует и запускает независимые проверки с контролем результата.
import tempfile # Удаляет только временные исходники и исполняемые файлы данного запуска.
ROOT = Path(__file__).resolve().parents[2] # Получает корень репозитория из расположения tests/testlamp.
def source(name): # Читает production-модуль без изменения исходного файла.
data = (ROOT / "Source/Internal" / name).read_bytes() # Читает исходник без изменения его кодировки: в 166 ещё есть модули Windows-1251.
try: # Сначала использует UTF-8 для перенесённых модулей индикации.
text = data.decode("utf-8") # Строго декодирует обновлённые исходники без потери русских комментариев.
except UnicodeDecodeError: # Поддерживает старый measure.c, не изменяя его ради хостового теста.
text = data.decode("cp1251") # Читает исходную Windows-1251 только при невозможности декодирования UTF-8.
return re.sub(r"/\*.*?\*/|//[^\n]*", "", text, flags=re.S) # Убирает комментарии перед разбором вложенных блоков.
def function(text, name): # Выбирает определение функции вместо копии её алгоритма в тесте.
match = re.search(r"(?:interrupt\s+)?(?:void|int)\s+" + name + r"\([^;{}]*\)\s*\{", text) # Отличает тело функции от прототипов и вызовов.
assert match is not None, name # Завершает проверку, если проверяемая production-функция исчезла.
end, depth = match.end(), 1 # Учитывает найденную открывающую скобку внешнего блока.
while depth: # Читает все вложенные ветви до конца определения.
depth += (text[end] == "{") - (text[end] == "}") # Находит парную закрывающую скобку.
end += 1 # Включает текущий символ в извлекаемый фрагмент.
return text[match.start():end] # Сохраняет порядок операторов настоящей функции.
parser = argparse.ArgumentParser(description=__doc__) # Объясняет назначение отдельного запуска проверки.
parser.add_argument("--cc", default="C:/msys64/mingw64/bin/gcc.exe", help="Путь к GCC") # Повторяет установленный в этом проекте хостовый компилятор.
args = parser.parse_args() # Разбирает параметры до формирования временных файлов.
button = function(source("peripher.c"), "get_Buttons") # Проверяет именно публикацию отфильтрованной кнопки в Modbus.
pult = source("pulto.c") # Берёт текущую обработку индикации, включая реальные ветви теста и яркости.
body = "\n".join((function(source("measure.c"), "er_anal"), button, function(pult, "what_is"), function(pult, "cpu_timer1_isr_PULT"))) # Собирает функции, участвующие в переключении режима и передаче масок.
before, after = Path(__file__).with_name("model.inc").read_text(encoding="utf-8").split("/* HANDLERS */") # Помещает production-функции между аппаратными заглушками и поведенческими сценариями.
old_button, count_button = re.subn(r"(er_anal\(GpioDataRegs\.GPADAT\.bit\.GPIO26,\s*&INcount0,\s*1000,\s*)bTestLamp(\))", r"\g<1>0\2", body) # Возвращает сброс фильтра при одном провале только во временную мутацию.
old_mask, count_mask = re.subn(r"IER\s*&=\s*\(MINT13\s*&\s*~M_INT13\);", "IER |= MINT13;", body) # Восстанавливает ошибочное разрешение прерываний в отдельном временном варианте.
assert count_button == count_mask == 1, "Не найдены единственные места исправлений TESTLAMP" # Не позволяет мутации незаметно проверять неизменённый код.
environment = dict(os.environ) # Сохраняет окружение основного процесса.
environment["PATH"] = str(Path(args.cc).resolve().parent) + os.pathsep + environment.get("PATH", "") # Делает DLL и вспомогательные программы GCC доступными дочерним процессам.
with tempfile.TemporaryDirectory(prefix="testlamp-") as directory: # Изолирует артефакты каждой проверки от дерева прошивки.
for name, functions, failure in (("production", body, None), ("old_button", old_button, "FAIL held test"), ("old_mask", old_mask, "FAIL interrupt mask")): # Проверяет исправление и чувствительность сценариев к каждой из двух старых ошибок.
c_path, exe_path = Path(directory) / (name + ".c"), Path(directory) / (name + ".exe") # Разделяет временные исходники и программы трёх вариантов.
c_path.write_text(before + functions + after, encoding="utf-8") # Сохраняет модель и русские пояснения в UTF-8.
subprocess.run([args.cc, "-std=c99", "-O3", "-fno-strict-aliasing", "-Werror=implicit-function-declaration", "-I", str(ROOT / "Source/Internal/Include"), "-I", str(ROOT / "Source/External/v120/DSP2833x_common/include"), str(c_path), "-o", str(exe_path)], env=environment, check=True) # Сохраняет адреса production-псевдонимов регистров и использует реальную таблицу приоритетов TI.
result = subprocess.run([str(exe_path)], env=environment, capture_output=True, text=True, timeout=20) # Ограничивает зависание и сохраняет конкретные нарушенные условия.
if failure is None: # Рабочая версия обязана пройти все сценарии.
print(result.stdout, end="") # Показывает результат поведенческих проверок текущей прошивки.
assert result.returncode == 0, result.stdout + result.stderr # Возвращает ошибку при любой регрессии TESTLAMP или рабочего режима.
else: # Старая ошибка должна воспроизводимо нарушить соответствующее требование.
assert result.returncode == 1 and failure in result.stdout, result.stdout + result.stderr # Доказывает, что тест действительно обнаруживает возвращённый дефект.
print("PASS mutation:", name, "rejected") # Сообщает об ожидаемом провале временного варианта со старой ошибкой.