#include #include #include #include "trikke_transport.h" typedef struct { trikke_transport_status_t begin_status; trikke_transport_status_t poll_status; unsigned int begin_calls; unsigned int poll_calls; const uint8_t *packet; size_t packet_size; } mock_transport_t; static int fail(int code, const char *message) { fprintf(stderr, "transport fixture failure %d: %s\n", code, message); return code; } static trikke_transport_status_t mock_begin( void *context, const uint8_t *packet, size_t packet_size) { mock_transport_t *mock = context; ++mock->begin_calls; mock->packet = packet; mock->packet_size = packet_size; return mock->begin_status; } static trikke_transport_status_t mock_poll(void *context) { mock_transport_t *mock = context; ++mock->poll_calls; return mock->poll_status; } int main(void) { const uint8_t packet[] = {0x54, 0x52, 0x4B, 0x31}; mock_transport_t mock = { .begin_status = TRIKKE_TRANSPORT_RETRY, .poll_status = TRIKKE_TRANSPORT_PENDING, }; const trikke_transport_t transport = { .context = &mock, .begin = mock_begin, .poll = mock_poll, }; trikke_transport_sender_t sender; trikke_transport_sender_init(&sender); if (trikke_transport_sender_step( &sender, &transport, packet, sizeof(packet)) != TRIKKE_TRANSPORT_RETRY || sender.pending || mock.begin_calls != 1 || mock.poll_calls != 0 || mock.packet != packet || mock.packet_size != sizeof(packet)) { return fail(1, "zero-accept submission must remain retryable"); } mock.begin_status = TRIKKE_TRANSPORT_PENDING; if (trikke_transport_sender_step( &sender, &transport, packet, sizeof(packet)) != TRIKKE_TRANSPORT_PENDING || !sender.pending || mock.begin_calls != 2 || mock.poll_calls != 0) { return fail(2, "accepted submission must become pending"); } if (trikke_transport_sender_step( &sender, &transport, packet, sizeof(packet)) != TRIKKE_TRANSPORT_PENDING || !sender.pending || mock.begin_calls != 2 || mock.poll_calls != 1) { return fail(3, "pending transfer must poll without resubmission"); } mock.poll_status = TRIKKE_TRANSPORT_COMPLETE; if (trikke_transport_sender_step( &sender, &transport, packet, sizeof(packet)) != TRIKKE_TRANSPORT_COMPLETE || sender.pending || mock.begin_calls != 2 || mock.poll_calls != 2) { return fail(4, "completed transfer must return to idle"); } mock.begin_status = TRIKKE_TRANSPORT_COMPLETE; if (trikke_transport_sender_step( &sender, &transport, packet, sizeof(packet)) != TRIKKE_TRANSPORT_COMPLETE || sender.pending || mock.begin_calls != 3) { return fail(5, "synchronous completion contract"); } mock.begin_status = TRIKKE_TRANSPORT_PENDING; mock.poll_status = TRIKKE_TRANSPORT_RETRY; if (trikke_transport_sender_step( &sender, &transport, packet, sizeof(packet)) != TRIKKE_TRANSPORT_PENDING || trikke_transport_sender_step( &sender, &transport, packet, sizeof(packet)) != TRIKKE_TRANSPORT_FATAL || !sender.pending || mock.begin_calls != 4 || mock.poll_calls != 3 || trikke_transport_sender_step( &sender, &transport, packet, sizeof(packet)) != TRIKKE_TRANSPORT_FATAL || mock.begin_calls != 4 || mock.poll_calls != 4) { return fail(6, "retry after acceptance must fail closed without resubmit"); } trikke_transport_sender_init(&sender); if (trikke_transport_sender_step( NULL, &transport, packet, sizeof(packet)) != TRIKKE_TRANSPORT_FATAL || trikke_transport_sender_step( &sender, NULL, packet, sizeof(packet)) != TRIKKE_TRANSPORT_FATAL || trikke_transport_sender_step( &sender, &transport, NULL, sizeof(packet)) != TRIKKE_TRANSPORT_FATAL || trikke_transport_sender_step( &sender, &transport, packet, 0) != TRIKKE_TRANSPORT_FATAL) { return fail(7, "invalid arguments must fail closed"); } mock.begin_status = (trikke_transport_status_t)99; if (trikke_transport_sender_step( &sender, &transport, packet, sizeof(packet)) != TRIKKE_TRANSPORT_FATAL || sender.pending) { return fail(8, "unknown backend status must fail closed"); } return 0; }