#include "../include/pcan_boot.h" /* Проверяет публичный BUSY codec и продление сессии на настоящем ядре. */ #include /* Несовпадение состояния или wire-полей завершает тест ненулевым результатом. */ #include /* Инициализирует опциональные поля конфигурации без аппаратных зависимостей. */ static unsigned int status, body; /* Сохраняет наблюдаемые поля последнего настоящего STATUS кадра. */ static bool send_frame(void *user, uint32_t id, const pcan_boot_byte_t *data, pcan_boot_byte_t length) /* Модель заменяет только физическую отправку CAN. */ { /* Содержимое статуса формирует production send_status, а не этот тест. */ (void)user; assert(length == 8U && pcan_boot_id_msg_type(id) == 12U); status = data[0]; body = pcan_boot_id_msg_body(id); return true; /* Проверяет тип, длину и сохранённую session/command. */ } /* Передача считается физически принятой аппаратным портом. */ static bool erase(void *user, pcan_boot_byte_t slot) { (void)user; return slot == 0U; } /* Минимальный single-slot callback удовлетворяет обязательному контракту init. */ static bool write_data(void *user, pcan_boot_byte_t slot, uint32_t offset, const pcan_boot_byte_t *data, pcan_boot_byte_t length) { (void)user; (void)offset; (void)data; return slot == 0U && length <= 8U; } /* Тест не программирует никакое реальное хранилище. */ int main(void) /* Проверяет, что операция длиннее исходного session timeout остаётся живой при BUSY. */ { /* Без этой проверки длительный NOR erase мог бы закончиться уже отменённой сессией. */ pcan_boot_t boot; pcan_boot_config_t config; pcan_boot_port_t port; /* Использует тот же экземпляр автомата, что аппаратные проекты. */ memset(&config, 0, sizeof(config)); memset(&port, 0, sizeof(port)); config.slot_count = 1U; config.active_slot = 0U; config.session_timeout_ms = 30000UL; /* Сессия истекает через 30 секунд реального бездействия. */ port.send = send_frame; port.erase_slot = erase; port.write_slot = write_data; assert(pcan_boot_init(&boot, &config, &port, 0)); /* Не обходит production валидацию обязательных callbacks. */ assert(pcan_boot_process(&boot, pcan_boot_make_id(1U, 0U, 0U, 0U, 9U, 0x0102U), 0, 0U)); /* Открывает session=1 штатной командой ENTER_BOOT. */ assert(pcan_boot_report_busy(&boot, 5U, 40000UL) && status == 1U && body == 0x0105U); /* BUSY долгого ERASE содержит исходную session и обновляет часы активности после сорока секунд. */ pcan_boot_poll(&boot, 69999UL); assert(boot.state == PCAN_BOOT_STATE_METADATA); /* Почти 30 секунд после последнего BUSY ещё не означают тайм-аут. */ pcan_boot_poll(&boot, 70000UL); assert(boot.state == PCAN_BOOT_STATE_IDLE); /* После полного интервала без прогресса сессия снова отменяется обычным способом. */ assert(!pcan_boot_report_busy(0, 5U, 0UL)); /* Неинициализированный порт не должен вызывать аппаратный send. */ return 0; /* Все проверки используют одно производственное ядро и его codec. */ } /* Криптография и аппаратные сроки конкретной NOR не моделируются этим unit-тестом. */