mirror of
https://github.com/bettse/seader.git
synced 2026-08-28 04:04:04 +00:00
Add basic test harness using munit
This commit is contained in:
@@ -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
|
||||
|
||||
+3
-2
@@ -1,5 +1,6 @@
|
||||
.vscode/
|
||||
dist/
|
||||
examples/*
|
||||
logs
|
||||
examples/
|
||||
logs/
|
||||
credential/
|
||||
build/
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -21,6 +21,7 @@ App(
|
||||
"*.c",
|
||||
"aeabi_uldivmod.sx",
|
||||
"!plugin/*.c",
|
||||
"!tests",
|
||||
],
|
||||
fap_icon="icons/logo.png",
|
||||
fap_category="NFC",
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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);
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "bit_buffer.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
@@ -0,0 +1,52 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#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,
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
#include <string.h>
|
||||
|
||||
#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,
|
||||
};
|
||||
+1
Submodule tests/host/vendor/munit added at fbbdf1467e
Reference in New Issue
Block a user