LCOV - code coverage report
Current view: top level - tests/unit - test_transport.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 93.8 % 65 61
Test Date: 2026-04-20 19:54:22 Functions: 100.0 % 6 6

            Line data    Source code
       1              : /* SPDX-License-Identifier: GPL-3.0-or-later */
       2              : /* Copyright 2026 Peter Csaszar */
       3              : 
       4              : /**
       5              :  * @file test_transport.c
       6              :  * @brief Unit tests for transport EINTR retry behaviour.
       7              :  *
       8              :  * Verifies that transport_send() and transport_recv() transparently retry
       9              :  * when the underlying sys_socket_send / sys_socket_recv call is interrupted
      10              :  * by a signal (errno == EINTR).
      11              :  */
      12              : 
      13              : #include "test_helpers.h"
      14              : #include "transport.h"
      15              : #include "mock_socket.h"
      16              : 
      17              : #include <stdint.h>
      18              : #include <string.h>
      19              : 
      20              : /* ------------------------------------------------------------------ *
      21              :  * Helpers                                                              *
      22              :  * ------------------------------------------------------------------ */
      23              : 
      24              : /**
      25              :  * Build an abridged-framed payload in `out` suitable for
      26              :  * mock_socket_set_response().  payload must be 4-byte aligned.
      27              :  * Returns total frame length written.
      28              :  */
      29            2 : static size_t make_abridged_frame(uint8_t *out, const uint8_t *payload,
      30              :                                   size_t payload_len)
      31              : {
      32            2 :     size_t wire_len = payload_len / 4;
      33            2 :     size_t off = 0;
      34            2 :     if (wire_len < 0x7F) {
      35            2 :         out[off++] = (uint8_t)wire_len;
      36              :     } else {
      37            0 :         out[off++] = 0x7F;
      38            0 :         out[off++] = (uint8_t)(wire_len & 0xFF);
      39            0 :         out[off++] = (uint8_t)((wire_len >> 8) & 0xFF);
      40            0 :         out[off++] = (uint8_t)((wire_len >> 16) & 0xFF);
      41              :     }
      42            2 :     memcpy(out + off, payload, payload_len);
      43            2 :     return off + payload_len;
      44              : }
      45              : 
      46              : /* ------------------------------------------------------------------ *
      47              :  * Tests                                                                *
      48              :  * ------------------------------------------------------------------ */
      49              : 
      50              : /**
      51              :  * transport_send() should succeed even when the first sys_socket_send call
      52              :  * for the payload returns -1/EINTR.
      53              :  *
      54              :  * Call sequence inside transport_send (after connect which uses send #1 for
      55              :  * the 0xEF abridged marker):
      56              :  *   call 1 (abridged marker, during connect): succeeds
      57              :  *   call 2 (1-byte length prefix):            succeeds
      58              :  *   call 3 (payload):                         EINTR on call 3 → retry → succeeds
      59              :  */
      60            1 : static void test_transport_send_eintr_retry(void) {
      61            1 :     mock_socket_reset();
      62              : 
      63              :     Transport t;
      64            1 :     transport_init(&t);
      65            1 :     int rc = transport_connect(&t, "127.0.0.1", 443);
      66            1 :     ASSERT(rc == 0, "connect should succeed");
      67              : 
      68              :     /* Prime EINTR on the 3rd sys_socket_send call (payload chunk) */
      69            1 :     mock_socket_eintr_send_at(3);
      70              : 
      71            1 :     uint8_t payload[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
      72            1 :     rc = transport_send(&t, payload, sizeof(payload));
      73            1 :     ASSERT(rc == 0, "transport_send should succeed after EINTR retry");
      74              : 
      75            1 :     transport_close(&t);
      76              : }
      77              : 
      78              : /**
      79              :  * transport_send() with EINTR on the length-prefix send (call 2) also retries.
      80              :  */
      81            1 : static void test_transport_send_eintr_on_prefix(void) {
      82            1 :     mock_socket_reset();
      83              : 
      84              :     Transport t;
      85            1 :     transport_init(&t);
      86            1 :     int rc = transport_connect(&t, "127.0.0.1", 443);
      87            1 :     ASSERT(rc == 0, "connect should succeed");
      88              : 
      89              :     /* EINTR on 2nd call (length prefix) */
      90            1 :     mock_socket_eintr_send_at(2);
      91              : 
      92            1 :     uint8_t payload[4] = {0xAA, 0xBB, 0xCC, 0xDD};
      93            1 :     rc = transport_send(&t, payload, sizeof(payload));
      94            1 :     ASSERT(rc == 0, "transport_send should succeed after EINTR on prefix");
      95              : 
      96            1 :     transport_close(&t);
      97              : }
      98              : 
      99              : /**
     100              :  * transport_recv() should succeed even when the first recv for the length
     101              :  * prefix byte returns -1/EINTR.
     102              :  *
     103              :  * recv call sequence:
     104              :  *   call 1: length prefix byte — inject EINTR → retry → succeeds
     105              :  *   call 2: payload bytes      — succeeds
     106              :  */
     107            1 : static void test_transport_recv_eintr_on_length(void) {
     108            1 :     mock_socket_reset();
     109              : 
     110              :     Transport t;
     111            1 :     transport_init(&t);
     112            1 :     transport_connect(&t, "127.0.0.1", 443);
     113              : 
     114              :     /* Build a canned response frame */
     115            1 :     uint8_t payload[4] = {0x11, 0x22, 0x33, 0x44};
     116              :     uint8_t frame[16];
     117            1 :     size_t frame_len = make_abridged_frame(frame, payload, sizeof(payload));
     118            1 :     mock_socket_set_response(frame, frame_len);
     119              : 
     120              :     /* EINTR on 1st recv (length prefix byte) */
     121            1 :     mock_socket_eintr_recv_at(1);
     122              : 
     123              :     uint8_t buf[64];
     124            1 :     size_t out_len = 0;
     125            1 :     int rc = transport_recv(&t, buf, sizeof(buf), &out_len);
     126            1 :     ASSERT(rc == 0, "transport_recv should succeed after EINTR on length prefix");
     127            1 :     ASSERT(out_len == 4, "should have received 4 payload bytes");
     128            1 :     ASSERT(memcmp(buf, payload, 4) == 0, "payload bytes should match");
     129              : 
     130            1 :     transport_close(&t);
     131              : }
     132              : 
     133              : /**
     134              :  * transport_recv() should succeed when EINTR fires during the payload read.
     135              :  *
     136              :  * recv call sequence:
     137              :  *   call 1: length prefix byte — succeeds
     138              :  *   call 2: payload chunk      — inject EINTR → retry → succeeds
     139              :  */
     140            1 : static void test_transport_recv_eintr_on_payload(void) {
     141            1 :     mock_socket_reset();
     142              : 
     143              :     Transport t;
     144            1 :     transport_init(&t);
     145            1 :     transport_connect(&t, "127.0.0.1", 443);
     146              : 
     147            1 :     uint8_t payload[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE};
     148              :     uint8_t frame[32];
     149            1 :     size_t frame_len = make_abridged_frame(frame, payload, sizeof(payload));
     150            1 :     mock_socket_set_response(frame, frame_len);
     151              : 
     152              :     /* EINTR on 2nd recv (payload chunk) */
     153            1 :     mock_socket_eintr_recv_at(2);
     154              : 
     155              :     uint8_t buf[64];
     156            1 :     size_t out_len = 0;
     157            1 :     int rc = transport_recv(&t, buf, sizeof(buf), &out_len);
     158            1 :     ASSERT(rc == 0, "transport_recv should succeed after EINTR on payload");
     159            1 :     ASSERT(out_len == 8, "should have received 8 payload bytes");
     160            1 :     ASSERT(memcmp(buf, payload, 8) == 0, "payload bytes should match");
     161              : 
     162            1 :     transport_close(&t);
     163              : }
     164              : 
     165              : /* ------------------------------------------------------------------ *
     166              :  * Suite entry point                                                    *
     167              :  * ------------------------------------------------------------------ */
     168              : 
     169            1 : void run_transport_eintr_tests(void) {
     170            1 :     RUN_TEST(test_transport_send_eintr_retry);
     171            1 :     RUN_TEST(test_transport_send_eintr_on_prefix);
     172            1 :     RUN_TEST(test_transport_recv_eintr_on_length);
     173            1 :     RUN_TEST(test_transport_recv_eintr_on_payload);
     174            1 : }
        

Generated by: LCOV version 2.0-1