LCOV - code coverage report
Current view: top level - tests/mocks - socket.c (source / functions) Coverage Total Hit
Test: coverage-functional.info Lines: 76.6 % 137 105
Test Date: 2026-04-20 19:54:24 Functions: 66.7 % 30 20

            Line data    Source code
       1              : /**
       2              :  * @file socket.c
       3              :  * @brief Mock socket implementation for unit/integration testing.
       4              :  *
       5              :  * Provides an in-memory fake server that stores sent data and returns
       6              :  * pre-programmed responses.  Test accessors in mock_socket.h.
       7              :  */
       8              : 
       9              : #include "platform/socket.h"
      10              : 
      11              : #include <errno.h>
      12              : #include <stdlib.h>
      13              : #include <string.h>
      14              : 
      15              : /* ---- Internal mock state ---- */
      16              : 
      17              : #define MOCK_BUF_SIZE (256 * 1024)
      18              : 
      19              : static struct {
      20              :     int created;
      21              :     int connected;
      22              :     int closed;
      23              :     int nonblocking;
      24              : 
      25              :     /* Sent data (client → server) */
      26              :     uint8_t *sent;
      27              :     size_t   sent_len;
      28              :     size_t   sent_cap;
      29              : 
      30              :     /* Pre-programmed response (server → client) */
      31              :     uint8_t *response;
      32              :     size_t   response_len;
      33              :     size_t   response_pos;
      34              : 
      35              :     /* Failure injection */
      36              :     int fail_create;
      37              :     int fail_connect;
      38              :     int refuse_connect;  /* persistent ECONNREFUSED */
      39              :     int fail_send_at;    /* 0 = never */
      40              :     int fail_recv_at;
      41              :     int short_send_at;
      42              :     int eintr_send_at;   /* Nth send() returns -1/EINTR, then succeeds */
      43              :     int eintr_recv_at;   /* Nth recv() returns -1/EINTR, then succeeds */
      44              :     int send_call_n;     /* call counters */
      45              :     int recv_call_n;
      46              : 
      47              :     /* TEST-82: fragmentation + one-shot EINTR/EAGAIN/EOF */
      48              :     size_t send_fragment;         /* cap each send() at N bytes; 0 = off */
      49              :     size_t recv_fragment;         /* cap each recv() at N bytes; 0 = off */
      50              :     int    inject_eintr_send;     /* next send() returns -1/EINTR */
      51              :     int    inject_eintr_recv;     /* next recv() returns -1/EINTR */
      52              :     int    inject_eagain_send;    /* next send() returns -1/EAGAIN */
      53              :     int    inject_eagain_recv;    /* next recv() returns -1/EAGAIN */
      54              :     int    kill_next_recv;        /* next recv() returns 0 (EOF) */
      55              : 
      56              :     /* Emulator hook: runs after every successful send, lets the fake
      57              :      * server parse + reply before the next recv. */
      58              :     void (*on_sent)(const uint8_t *, size_t);
      59              : } g_mock_socket;
      60              : 
      61              : /* ---- Test accessor functions ---- */
      62              : 
      63          475 : void mock_socket_reset(void) {
      64          475 :     free(g_mock_socket.sent);
      65          475 :     free(g_mock_socket.response);
      66          475 :     memset(&g_mock_socket, 0, sizeof(g_mock_socket));
      67          475 : }
      68              : 
      69            0 : int mock_socket_was_created(void) { return g_mock_socket.created; }
      70            0 : int mock_socket_was_connected(void) { return g_mock_socket.connected; }
      71            0 : int mock_socket_was_closed(void) { return g_mock_socket.closed; }
      72              : 
      73            0 : const uint8_t *mock_socket_get_sent(size_t *out_len) {
      74            0 :     if (out_len) *out_len = g_mock_socket.sent_len;
      75            0 :     return g_mock_socket.sent;
      76              : }
      77              : 
      78            4 : void mock_socket_set_response(const uint8_t *data, size_t len) {
      79            4 :     free(g_mock_socket.response);
      80            4 :     g_mock_socket.response = (uint8_t *)malloc(len);
      81            4 :     memcpy(g_mock_socket.response, data, len);
      82            4 :     g_mock_socket.response_len = len;
      83            4 :     g_mock_socket.response_pos = 0;
      84            4 : }
      85              : 
      86          682 : void mock_socket_append_response(const uint8_t *data, size_t len) {
      87          682 :     if (!data || len == 0) return;
      88          682 :     size_t new_len = g_mock_socket.response_len + len;
      89          682 :     g_mock_socket.response = (uint8_t *)realloc(g_mock_socket.response, new_len);
      90          682 :     memcpy(g_mock_socket.response + g_mock_socket.response_len, data, len);
      91          682 :     g_mock_socket.response_len = new_len;
      92              : }
      93              : 
      94            0 : void mock_socket_clear_sent(void) {
      95            0 :     g_mock_socket.sent_len = 0;
      96            0 : }
      97              : 
      98            1 : void mock_socket_fail_create(void)      { g_mock_socket.fail_create  = 1; }
      99            1 : void mock_socket_fail_connect(void)     { g_mock_socket.fail_connect = 1; }
     100            4 : void mock_socket_fail_send_at(int n)    { g_mock_socket.fail_send_at = n; }
     101            0 : void mock_socket_fail_recv_at(int n)    { g_mock_socket.fail_recv_at = n; }
     102            0 : void mock_socket_short_send_at(int n)   { g_mock_socket.short_send_at = n; }
     103            0 : void mock_socket_eintr_send_at(int n)   { g_mock_socket.eintr_send_at = n; }
     104            0 : void mock_socket_eintr_recv_at(int n)   { g_mock_socket.eintr_recv_at = n; }
     105              : 
     106            1 : void mock_socket_set_send_fragment(size_t step) { g_mock_socket.send_fragment = step; }
     107            1 : void mock_socket_set_recv_fragment(size_t step) { g_mock_socket.recv_fragment = step; }
     108            1 : void mock_socket_inject_eintr_next_send(void)   { g_mock_socket.inject_eintr_send  = 1; }
     109            1 : void mock_socket_inject_eintr_next_recv(void)   { g_mock_socket.inject_eintr_recv  = 1; }
     110            1 : void mock_socket_inject_eagain_next_send(void)  { g_mock_socket.inject_eagain_send = 1; }
     111            1 : void mock_socket_inject_eagain_next_recv(void)  { g_mock_socket.inject_eagain_recv = 1; }
     112            1 : void mock_socket_kill_on_next_recv(void)        { g_mock_socket.kill_next_recv     = 1; }
     113            1 : void mock_socket_refuse_connect(void)           { g_mock_socket.refuse_connect     = 1; }
     114              : 
     115          950 : void mock_socket_set_on_sent(void (*fn)(const uint8_t *, size_t)) {
     116          950 :     g_mock_socket.on_sent = fn;
     117          950 : }
     118              : 
     119              : /* ---- socket.h interface implementation ---- */
     120              : 
     121          259 : int sys_socket_create(void) {
     122          259 :     if (g_mock_socket.fail_create) {
     123            1 :         g_mock_socket.fail_create = 0;
     124            1 :         return -1;
     125              :     }
     126          258 :     g_mock_socket.created++;
     127          258 :     return 42; /* fake fd */
     128              : }
     129              : 
     130          258 : int sys_socket_connect(int fd, const char *host, int port) {
     131              :     (void)fd; (void)host; (void)port;
     132          258 :     if (g_mock_socket.refuse_connect) {
     133              :         /* Persistent refusal — every reconnect attempt fails with
     134              :          * ECONNREFUSED until mock_socket_reset() clears it. */
     135            2 :         errno = ECONNREFUSED;
     136            2 :         return -1;
     137              :     }
     138          256 :     if (g_mock_socket.fail_connect) {
     139            1 :         g_mock_socket.fail_connect = 0;
     140            1 :         return -1;
     141              :     }
     142          255 :     g_mock_socket.connected++;
     143          255 :     return 0;
     144              : }
     145              : 
     146          922 : ssize_t sys_socket_send(int fd, const void *buf, size_t len) {
     147              :     (void)fd;
     148          922 :     if (!buf || len == 0) return 0;
     149              : 
     150          922 :     g_mock_socket.send_call_n++;
     151              : 
     152              :     /* Next-call EINTR / EAGAIN injection beats the Nth-call variant —
     153              :      * tests that do not want to count calls can use these one-shots. */
     154          922 :     if (g_mock_socket.inject_eintr_send) {
     155            1 :         g_mock_socket.inject_eintr_send = 0;
     156            1 :         errno = EINTR;
     157            1 :         return -1;
     158              :     }
     159          921 :     if (g_mock_socket.inject_eagain_send) {
     160            1 :         g_mock_socket.inject_eagain_send = 0;
     161            1 :         errno = EAGAIN;
     162            1 :         return -1;
     163              :     }
     164              : 
     165          920 :     if (g_mock_socket.eintr_send_at &&
     166            0 :         g_mock_socket.send_call_n == g_mock_socket.eintr_send_at) {
     167            0 :         g_mock_socket.eintr_send_at = 0;
     168            0 :         errno = EINTR;
     169            0 :         return -1;
     170              :     }
     171              : 
     172          920 :     if (g_mock_socket.fail_send_at &&
     173            5 :         g_mock_socket.send_call_n == g_mock_socket.fail_send_at) {
     174            4 :         g_mock_socket.fail_send_at = 0;
     175            4 :         return -1;
     176              :     }
     177              : 
     178              :     /* send_fragment: emit at most `step` bytes per call.  Distinct from
     179              :      * short_send_at which is one-shot — send_fragment persists so the
     180              :      * transport layer loops until the caller's payload is drained. */
     181          916 :     size_t emit = len;
     182          916 :     if (g_mock_socket.send_fragment && emit > g_mock_socket.send_fragment) {
     183            5 :         emit = g_mock_socket.send_fragment;
     184              :     }
     185              : 
     186              :     /* Append to sent buffer */
     187          916 :     if (g_mock_socket.sent_len + emit > g_mock_socket.sent_cap) {
     188          246 :         size_t new_cap = g_mock_socket.sent_cap ? g_mock_socket.sent_cap : 4096;
     189          267 :         while (new_cap < g_mock_socket.sent_len + emit) new_cap *= 2;
     190          246 :         g_mock_socket.sent = (uint8_t *)realloc(g_mock_socket.sent, new_cap);
     191          246 :         g_mock_socket.sent_cap = new_cap;
     192              :     }
     193          916 :     memcpy(g_mock_socket.sent + g_mock_socket.sent_len, buf, emit);
     194          916 :     g_mock_socket.sent_len += emit;
     195              : 
     196          916 :     if (g_mock_socket.short_send_at &&
     197            0 :         g_mock_socket.send_call_n == g_mock_socket.short_send_at && emit > 1) {
     198            0 :         g_mock_socket.short_send_at = 0;
     199              :         /* Hook fires after short send too so the server can react to
     200              :          * the partial frame exactly as the real DC would. */
     201            0 :         if (g_mock_socket.on_sent) {
     202            0 :             g_mock_socket.on_sent(g_mock_socket.sent, g_mock_socket.sent_len);
     203              :         }
     204            0 :         return (ssize_t)(emit - 1);
     205              :     }
     206              : 
     207          916 :     if (g_mock_socket.on_sent) {
     208          916 :         g_mock_socket.on_sent(g_mock_socket.sent, g_mock_socket.sent_len);
     209              :     }
     210          916 :     return (ssize_t)emit;
     211              : }
     212              : 
     213          711 : ssize_t sys_socket_recv(int fd, void *buf, size_t len) {
     214              :     (void)fd;
     215          711 :     if (!buf || len == 0) return 0;
     216              : 
     217          711 :     g_mock_socket.recv_call_n++;
     218              : 
     219              :     /* Kill-on-next wins unconditionally so mid-RPC disconnect tests can
     220              :      * interleave it with queued responses.  We also flush any pending
     221              :      * response bytes: a real peer that closed the socket before the
     222              :      * reply was written never delivers those bytes, so the client must
     223              :      * reconnect and re-issue the RPC rather than finding the old reply
     224              :      * on the next recv. */
     225          711 :     if (g_mock_socket.kill_next_recv) {
     226            1 :         g_mock_socket.kill_next_recv = 0;
     227            1 :         g_mock_socket.response_pos = g_mock_socket.response_len;
     228            1 :         return 0;
     229              :     }
     230              : 
     231          710 :     if (g_mock_socket.inject_eintr_recv) {
     232            1 :         g_mock_socket.inject_eintr_recv = 0;
     233            1 :         errno = EINTR;
     234            1 :         return -1;
     235              :     }
     236          709 :     if (g_mock_socket.inject_eagain_recv) {
     237            1 :         g_mock_socket.inject_eagain_recv = 0;
     238            1 :         errno = EAGAIN;
     239            1 :         return -1;
     240              :     }
     241              : 
     242          708 :     if (g_mock_socket.eintr_recv_at &&
     243            0 :         g_mock_socket.recv_call_n == g_mock_socket.eintr_recv_at) {
     244            0 :         g_mock_socket.eintr_recv_at = 0;
     245            0 :         errno = EINTR;
     246            0 :         return -1;
     247              :     }
     248              : 
     249          708 :     if (g_mock_socket.fail_recv_at &&
     250            0 :         g_mock_socket.recv_call_n == g_mock_socket.fail_recv_at) {
     251            0 :         g_mock_socket.fail_recv_at = 0;
     252            0 :         return -1;
     253              :     }
     254              : 
     255          708 :     size_t avail = g_mock_socket.response_len - g_mock_socket.response_pos;
     256          708 :     if (avail == 0) return 0; /* EOF */
     257              : 
     258          705 :     size_t to_read = len < avail ? len : avail;
     259          705 :     if (g_mock_socket.recv_fragment && to_read > g_mock_socket.recv_fragment) {
     260            6 :         to_read = g_mock_socket.recv_fragment;
     261              :     }
     262          705 :     memcpy(buf, g_mock_socket.response + g_mock_socket.response_pos, to_read);
     263          705 :     g_mock_socket.response_pos += to_read;
     264              : 
     265          705 :     return (ssize_t)to_read;
     266              : }
     267              : 
     268          258 : int sys_socket_close(int fd) {
     269              :     (void)fd;
     270          258 :     g_mock_socket.closed++;
     271          258 :     return 0;
     272              : }
     273              : 
     274            0 : int sys_socket_set_nonblocking(int fd) {
     275              :     (void)fd;
     276            0 :     g_mock_socket.nonblocking++;
     277            0 :     return 0;
     278              : }
        

Generated by: LCOV version 2.0-1