From 54c20878ecf12ccc8d3ce5fcb0faa21980c959af Mon Sep 17 00:00:00 2001 From: CinderSocket Date: Sun, 8 Mar 2026 18:12:48 -0700 Subject: [PATCH] Add basic test harness using munit --- .github/workflows/main.yml | 19 ++- .gitignore | 5 +- .gitmodules | 3 + Makefile | 17 +- application.fam | 1 + t_1.c | 4 + tests/host/bit_buffer.h | 15 ++ tests/host/bit_buffer_mock.c | 48 ++++++ tests/host/t1_test_stubs.c | 31 ++++ tests/host/t_1_host_env.h | 103 +++++++++++ tests/host/test_lrc.c | 52 ++++++ tests/host/test_main.c | 20 +++ tests/host/test_t1_existing.c | 312 ++++++++++++++++++++++++++++++++++ tests/host/vendor/munit | 1 + 14 files changed, 620 insertions(+), 11 deletions(-) create mode 100644 tests/host/bit_buffer.h create mode 100644 tests/host/bit_buffer_mock.c create mode 100644 tests/host/t1_test_stubs.c create mode 100644 tests/host/t_1_host_env.h create mode 100644 tests/host/test_lrc.c create mode 100644 tests/host/test_main.c create mode 100644 tests/host/test_t1_existing.c create mode 160000 tests/host/vendor/munit diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 23b7a2c..12ba0b2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: "FAP: Build and lint" +name: "FAP: Build, test, and lint" on: [push, pull_request] jobs: ufbt-build-action: @@ -9,20 +9,23 @@ jobs: uses: actions/checkout@v3 with: submodules: 'true' + - name: Setup ufbt + uses: flipperdevices/flipperzero-ufbt-action@v0.1.2 + with: + sdk-channel: release + task: setup + - name: Run host tests + run: make test-host + - name: Lint sources + run: ufbt lint ARGS="!./tests/host/vendor/munit" - name: Build with ufbt uses: flipperdevices/flipperzero-ufbt-action@v0.1.2 id: build-app with: + skip-setup: true sdk-channel: release - name: Upload app artifacts uses: actions/upload-artifact@v4 with: name: ${{ github.event.repository.name }}-${{ steps.build-app.outputs.suffix }} path: ${{ steps.build-app.outputs.fap-artifacts }} - # You can remove this step if you don't want to check source code formatting - - name: Lint sources - uses: flipperdevices/flipperzero-ufbt-action@v0.1.2 - with: - # skip SDK setup, we already did it in previous step - skip-setup: true - task: lint diff --git a/.gitignore b/.gitignore index 51ee080..0cc5d6f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .vscode/ dist/ -examples/* -logs +examples/ +logs/ credential/ +build/ diff --git a/.gitmodules b/.gitmodules index e691db8..8599403 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "plugin"] path = plugin url = https://gitlab.com/bettse/flipper-wiegand-plugin.git +[submodule "tests/host/vendor/munit"] + path = tests/host/vendor/munit + url = https://github.com/nemequ/munit.git diff --git a/Makefile b/Makefile index c210d22..945312a 100644 --- a/Makefile +++ b/Makefile @@ -9,11 +9,26 @@ asn1: build: ufbt +test-host: + mkdir -p build/host_tests + cc -std=c11 -Wall -Wextra -Werror -DSEADER_HOST_TEST -Itests/host/vendor/munit -Itests/host -I. \ + tests/host/vendor/munit/munit.c \ + tests/host/test_main.c \ + tests/host/test_lrc.c \ + tests/host/test_t1_existing.c \ + tests/host/t1_test_stubs.c \ + tests/host/bit_buffer_mock.c \ + lrc.c \ + t_1.c \ + -o build/host_tests/seader_tests + ./build/host_tests/seader_tests + launch: ufbt launch format: - ufbt format + ufbt format ARGS="!./tests/host/vendor/munit" clean: rm -rf dist + rm -rf build/host_tests diff --git a/application.fam b/application.fam index 444f63b..9830012 100644 --- a/application.fam +++ b/application.fam @@ -21,6 +21,7 @@ App( "*.c", "aeabi_uldivmod.sx", "!plugin/*.c", + "!tests", ], fap_icon="icons/logo.png", fap_category="NFC", diff --git a/t_1.c b/t_1.c index 0eda220..d558795 100644 --- a/t_1.c +++ b/t_1.c @@ -1,4 +1,8 @@ +#ifdef SEADER_HOST_TEST +#include "tests/host/t_1_host_env.h" +#else #include "t_1.h" +#endif #define TAG "Seader:T=1" diff --git a/tests/host/bit_buffer.h b/tests/host/bit_buffer.h new file mode 100644 index 0000000..8af0ff0 --- /dev/null +++ b/tests/host/bit_buffer.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include +#include + +typedef struct BitBuffer BitBuffer; + +BitBuffer* bit_buffer_alloc(size_t capacity_bytes); +void bit_buffer_free(BitBuffer* buf); +void bit_buffer_reset(BitBuffer* buf); +void bit_buffer_copy_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes); +size_t bit_buffer_get_size_bytes(const BitBuffer* buf); +const uint8_t* bit_buffer_get_data(const BitBuffer* buf); +void bit_buffer_append_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes); diff --git a/tests/host/bit_buffer_mock.c b/tests/host/bit_buffer_mock.c new file mode 100644 index 0000000..fd34458 --- /dev/null +++ b/tests/host/bit_buffer_mock.c @@ -0,0 +1,48 @@ +#include "bit_buffer.h" + +#include +#include + +struct BitBuffer { + uint8_t* data; + size_t size_bits; + size_t capacity_bytes; +}; + +BitBuffer* bit_buffer_alloc(size_t capacity_bytes) { + BitBuffer* buf = malloc(sizeof(BitBuffer)); + buf->data = calloc(1, capacity_bytes); + buf->size_bits = 0; + buf->capacity_bytes = capacity_bytes; + return buf; +} + +void bit_buffer_free(BitBuffer* buf) { + if(buf) { + free(buf->data); + free(buf); + } +} + +void bit_buffer_reset(BitBuffer* buf) { + buf->size_bits = 0; +} + +void bit_buffer_copy_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes) { + memcpy(buf->data, data, size_bytes); + buf->size_bits = size_bytes * 8; +} + +size_t bit_buffer_get_size_bytes(const BitBuffer* buf) { + return (buf->size_bits + 7) / 8; +} + +const uint8_t* bit_buffer_get_data(const BitBuffer* buf) { + return buf->data; +} + +void bit_buffer_append_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes) { + size_t current_bytes = bit_buffer_get_size_bytes(buf); + memcpy(buf->data + current_bytes, data, size_bytes); + buf->size_bits += size_bytes * 8; +} diff --git a/tests/host/t1_test_stubs.c b/tests/host/t1_test_stubs.c new file mode 100644 index 0000000..da69ec0 --- /dev/null +++ b/tests/host/t1_test_stubs.c @@ -0,0 +1,31 @@ +#include "t_1_host_env.h" + +T1HostTestState g_t1_host_test_state = {0}; + +void t1_host_test_reset(void) { + /* Default to success so tests opt into failure only when needed. */ + memset(&g_t1_host_test_state, 0, sizeof(g_t1_host_test_state)); + g_t1_host_test_state.process_return_value = true; +} + +void seader_ccid_XfrBlock(SeaderUartBridge* seader_uart, uint8_t* data, size_t len) { + (void)seader_uart; + /* Record the exact frame the production code asked CCID to transmit. */ + g_t1_host_test_state.xfrblock_call_count++; + g_t1_host_test_state.last_frame_len = len; + memcpy(g_t1_host_test_state.last_frame, data, len); +} + +bool seader_worker_process_sam_message(Seader* seader, uint8_t* apdu, uint32_t len) { + (void)seader; + /* Capture the final APDU bytes that would be handed to the worker layer. */ + g_t1_host_test_state.process_call_count++; + g_t1_host_test_state.last_apdu_len = len; + memcpy(g_t1_host_test_state.last_apdu, apdu, len); + return g_t1_host_test_state.process_return_value; +} + +void seader_worker_send_version(Seader* seader) { + (void)seader; + g_t1_host_test_state.send_version_call_count++; +} diff --git a/tests/host/t_1_host_env.h b/tests/host/t_1_host_env.h new file mode 100644 index 0000000..2bc0333 --- /dev/null +++ b/tests/host/t_1_host_env.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "bit_buffer.h" +#include "lrc.h" + +/* Keep the host harness aligned with the production UART scratchpad size. */ +#define SEADER_UART_RX_BUF_SIZE (300) +#define FURI_LOG_W(tag, fmt, ...) ((void)0) + +typedef struct BitBuffer BitBuffer; +typedef struct Seader Seader; +typedef struct SeaderWorker SeaderWorker; +typedef struct SeaderUartBridge SeaderUartBridge; + +typedef enum { + SeaderWorkerEventSamPresent = 53, +} SeaderWorkerEvent; + +typedef void (*SeaderWorkerCallback)(uint32_t event, void* context); + +typedef enum { + SEADER_T1_PCB_I_BLOCK_MORE = 0x20, + SEADER_T1_PCB_SEQUENCE_BIT = 0x40, + SEADER_T1_PCB_R_BLOCK = 0x80, + SEADER_T1_PCB_S_BLOCK = 0xC0, + SEADER_T1_R_BLOCK_SEQUENCE_MASK = 0x10, + SEADER_T1_S_BLOCK_IFS = 0x01, +} SeaderT1Constant; + +typedef struct { + uint8_t ifsc; + uint8_t nad; + uint8_t send_pcb; + uint8_t recv_pcb; + BitBuffer* tx_buffer; + size_t tx_buffer_offset; + BitBuffer* rx_buffer; +} SeaderT1State; + +struct SeaderUartBridge { + uint8_t rx_buf[SEADER_UART_RX_BUF_SIZE]; + uint8_t tx_buf[SEADER_UART_RX_BUF_SIZE]; + size_t tx_len; + uint8_t T; + SeaderT1State t1; +}; + +struct SeaderWorker { + SeaderUartBridge* uart; + SeaderWorkerCallback callback; + void* context; +}; + +struct Seader { + SeaderWorker* worker; +}; + +typedef struct CCID_Message { + uint8_t bMessageType; + uint32_t dwLength; + uint8_t bSlot; + uint8_t bSeq; + uint8_t bStatus; + uint8_t bError; + uint8_t* payload; + size_t consumed; +} CCID_Message; + +void seader_ccid_XfrBlock(SeaderUartBridge* seader_uart, uint8_t* data, size_t len); +bool seader_worker_process_sam_message(Seader* seader, uint8_t* apdu, uint32_t len); +void seader_worker_send_version(Seader* seader); + +typedef struct { + /* Captured outbound CCID payload emitted by the T=1 implementation. */ + size_t xfrblock_call_count; + uint8_t last_frame[SEADER_UART_RX_BUF_SIZE]; + size_t last_frame_len; + /* Captured inbound APDU delivered upward to the SAM worker boundary. */ + size_t process_call_count; + uint8_t last_apdu[SEADER_UART_RX_BUF_SIZE]; + size_t last_apdu_len; + bool process_return_value; + /* Side effects used by the IFS response path. */ + size_t send_version_call_count; + size_t callback_call_count; + uint32_t last_callback_event; +} T1HostTestState; + +extern T1HostTestState g_t1_host_test_state; + +void t1_host_test_reset(void); + +void seader_send_t1(SeaderUartBridge* seader_uart, uint8_t* apdu, size_t len); +bool seader_recv_t1(Seader* seader, CCID_Message* message); +void seader_t_1_set_IFSD(Seader* seader); +void seader_t_1_reset(SeaderUartBridge* seader_uart); diff --git a/tests/host/test_lrc.c b/tests/host/test_lrc.c new file mode 100644 index 0000000..23c3460 --- /dev/null +++ b/tests/host/test_lrc.c @@ -0,0 +1,52 @@ +#include + +#include "lrc.h" +#include "munit.h" + +static MunitResult test_calc_lrc(const MunitParameter params[], void* fixture) { + (void)params; + (void)fixture; + + uint8_t bytes[] = {0x03, 0x06, 0x62, 0x00}; + munit_assert_uint8( + seader_calc_lrc(bytes, sizeof(bytes)), ==, (uint8_t)(0x03 ^ 0x06 ^ 0x62 ^ 0x00)); + return MUNIT_OK; +} + +static MunitResult test_add_and_validate_lrc(const MunitParameter params[], void* fixture) { + (void)params; + (void)fixture; + + uint8_t frame[8] = {0x03, 0x06, 0x62, 0x00, 0x01, 0x02, 0x00, 0x00}; + size_t len = seader_add_lrc(frame, 6); + munit_assert_size(len, ==, 7); + munit_assert_true(seader_validate_lrc(frame, len)); + + frame[2] ^= 0x80; + munit_assert_false(seader_validate_lrc(frame, len)); + return MUNIT_OK; +} + +static MunitResult test_validate_nak_triplet(const MunitParameter params[], void* fixture) { + (void)params; + (void)fixture; + + uint8_t nak_triplet[] = {0x03, 0x15, 0x16}; + munit_assert_true(seader_validate_lrc(nak_triplet, 3)); + return MUNIT_OK; +} + +static MunitTest test_lrc_cases[] = { + {(char*)"/calc", test_calc_lrc, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {(char*)"/roundtrip", test_add_and_validate_lrc, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {(char*)"/nak-triplet", test_validate_nak_triplet, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {NULL, NULL, NULL, NULL, 0, NULL}, +}; + +MunitSuite test_lrc_suite = { + "", + test_lrc_cases, + NULL, + 1, + MUNIT_SUITE_OPTION_NONE, +}; diff --git a/tests/host/test_main.c b/tests/host/test_main.c new file mode 100644 index 0000000..504c5a8 --- /dev/null +++ b/tests/host/test_main.c @@ -0,0 +1,20 @@ +#include "munit.h" + +extern MunitSuite test_lrc_suite; +extern MunitSuite test_t1_existing_suite; + +int main(int argc, char* argv[]) { + MunitSuite child_suites[] = { + {"/lrc", test_lrc_suite.tests, NULL, 1, MUNIT_SUITE_OPTION_NONE}, + {"/t1", test_t1_existing_suite.tests, NULL, 1, MUNIT_SUITE_OPTION_NONE}, + {NULL, NULL, NULL, 0, 0}, + }; + MunitSuite main_suite = { + "", + NULL, + child_suites, + 1, + MUNIT_SUITE_OPTION_NONE, + }; + return munit_suite_main(&main_suite, NULL, argc, argv); +} diff --git a/tests/host/test_t1_existing.c b/tests/host/test_t1_existing.c new file mode 100644 index 0000000..8f0da8f --- /dev/null +++ b/tests/host/test_t1_existing.c @@ -0,0 +1,312 @@ +#include + +#include "munit.h" +#include "t_1_host_env.h" + +/* The production worker reports SAM-present through a callback; the harness just records it. */ +static void test_callback(uint32_t event, void* context) { + (void)context; + g_t1_host_test_state.callback_call_count++; + g_t1_host_test_state.last_callback_event = event; +} + +static Seader make_test_seader(SeaderUartBridge* uart, SeaderWorker* worker) { + memset(uart, 0, sizeof(*uart)); + memset(worker, 0, sizeof(*worker)); + worker->uart = uart; + worker->callback = test_callback; + + Seader seader = {.worker = worker}; + return seader; +} + +static bool last_frame_prefix_matches(const uint8_t* expected, size_t len) { + return g_t1_host_test_state.last_frame_len >= len && + memcmp(g_t1_host_test_state.last_frame, expected, len) == 0; +} + +static bool last_apdu_matches(const uint8_t* expected, size_t len) { + return g_t1_host_test_state.last_apdu_len == len && + memcmp(g_t1_host_test_state.last_apdu, expected, len) == 0; +} + +static MunitResult test_send_ifs_request(const MunitParameter params[], void* fixture) { + (void)params; + (void)fixture; + + SeaderUartBridge uart = {0}; + SeaderWorker worker = {0}; + Seader seader = make_test_seader(&uart, &worker); + + t1_host_test_reset(); + uart.t1.ifsc = 0x20; + uart.t1.nad = 0x00; + uart.t1.send_pcb = SEADER_T1_PCB_SEQUENCE_BIT; + + seader_t_1_set_IFSD(&seader); + munit_assert_size(g_t1_host_test_state.xfrblock_call_count, ==, 1); + munit_assert_size(g_t1_host_test_state.last_frame_len, ==, 5); + munit_assert_true(last_frame_prefix_matches((const uint8_t*)"\x00\xC1\x01\x20", 4)); + munit_assert_true( + seader_validate_lrc(g_t1_host_test_state.last_frame, g_t1_host_test_state.last_frame_len)); + return MUNIT_OK; +} + +static MunitResult test_send_single_block(const MunitParameter params[], void* fixture) { + (void)params; + (void)fixture; + + SeaderUartBridge uart = {0}; + SeaderWorker worker = {0}; + make_test_seader(&uart, &worker); + + t1_host_test_reset(); + uart.t1.ifsc = 0x20; + uart.t1.send_pcb = SEADER_T1_PCB_SEQUENCE_BIT; + uint8_t apdu_short[] = {0xDE, 0xAD, 0xBE}; + + seader_send_t1(&uart, apdu_short, sizeof(apdu_short)); + munit_assert_size(g_t1_host_test_state.xfrblock_call_count, ==, 1); + munit_assert_size(g_t1_host_test_state.last_frame_len, ==, 7); + munit_assert_true(last_frame_prefix_matches((const uint8_t*)"\x00\x00\x03", 3)); + munit_assert_memory_equal(sizeof(apdu_short), g_t1_host_test_state.last_frame + 3, apdu_short); + munit_assert_uint8(uart.t1.send_pcb, ==, 0x00); + return MUNIT_OK; +} + +static MunitResult test_send_scratchpad_block(const MunitParameter params[], void* fixture) { + (void)params; + (void)fixture; + + SeaderUartBridge uart = {0}; + SeaderWorker worker = {0}; + make_test_seader(&uart, &worker); + + t1_host_test_reset(); + uart.t1.ifsc = 0x20; + uart.t1.send_pcb = SEADER_T1_PCB_SEQUENCE_BIT; + uint8_t* scratchpad_apdu = uart.tx_buf + 3; + scratchpad_apdu[0] = 0x01; + scratchpad_apdu[1] = 0x02; + + seader_send_t1(&uart, scratchpad_apdu, 2); + munit_assert_size(g_t1_host_test_state.xfrblock_call_count, ==, 1); + munit_assert_true(last_frame_prefix_matches((const uint8_t*)"\x00\x00\x02\x01\x02", 5)); + return MUNIT_OK; +} + +static MunitResult test_send_chained_block(const MunitParameter params[], void* fixture) { + (void)params; + (void)fixture; + + SeaderUartBridge uart = {0}; + SeaderWorker worker = {0}; + make_test_seader(&uart, &worker); + + t1_host_test_reset(); + uart.t1.send_pcb = SEADER_T1_PCB_SEQUENCE_BIT; + uart.t1.ifsc = 2; + uint8_t apdu_long[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE}; + + seader_send_t1(&uart, apdu_long, sizeof(apdu_long)); + munit_assert_size(g_t1_host_test_state.xfrblock_call_count, ==, 1); + munit_assert_uint8( + g_t1_host_test_state.last_frame[1], ==, (SEADER_T1_PCB_I_BLOCK_MORE | 0x00)); + munit_assert_uint8(g_t1_host_test_state.last_frame[2], ==, 2); + munit_assert_not_null(uart.t1.tx_buffer); + munit_assert_size(uart.t1.tx_buffer_offset, ==, 2); + + bit_buffer_free(uart.t1.tx_buffer); + return MUNIT_OK; +} + +static MunitResult test_recv_ifs_response(const MunitParameter params[], void* fixture) { + (void)params; + (void)fixture; + + SeaderUartBridge uart = {0}; + SeaderWorker worker = {0}; + Seader seader = make_test_seader(&uart, &worker); + + t1_host_test_reset(); + uint8_t ifs_response[] = {0x00, 0xE1, 0x01, 0x20, 0x00}; + seader_add_lrc(ifs_response, 4); + CCID_Message ifs_message = {.payload = ifs_response, .dwLength = 5}; + + munit_assert_false(seader_recv_t1(&seader, &ifs_message)); + munit_assert_size(g_t1_host_test_state.send_version_call_count, ==, 1); + munit_assert_size(g_t1_host_test_state.callback_call_count, ==, 1); + munit_assert_uint32(g_t1_host_test_state.last_callback_event, ==, SeaderWorkerEventSamPresent); + return MUNIT_OK; +} + +static MunitResult test_recv_single_i_block(const MunitParameter params[], void* fixture) { + (void)params; + (void)fixture; + + SeaderUartBridge uart = {0}; + SeaderWorker worker = {0}; + Seader seader = make_test_seader(&uart, &worker); + + t1_host_test_reset(); + uart.t1.recv_pcb = 0x00; + uint8_t i_block[] = {0x00, 0x00, 0x03, 0x11, 0x22, 0x33, 0x00}; + seader_add_lrc(i_block, 6); + CCID_Message i_message = {.payload = i_block, .dwLength = 7}; + + munit_assert_true(seader_recv_t1(&seader, &i_message)); + munit_assert_size(g_t1_host_test_state.process_call_count, ==, 1); + munit_assert_true(last_apdu_matches((const uint8_t*)"\x11\x22\x33", 3)); + munit_assert_uint8(uart.t1.recv_pcb, ==, 0x40); + return MUNIT_OK; +} + +static MunitResult test_recv_chained_i_blocks(const MunitParameter params[], void* fixture) { + (void)params; + (void)fixture; + + SeaderUartBridge uart = {0}; + SeaderWorker worker = {0}; + Seader seader = make_test_seader(&uart, &worker); + + t1_host_test_reset(); + uart.t1.recv_pcb = 0x00; + uint8_t i_more[] = {0x00, 0x20, 0x03, 0x44, 0x55, 0x66, 0x00}; + seader_add_lrc(i_more, 6); + CCID_Message more_message = {.payload = i_more, .dwLength = 7}; + + munit_assert_false(seader_recv_t1(&seader, &more_message)); + munit_assert_size(g_t1_host_test_state.xfrblock_call_count, ==, 1); + munit_assert_not_null(uart.t1.rx_buffer); + munit_assert_uint8(uart.t1.recv_pcb, ==, 0x40); + + t1_host_test_reset(); + uint8_t i_final[] = {0x00, 0x40, 0x02, 0x77, 0x88, 0x00}; + seader_add_lrc(i_final, 5); + CCID_Message final_message = {.payload = i_final, .dwLength = 6}; + + munit_assert_true(seader_recv_t1(&seader, &final_message)); + munit_assert_size(g_t1_host_test_state.process_call_count, ==, 1); + munit_assert_true(last_apdu_matches((const uint8_t*)"\x44\x55\x66\x77\x88", 5)); + munit_assert_null(uart.t1.rx_buffer); + return MUNIT_OK; +} + +static MunitResult test_recv_ifs_request(const MunitParameter params[], void* fixture) { + (void)params; + (void)fixture; + + SeaderUartBridge uart = {0}; + SeaderWorker worker = {0}; + Seader seader = make_test_seader(&uart, &worker); + + t1_host_test_reset(); + uart.t1.ifsc = 0x33; + uint8_t ifs_request[] = {0x00, 0xC1, 0x01, 0x20, 0x00}; + seader_add_lrc(ifs_request, 4); + CCID_Message ifs_request_message = {.payload = ifs_request, .dwLength = 5}; + + munit_assert_false(seader_recv_t1(&seader, &ifs_request_message)); + munit_assert_size(g_t1_host_test_state.xfrblock_call_count, ==, 1); + munit_assert_true(last_frame_prefix_matches((const uint8_t*)"\x00\xE1\x01\x33", 4)); + return MUNIT_OK; +} + +static MunitResult test_recv_r_block_resends_buffer(const MunitParameter params[], void* fixture) { + (void)params; + (void)fixture; + + SeaderUartBridge uart = {0}; + SeaderWorker worker = {0}; + Seader seader = make_test_seader(&uart, &worker); + + t1_host_test_reset(); + uart.t1.send_pcb = 0x00; + uart.t1.tx_buffer = bit_buffer_alloc(8); + bit_buffer_copy_bytes(uart.t1.tx_buffer, (const uint8_t*)"\xA0\xA1\xA2", 3); + uart.t1.tx_buffer_offset = 0; + uart.t1.ifsc = 4; + uint8_t r_block[] = {0x00, 0x90, 0x00, 0x00}; + seader_add_lrc(r_block, 3); + CCID_Message r_message = {.payload = r_block, .dwLength = 4}; + + munit_assert_false(seader_recv_t1(&seader, &r_message)); + munit_assert_size(g_t1_host_test_state.xfrblock_call_count, ==, 1); + munit_assert_not_null(uart.t1.tx_buffer); + bit_buffer_free(uart.t1.tx_buffer); + return MUNIT_OK; +} + +static MunitResult test_reset_clears_state(const MunitParameter params[], void* fixture) { + (void)params; + (void)fixture; + + SeaderUartBridge uart = {0}; + uart.t1.nad = 0x77; + uart.t1.send_pcb = 0x00; + uart.t1.recv_pcb = 0x40; + uart.t1.tx_buffer = bit_buffer_alloc(8); + uart.t1.rx_buffer = bit_buffer_alloc(8); + uart.t1.tx_buffer_offset = 3; + + seader_t_1_reset(&uart); + munit_assert_uint8(uart.t1.nad, ==, 0x00); + munit_assert_uint8(uart.t1.send_pcb, ==, SEADER_T1_PCB_SEQUENCE_BIT); + munit_assert_uint8(uart.t1.recv_pcb, ==, 0x00); + munit_assert_null(uart.t1.tx_buffer); + munit_assert_null(uart.t1.rx_buffer); + munit_assert_size(uart.t1.tx_buffer_offset, ==, 0); + return MUNIT_OK; +} + +static MunitTest test_t1_cases[] = { + {(char*)"/send/ifs-request", test_send_ifs_request, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {(char*)"/send/single-block", test_send_single_block, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {(char*)"/send/scratchpad-block", + test_send_scratchpad_block, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, + NULL}, + {(char*)"/send/chained-block", + test_send_chained_block, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, + NULL}, + {(char*)"/recv/ifs-response", test_recv_ifs_response, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {(char*)"/recv/single-i-block", + test_recv_single_i_block, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, + NULL}, + {(char*)"/recv/chained-i-blocks", + test_recv_chained_i_blocks, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, + NULL}, + {(char*)"/recv/ifs-request", test_recv_ifs_request, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL}, + {(char*)"/recv/r-block-resends-buffer", + test_recv_r_block_resends_buffer, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, + NULL}, + {(char*)"/reset/clears-state", + test_reset_clears_state, + NULL, + NULL, + MUNIT_TEST_OPTION_NONE, + NULL}, + {NULL, NULL, NULL, NULL, 0, NULL}, +}; + +MunitSuite test_t1_existing_suite = { + "", + test_t1_cases, + NULL, + 1, + MUNIT_SUITE_OPTION_NONE, +}; diff --git a/tests/host/vendor/munit b/tests/host/vendor/munit new file mode 160000 index 0000000..fbbdf14 --- /dev/null +++ b/tests/host/vendor/munit @@ -0,0 +1 @@ +Subproject commit fbbdf1467eb0d04a6ee465def2e529e4c87f2118