Files
templates/c/firmware-info/tests/test_c2000_build.py
T
Andrey 45a34db657 Исправить каталог выпуска прошивки TMS320F2812
Размещать основной SCI8-комплект F2812 в flash, включая BIN, OUT, HEX,
карты, manifest и SHA-256. Передавать выбранный путь в указатель
последней сборки и экспорт; сохранить раскладку остальных процессоров.

Добавить проверку раскладки, паспортов и экспорта и описать правило.
Проверка: пять тестов общей сборки и реальный postbuild текущего OUT;
побайтовая сверка BIN после переноса.
2026-11-06 16:20:37 +03:00

75 lines
12 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.
import importlib.util # Загружает общий модуль подготовки непосредственно из проверяемой копии templates.
import json # Проверяет сохранение пользовательских полей при резервировании версии 166.
from pathlib import Path # Все файлы теста находятся в изолированном временном каталоге.
import tempfile # Исключает запись версий и паспортов в рабочие прошивки пользователя.
import unittest # Запускает проверки без зависимостей SETGUI и Qt.
TOOLS = Path(__file__).resolve().parents[1] / "tools" # Тест переносится вместе с c/firmware-info в полный checkout templates.
SPEC = importlib.util.spec_from_file_location("checked_c2000_build", TOOLS / "c2000_build.py") # Выбирает одну реализацию общего процесса.
BUILD = importlib.util.module_from_spec(SPEC) # Импорт не выполняет prebuild и не меняет счётчики версии.
SPEC.loader.exec_module(BUILD) # Проверяет реальный модуль, а не копию функций внутри теста.
class LegacyVersionTests(unittest.TestCase): # Подтверждает совместимость общей подготовки с JSON-конфигурацией 166.
def test_json_counter_increments_once_and_keeps_settings(self): # Перенос в templates не должен удваивать резервирование версии.
with tempfile.TemporaryDirectory() as temporary: # Версия настоящего проекта 166 остаётся неизменной.
project = Path(temporary) # Минимальный проект использует прежние настройки без нового аппаратного профиля.
settings = {"product": "Balsam-166-PM35", "target": "TMS320F28335", "version": [1, 0, 999], "increment_version": True, "max_image_bytes": 32508, "copy_to": "../Bin"} # Patch 999 проверяет перенос в minor по общему контракту версии.
config = project / "firmware_identity.json" # Сохраняемый счётчик расположен в том же JSON, что в ветке 166.
config.write_text(json.dumps(settings), encoding="utf-8") # Русские данные и настройки сохраняются как UTF-8.
templates = TOOLS.parents[2] # Полный путь templates восстанавливается из c/firmware-info/tools.
metadata = BUILD.prepare(project, templates, project / "Debug/generated") # Выполняет настоящий общий prebuild без компиляции и сети.
saved = json.loads(config.read_text(encoding="utf-8")) # Проверяет именно сохранённую версию, а не только возвращённые метаданные.
self.assertEqual(saved["version"], [1, 1, 0]) # Номер увеличен один раз с переносом patch в minor.
self.assertEqual(metadata["version"], saved["version"]) # Паспорт и сохраняемый счётчик описывают одну попытку компиляции.
self.assertEqual(metadata["max_image_bytes"], 32508) # Историческая граница EEPROM 166 остаётся обязательной для postbuild.
self.assertEqual(saved["copy_to"], settings["copy_to"]) # Общий генератор сохраняет пользовательские поля, даже если сеть выполняет Публикатор.
class ReleaseLayoutTests(unittest.TestCase): # Проверяет назначение комплекта F2812 и совместимость экспорта для остальных процессоров.
def test_primary_package_follows_target_and_exports_with_receipt(self): # Проверяет пути, байты и паспорта полного комплекта после раскладки и экспорта.
identity = BUILD.load_identity(TOOLS.parents[2]) # Использует действующие функции подписи и копирования общей библиотеки.
for target, purpose in ((None, "eeprom"), ("TMS320F2812", "flash"), ("TMS320F28335", "eeprom")): # Старые вызовы и F28335 сохраняют EEPROM; F2812 использует внешнюю Flash.
with self.subTest(target=target), tempfile.TemporaryDirectory() as temporary: # Каждый вариант получает независимый выпуск и отдельную диагностику.
root = Path(temporary) # Файлы теста не попадают в действующий проект или каталог загрузок.
stage = root / "stage" # Воспроизводит временный каталог защищённого postbuild.
archive_name = "current-build" # Имя архива здесь фиксировано: проверяется раскладка, а не генерация даты.
archive = stage / archive_name # Полная копия компиляции должна получить те же папки, что текущие результаты.
archive.mkdir(parents=True) # Создаёт только каталоги данного проверочного выпуска.
metadata = {"target": target, "version": [1, 2, 3]} # Проверка паспорта должна сохранить процессор и версию конкретного сценария.
metadata_path = archive / "firmware_identity.json" # Общий паспорт остаётся в корне архива после раскладки.
metadata_path.write_text(json.dumps(metadata), encoding="utf-8") # Записывает метаданные в штатной кодировке общей библиотеки.
artifact, image_name = "Firmware", "Firmware_NOGIT000.bin" # Различает исходный OUT и BIN с идентификатором сборки.
companions = {"Firmware.out": b"original OUT", "Firmware.hex": b"original HEX", image_name + ".map": b"original map"} # Сопроводительные файлы обязаны следовать за основным образом без изменения байтов.
variants = {"Firmware_flash.out": "flash", "Firmware_flash_rs.bin": "rs", "Firmware_NOGIT000_can.bin": "can"} # Именованные дополнительные варианты сохраняют прежние независимые назначения.
for folder in (stage, archive): # Готовит одинаковые результаты для текущего выпуска и полного архива.
image = folder / image_name # Каждый экземпляр BIN получает собственные настоящие manifest и SHA-256.
image.write_bytes(b"primary SCI8 payload") # Узнаваемые байты выявляют подмену образа при переносе или экспорте.
identity.seal(image, metadata_path) # Вычисляет контрольные суммы штатной реализацией, а не подставляет фиктивный manifest.
for name, content in companions.items(): # Подготавливает OUT, HEX и карту того же основного комплекта.
(folder / name).write_bytes(content) # Сохраняет эталон для проверки точности переноса.
for name in variants: # Дополнительные образы нужны для проверки сохранения маршрутов flash, rs и can.
(folder / name).write_bytes(name.encode("ascii")) # Отличимые имена в содержимом исключают случайную перестановку файлов.
relative = BUILD.arrange_release(stage, archive_name, artifact, image_name, Path("Firmware_NOGIT000_can.bin"), target=target) # Вызывает ту же раскладку, что действующий postbuild.
self.assertEqual(relative, purpose + "/" + image_name) # Возвращаемый путь должен вести к основному образу в назначении выбранного процессора.
BUILD.write_receipt(stage, archive_name, relative) # Проверяет связь раскладки с указателем последнего выпуска.
receipt = json.loads((stage / "latest_build.json").read_text(encoding="utf-8")) # Читает записанный указатель как его потребитель.
self.assertEqual(receipt["manifest"], relative + ".manifest.json") # Путь импорта должен учитывать flash у F2812 и eeprom у остальных.
exported = identity.export_build(archive, root / "export", relative) # Выполняет настоящий экспорт всего архива в отдельный каталог.
for folder in (stage, archive, exported): # Одновременно проверяет локальный результат, архив и копию для пользователя.
BUILD.verify_package(folder / relative, metadata) # Сверяет реальные байты BIN, размер, SHA-256 и поля паспорта после всех переносов.
self.assertEqual((folder / relative).read_bytes(), b"primary SCI8 payload") # Основной образ сохраняется побайтово.
for suffix in (".manifest.json", ".sha256"): # Оба сопроводительных файла должны лежать рядом с BIN.
self.assertTrue((folder / (relative + suffix)).is_file()) # Обнаруживает потерю manifest или контрольной суммы при раскладке.
for name, content in companions.items(): # OUT, HEX и карта должны повторять назначение основного BIN.
self.assertEqual((folder / purpose / name).read_bytes(), content) # Проверяет путь и точное содержимое каждого файла.
for name, destination in variants.items(): # Проверяет неизменность маршрутов дополнительных вариантов.
self.assertEqual((folder / destination / name).read_bytes(), name.encode("ascii")) # Отдельные flash, rs и can не должны уйти в папку основного образа.
self.assertTrue((folder / "firmware_identity.json").is_file()) # Общий паспорт остаётся доступен в корне каждого комплекта.
if target == "TMS320F2812": # Только для F2812 проверяет отсутствие вводящей в заблуждение папки EEPROM.
self.assertFalse((folder / "eeprom").exists()) # Новый выпуск F2812 должен содержать основной комплект исключительно в flash.
if __name__ == "__main__": # Импорт набора проверок не запускает их повторно.
unittest.main() # Позволяет проверить совместимость прямой командой Python.