Перенести настройки таймингов из13e5bfe373и относящуюся к индикации часть04a9d989fb. Сохранить текущую последовательность DATA/CLOCK ветки 167, удерживать TESTLAMP при кратких провалах входа и исключить повторный вход Timer1. Адаптировать тесты TESTLAMP к типу накопителя и кодировкам проекта 166. Сохранить изменённые исходники и русские комментарии в UTF-8. Проверка: поведенческие и мутационные тесты TESTLAMP, полная сборка 27 модулей TI C2000 21.12.0.STS и линковка, образ SCI8 30174 байта, компиляция канала индикации с общей оптимизацией -O3.
52 lines
8.2 KiB
Python
52 lines
8.2 KiB
Python
"""Проверяет 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") # Сообщает об ожидаемом провале временного варианта со старой ошибкой.
|