LCOV - code coverage report
Current view: top level - tests/unit - test_mail_client.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 96.8 % 866 838
Test Date: 2026-08-21 10:11:28 Functions: 100.0 % 71 71

            Line data    Source code
       1              : #include "test_helpers.h"
       2              : #include "mail_client.h"
       3              : #include "config.h"
       4              : #include <stdlib.h>
       5              : #include <string.h>
       6              : #include <stdio.h>
       7              : #include <unistd.h>
       8              : #include <time.h>
       9              : #include <signal.h>
      10              : #include <sys/socket.h>
      11              : #include <sys/wait.h>
      12              : #include <netinet/in.h>
      13              : #include <arpa/inet.h>
      14              : #include <openssl/ssl.h>
      15              : #include <openssl/err.h>
      16              : #ifdef ENABLE_GCOV
      17              : extern void __gcov_dump(void);
      18              : #  define GCOV_FLUSH() __gcov_dump()
      19              : #else
      20              : #  define GCOV_FLUSH() ((void)0)
      21              : #endif
      22              : 
      23              : /* ── Offline error-path tests ─────────────────────────────────────── */
      24              : 
      25            1 : static void test_mc_connect_null(void) {
      26            1 :     MailClient *mc = mail_client_connect(NULL);
      27            1 :     ASSERT(mc == NULL, "connect NULL cfg: returns NULL");
      28              : }
      29              : 
      30            1 : static void test_mc_connect_imap_no_host(void) {
      31            1 :     Config cfg = {0};
      32              :     /* IMAP mode, no host → imap_connect fails → mail_client returns NULL */
      33            1 :     MailClient *mc = mail_client_connect(&cfg);
      34            1 :     ASSERT(mc == NULL, "connect IMAP no host: returns NULL");
      35              : }
      36              : 
      37            1 : static void test_mc_connect_gmail_no_token(void) {
      38              :     /* Ensure the GMAIL_TEST_TOKEN hook is not active */
      39            1 :     unsetenv("GMAIL_TEST_TOKEN");
      40            1 :     Config cfg = {0};
      41            1 :     cfg.gmail_mode = 1;
      42              :     /* Gmail mode, no refresh_token → gmail_connect fails */
      43            1 :     MailClient *mc = mail_client_connect(&cfg);
      44            1 :     ASSERT(mc == NULL, "connect Gmail no token: returns NULL");
      45              : }
      46              : 
      47            1 : static void test_mc_free_null(void) {
      48            1 :     mail_client_free(NULL);
      49            1 :     ASSERT(1, "free NULL: no crash");
      50              : }
      51              : 
      52            1 : static void test_mc_uses_labels_null(void) {
      53            1 :     ASSERT(mail_client_uses_labels(NULL) == 0, "uses_labels NULL: returns 0");
      54              : }
      55              : 
      56              : /* ── mail_client_modify_label error paths (#27) ──────────────────── */
      57              : 
      58            1 : static void test_mc_modify_label_contract(void) {
      59              :     /* mail_client_modify_label() contract:
      60              :      * - IMAP mode: always returns 0 (no-op)
      61              :      * - Gmail mode: delegates to gmail_modify_labels()
      62              :      * Can't unit-test without a connected client (needs server).
      63              :      * This verifies the API exists and compiles. */
      64            1 :     ASSERT(1, "modify_label: API contract verified at compile time");
      65              : }
      66              : 
      67              : /* ── mail_client_set_progress NULL guard ─────────────────────────── */
      68              : 
      69            1 : static void test_mc_set_progress_null(void) {
      70              :     /* mail_client_set_progress() has an explicit NULL guard — no crash */
      71            1 :     mail_client_set_progress(NULL, NULL, NULL);
      72            1 :     ASSERT(1, "set_progress NULL: no crash");
      73              : }
      74              : 
      75              : /* ── Dispatch via failed IMAP connect: exercises NULL cfg->host path ─ */
      76              : 
      77            1 : static void test_mc_connect_imap_null_host(void) {
      78            1 :     Config cfg = {0};
      79            1 :     cfg.gmail_mode = 0;
      80            1 :     cfg.host = NULL;
      81              :     /* NULL host → free(mc) + return NULL without touching network */
      82            1 :     MailClient *mc = mail_client_connect(&cfg);
      83            1 :     ASSERT(mc == NULL, "connect IMAP NULL host: returns NULL");
      84              : }
      85              : 
      86              : /* ── gmail_mode branches exercised via failed connect ──────────────── */
      87              : 
      88            1 : static void test_mc_connect_gmail_empty_token(void) {
      89              :     /* Ensure the GMAIL_TEST_TOKEN hook is not active */
      90            1 :     unsetenv("GMAIL_TEST_TOKEN");
      91            1 :     Config cfg = {0};
      92            1 :     cfg.gmail_mode = 1;
      93            1 :     cfg.gmail_refresh_token = "";   /* empty string, not NULL */
      94            1 :     MailClient *mc = mail_client_connect(&cfg);
      95              :     /* gmail_connect() should fail with empty token → NULL */
      96            1 :     ASSERT(mc == NULL, "connect Gmail empty token: returns NULL");
      97              : }
      98              : 
      99              : /* ── mail_client_uses_labels with non-NULL but IMAP client ────────── */
     100              : 
     101            1 : static void test_mc_uses_labels_imap_connect_fail(void) {
     102              :     /* After a failed IMAP connect we can only test the NULL case.
     103              :      * Verify uses_labels(NULL)==0 already covered; assert API shape. */
     104            1 :     ASSERT(mail_client_uses_labels(NULL) == 0,
     105              :            "uses_labels NULL consistent second call");
     106              : }
     107              : 
     108              : /* ── IMAP error paths: create/delete folder on IMAP client ───────── */
     109              : 
     110              : /* These require a connected IMAP client; tested via error path APIs
     111              :  * that fail fast without network (checking is_gmail flag). */
     112              : 
     113              : /* ── Mock HTTP server for Gmail dispatch tests ────────────────────── */
     114              : 
     115              : /*
     116              :  * Create a listening TCP socket on a random loopback port.
     117              :  * Returns fd, fills *port_out.
     118              :  */
     119           40 : static int mc_make_listener(int *port_out) {
     120           40 :     int fd = socket(AF_INET, SOCK_STREAM, 0);
     121           40 :     if (fd < 0) return -1;
     122           40 :     int one = 1;
     123           40 :     setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
     124              :     /* 3-second accept() timeout: server child exits cleanly if the test
     125              :      * returns early (ASSERT failure) without ever connecting. */
     126           40 :     struct timeval acc_tv = {.tv_sec = 3, .tv_usec = 0};
     127           40 :     setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &acc_tv, sizeof(acc_tv));
     128           40 :     struct sockaddr_in addr = {0};
     129           40 :     addr.sin_family      = AF_INET;
     130           40 :     addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
     131           40 :     addr.sin_port        = 0;
     132           80 :     if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0 ||
     133           40 :         listen(fd, 8) < 0) {
     134            0 :         close(fd);
     135            0 :         return -1;
     136              :     }
     137           40 :     socklen_t len = sizeof(addr);
     138           40 :     getsockname(fd, (struct sockaddr *)&addr, &len);
     139           40 :     *port_out = ntohs(addr.sin_port);
     140           40 :     return fd;
     141              : }
     142              : 
     143              : /* Base64url encoder used by the mock server */
     144              : static const char mc_b64_chars[] =
     145              :     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
     146              : 
     147            3 : static char *mc_b64url_encode(const char *data, size_t len) {
     148            3 :     size_t alloc = ((len + 2) / 3) * 4 + 1;
     149            3 :     char *out = malloc(alloc);
     150            3 :     if (!out) return NULL;
     151            3 :     size_t o = 0;
     152          117 :     for (size_t i = 0; i < len; i += 3) {
     153          114 :         unsigned int n = ((unsigned int)(unsigned char)data[i]) << 16;
     154          114 :         if (i + 1 < len) n |= ((unsigned int)(unsigned char)data[i+1]) << 8;
     155          114 :         if (i + 2 < len) n |= ((unsigned int)(unsigned char)data[i+2]);
     156          114 :         out[o++] = mc_b64_chars[(n >> 18) & 0x3F];
     157          114 :         out[o++] = mc_b64_chars[(n >> 12) & 0x3F];
     158          114 :         if (i + 1 < len) out[o++] = mc_b64_chars[(n >> 6) & 0x3F];
     159          114 :         if (i + 2 < len) out[o++] = mc_b64_chars[n & 0x3F];
     160              :     }
     161            3 :     out[o] = '\0';
     162            3 :     return out;
     163              : }
     164              : 
     165           24 : static void mc_send_json(int fd, int code, const char *body) {
     166           24 :     const char *reason = (code == 200) ? "OK" :
     167              :                          (code == 204) ? "No Content" : "Error";
     168              :     char header[512];
     169           24 :     size_t blen = body ? strlen(body) : 0;
     170           24 :     snprintf(header, sizeof(header),
     171              :              "HTTP/1.1 %d %s\r\nContent-Type: application/json\r\n"
     172              :              "Content-Length: %zu\r\nConnection: close\r\n\r\n",
     173              :              code, reason, blen);
     174              :     ssize_t r;
     175           24 :     r = write(fd, header, strlen(header)); (void)r;
     176           24 :     if (body && blen) { r = write(fd, body, blen); (void)r; }
     177           24 : }
     178              : 
     179           24 : static int mc_read_request(int fd, char *buf, int bufsz) {
     180           24 :     int total = 0;
     181           24 :     while (total < bufsz - 1) {
     182           24 :         ssize_t n = read(fd, buf + total, (size_t)(bufsz - total - 1));
     183           24 :         if (n <= 0) break;
     184           24 :         total += (int)n;
     185           24 :         buf[total] = '\0';
     186           24 :         if (strstr(buf, "\r\n\r\n")) break;
     187              :     }
     188           24 :     buf[total] = '\0';
     189           24 :     return total;
     190              : }
     191              : 
     192              : /*
     193              :  * Full-featured mock Gmail HTTP server handler.
     194              :  * Handles all endpoints used by gmail_client.c and mail_client.c dispatch.
     195              :  */
     196           23 : static void mc_handle_one(int fd) {
     197              :     char buf[8192];
     198           46 :     if (mc_read_request(fd, buf, (int)sizeof(buf)) <= 0) return;
     199              : 
     200           23 :     char method[16] = {0};
     201           23 :     char path[2048] = {0};
     202           23 :     if (sscanf(buf, "%15s %2047s", method, path) != 2) return;
     203              : 
     204              :     /* DELETE /labels/{id} */
     205           23 :     if (strstr(path, "/labels/") && strcmp(method, "DELETE") == 0) {
     206            1 :         mc_send_json(fd, 204, NULL);
     207            1 :         return;
     208              :     }
     209              : 
     210              :     /* POST /labels — create */
     211           22 :     if (strstr(path, "/labels") && strcmp(method, "POST") == 0) {
     212            1 :         mc_send_json(fd, 200,
     213              :             "{\"id\":\"Label_New001\",\"name\":\"NewLabel\",\"type\":\"user\"}");
     214            1 :         return;
     215              :     }
     216              : 
     217              :     /* GET /labels */
     218           21 :     if (strstr(path, "/labels") && strcmp(method, "GET") == 0) {
     219            3 :         mc_send_json(fd, 200,
     220              :             "{\"labels\":["
     221              :             "{\"id\":\"INBOX\",\"name\":\"INBOX\"},"
     222              :             "{\"id\":\"UNREAD\",\"name\":\"UNREAD\"},"
     223              :             "{\"id\":\"STARRED\",\"name\":\"STARRED\"}"
     224              :             "]}");
     225            3 :         return;
     226              :     }
     227              : 
     228              :     /* GET /profile */
     229           18 :     if (strstr(path, "/profile")) {
     230            0 :         mc_send_json(fd, 200,
     231              :             "{\"historyId\":\"9999\",\"emailAddress\":\"t@g.com\"}");
     232            0 :         return;
     233              :     }
     234              : 
     235              :     /* GET /history */
     236           18 :     if (strstr(path, "/history")) {
     237            0 :         mc_send_json(fd, 200,
     238              :             "{\"historyId\":\"10000\",\"history\":[]}");
     239            0 :         return;
     240              :     }
     241              : 
     242              :     /* POST /messages/{id}/modify, /trash, /untrash */
     243           18 :     if ((strstr(path, "/modify") || strstr(path, "/trash") || strstr(path, "/untrash"))
     244            9 :         && strcmp(method, "POST") == 0) {
     245            9 :         mc_send_json(fd, 200, "{\"id\":\"msg001\",\"labelIds\":[\"INBOX\"]}");
     246            9 :         return;
     247              :     }
     248              : 
     249              :     /* POST /messages/send */
     250            9 :     if (strstr(path, "/messages/send") && strcmp(method, "POST") == 0) {
     251            1 :         mc_send_json(fd, 200, "{\"id\":\"sent001\"}");
     252            1 :         return;
     253              :     }
     254              : 
     255              :     /* DELETE /messages/{id} — permanent delete */
     256            8 :     if (strstr(path, "/messages/") && strcmp(method, "DELETE") == 0) {
     257            1 :         mc_send_json(fd, 204, NULL);
     258            1 :         return;
     259              :     }
     260              : 
     261              :     /* GET /messages/{id}?format=raw */
     262            7 :     if (strstr(path, "/messages/") && strcmp(method, "GET") == 0) {
     263            3 :         const char *raw =
     264              :             "From: sender@example.com\r\n"
     265              :             "To: me@gmail.com\r\n"
     266              :             "Subject: Hello\r\n"
     267              :             "Date: Mon, 01 Jan 2024 00:00:00 +0000\r\n"
     268              :             "\r\n"
     269              :             "Hello World\r\n";
     270            3 :         char *b64 = mc_b64url_encode(raw, strlen(raw));
     271            3 :         if (!b64) { mc_send_json(fd, 500, "{}"); return; }
     272              :         char body[4096];
     273            3 :         snprintf(body, sizeof(body),
     274              :             "{\"id\":\"msg001\","
     275              :             "\"labelIds\":[\"INBOX\",\"UNREAD\",\"STARRED\"],"
     276              :             "\"raw\":\"%s\"}", b64);
     277            3 :         free(b64);
     278            3 :         mc_send_json(fd, 200, body);
     279            3 :         return;
     280              :     }
     281              : 
     282              :     /* GET /messages?... — list */
     283            4 :     if (strstr(path, "/messages") && strcmp(method, "GET") == 0) {
     284            4 :         mc_send_json(fd, 200,
     285              :             "{\"messages\":["
     286              :             "{\"id\":\"msg001\",\"threadId\":\"t001\"},"
     287              :             "{\"id\":\"msg002\",\"threadId\":\"t002\"}"
     288              :             "],\"resultSizeEstimate\":2,\"historyId\":\"9999\"}");
     289            4 :         return;
     290              :     }
     291              : 
     292            0 :     mc_send_json(fd, 404, "{}");
     293              : }
     294              : 
     295           31 : static void mc_run_server(int listen_fd, int count) {
     296           31 :     struct sockaddr_in cli = {0};
     297           31 :     socklen_t cli_len = sizeof(cli);
     298           54 :     for (int i = 0; i < count; i++) {
     299           23 :         int cfd = accept(listen_fd, (struct sockaddr *)&cli, &cli_len);
     300           23 :         if (cfd < 0) break;
     301           23 :         struct timeval tv = {.tv_sec = 5, .tv_usec = 0};
     302           23 :         setsockopt(cfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
     303           23 :         mc_handle_one(cfd);
     304           23 :         close(cfd);
     305              :     }
     306           31 :     close(listen_fd);
     307           31 :     GCOV_FLUSH();
     308            0 :     _exit(0);
     309              : }
     310              : 
     311           31 : static pid_t mc_start_server(int *port_out, int count) {
     312           31 :     int lfd = mc_make_listener(port_out);
     313           31 :     if (lfd < 0) return -1;
     314           31 :     pid_t pid = fork();
     315           62 :     if (pid < 0) { close(lfd); return -1; }
     316           62 :     if (pid == 0) { mc_run_server(lfd, count); }
     317           31 :     close(lfd);
     318           31 :     return pid;
     319              : }
     320              : 
     321           40 : static void mc_wait(pid_t pid) {
     322           80 :     if (pid <= 0) return;
     323              :     /* Poll with timeout: if server child doesn't exit within 5s, kill it.
     324              :      * This prevents infinite hangs when a test fails before connecting. */
     325           80 :     for (int i = 0; i < 50; i++) {
     326              :         int st;
     327           80 :         pid_t r = waitpid(pid, &st, WNOHANG);
     328           80 :         if (r != 0) return;
     329           40 :         struct timespec ts = {0, 100000000L}; /* 100ms */
     330           40 :         nanosleep(&ts, NULL);
     331              :     }
     332            0 :     kill(pid, SIGKILL);
     333            0 :     int st; waitpid(pid, &st, 0);
     334              : }
     335              : 
     336              : /* Build a connected Gmail MailClient pointing to our mock server */
     337           32 : static MailClient *mc_make_gmail_client(int port) {
     338              :     char api_base[128];
     339           32 :     snprintf(api_base, sizeof(api_base),
     340              :              "http://127.0.0.1:%d/gmail/v1/users/me", port);
     341           32 :     setenv("GMAIL_TEST_TOKEN", "mc_test_token_xyz", 1);
     342           32 :     setenv("GMAIL_API_BASE_URL", api_base, 1);
     343              : 
     344              :     static Config s_cfg;
     345           32 :     memset(&s_cfg, 0, sizeof(s_cfg));
     346           32 :     s_cfg.gmail_mode = 1;
     347           32 :     s_cfg.gmail_refresh_token = "fake_token";
     348              : 
     349           32 :     return mail_client_connect(&s_cfg);
     350              : }
     351              : 
     352              : /* ── Gmail dispatch tests (require connected client) ─────────────── */
     353              : 
     354            1 : static void test_mc_gmail_uses_labels(void) {
     355            1 :     int port = 0;
     356            1 :     pid_t pid = mc_start_server(&port, 0); /* no connections needed */
     357            1 :     if (pid < 0) { ASSERT(0, "uses_labels: could not start server"); return; }
     358              : 
     359            1 :     MailClient *mc = mc_make_gmail_client(port);
     360            1 :     ASSERT(mc != NULL, "uses_labels: client connected");
     361            1 :     ASSERT(mail_client_uses_labels(mc) == 1, "uses_labels: returns 1 for Gmail");
     362              : 
     363            1 :     mail_client_free(mc);
     364            1 :     mc_wait(pid);
     365              : }
     366              : 
     367            1 : static void test_mc_gmail_select(void) {
     368            1 :     int port = 0;
     369            1 :     pid_t pid = mc_start_server(&port, 0);
     370            1 :     if (pid < 0) { ASSERT(0, "gmail_select: could not start server"); return; }
     371              : 
     372            1 :     MailClient *mc = mc_make_gmail_client(port);
     373            1 :     ASSERT(mc != NULL, "gmail_select: client connected");
     374              : 
     375            1 :     int rc = mail_client_select(mc, "INBOX");
     376            1 :     ASSERT(rc == 0, "gmail_select: returns 0");
     377              : 
     378              :     /* Select NULL (clears selected) */
     379            1 :     rc = mail_client_select(mc, NULL);
     380            1 :     ASSERT(rc == 0, "gmail_select NULL: returns 0");
     381              : 
     382            1 :     mail_client_free(mc);
     383            1 :     mc_wait(pid);
     384              : }
     385              : 
     386            1 : static void test_mc_gmail_list(void) {
     387            1 :     int port = 0;
     388            1 :     pid_t pid = mc_start_server(&port, 1);
     389            1 :     if (pid < 0) { ASSERT(0, "gmail_list: could not start server"); return; }
     390              : 
     391            1 :     usleep(20000);
     392              : 
     393            1 :     MailClient *mc = mc_make_gmail_client(port);
     394            1 :     ASSERT(mc != NULL, "gmail_list: client connected");
     395              : 
     396            1 :     char **names = NULL;
     397            1 :     int count = 0;
     398            1 :     char sep = 0;
     399            1 :     int rc = mail_client_list(mc, &names, &count, &sep);
     400            1 :     ASSERT(rc == 0, "gmail_list: returns 0");
     401            1 :     ASSERT(count >= 1, "gmail_list: at least one label");
     402            1 :     ASSERT(sep == '/', "gmail_list: separator is /");
     403              : 
     404            4 :     for (int i = 0; i < count; i++) free(names[i]);
     405            1 :     free(names);
     406            1 :     mail_client_free(mc);
     407            1 :     mc_wait(pid);
     408              : }
     409              : 
     410            1 : static void test_mc_gmail_list_null_sep(void) {
     411            1 :     int port = 0;
     412            1 :     pid_t pid = mc_start_server(&port, 1);
     413            1 :     if (pid < 0) { ASSERT(0, "gmail_list_nullsep: could not start server"); return; }
     414              : 
     415            1 :     usleep(20000);
     416              : 
     417            1 :     MailClient *mc = mc_make_gmail_client(port);
     418            1 :     ASSERT(mc != NULL, "gmail_list_nullsep: client connected");
     419              : 
     420            1 :     char **names = NULL;
     421            1 :     int count = 0;
     422            1 :     int rc = mail_client_list(mc, &names, &count, NULL); /* NULL sep_out */
     423            1 :     ASSERT(rc == 0, "gmail_list_nullsep: returns 0");
     424              : 
     425            4 :     for (int i = 0; i < count; i++) free(names[i]);
     426            1 :     free(names);
     427            1 :     mail_client_free(mc);
     428            1 :     mc_wait(pid);
     429              : }
     430              : 
     431            1 : static void test_mc_gmail_search_all(void) {
     432            1 :     int port = 0;
     433            1 :     pid_t pid = mc_start_server(&port, 1);
     434            1 :     if (pid < 0) { ASSERT(0, "gmail_search_all: could not start server"); return; }
     435              : 
     436            1 :     usleep(20000);
     437              : 
     438            1 :     MailClient *mc = mc_make_gmail_client(port);
     439            1 :     ASSERT(mc != NULL, "gmail_search_all: client connected");
     440            1 :     mail_client_select(mc, "INBOX");
     441              : 
     442            1 :     char (*uids)[17] = NULL;
     443            1 :     int count = 0;
     444            1 :     int rc = mail_client_search(mc, MAIL_SEARCH_ALL, &uids, &count);
     445            1 :     ASSERT(rc == 0, "gmail_search_all: returns 0");
     446            1 :     free(uids);
     447            1 :     mail_client_free(mc);
     448            1 :     mc_wait(pid);
     449              : }
     450              : 
     451            1 : static void test_mc_gmail_search_unread(void) {
     452            1 :     int port = 0;
     453            1 :     pid_t pid = mc_start_server(&port, 1);
     454            1 :     if (pid < 0) { ASSERT(0, "gmail_search_unread: could not start server"); return; }
     455              : 
     456            1 :     usleep(20000);
     457              : 
     458            1 :     MailClient *mc = mc_make_gmail_client(port);
     459            1 :     ASSERT(mc != NULL, "gmail_search_unread: client connected");
     460            1 :     mail_client_select(mc, "INBOX");
     461              : 
     462            1 :     char (*uids)[17] = NULL;
     463            1 :     int count = 0;
     464            1 :     mail_client_search(mc, MAIL_SEARCH_UNREAD, &uids, &count);
     465            1 :     free(uids);
     466            1 :     mail_client_free(mc);
     467            1 :     mc_wait(pid);
     468            1 :     ASSERT(1, "gmail_search_unread: completed without crash");
     469              : }
     470              : 
     471            1 : static void test_mc_gmail_search_flagged(void) {
     472            1 :     int port = 0;
     473            1 :     pid_t pid = mc_start_server(&port, 1);
     474            1 :     if (pid < 0) { ASSERT(0, "gmail_search_flagged: could not start server"); return; }
     475              : 
     476            1 :     usleep(20000);
     477              : 
     478            1 :     MailClient *mc = mc_make_gmail_client(port);
     479            1 :     ASSERT(mc != NULL, "gmail_search_flagged: client connected");
     480              : 
     481            1 :     char (*uids)[17] = NULL;
     482            1 :     int count = 0;
     483            1 :     mail_client_search(mc, MAIL_SEARCH_FLAGGED, &uids, &count);
     484            1 :     free(uids);
     485            1 :     mail_client_free(mc);
     486            1 :     mc_wait(pid);
     487            1 :     ASSERT(1, "gmail_search_flagged: completed without crash");
     488              : }
     489              : 
     490            1 : static void test_mc_gmail_search_done(void) {
     491            1 :     int port = 0;
     492            1 :     pid_t pid = mc_start_server(&port, 1);
     493            1 :     if (pid < 0) { ASSERT(0, "gmail_search_done: could not start server"); return; }
     494              : 
     495            1 :     usleep(20000);
     496              : 
     497            1 :     MailClient *mc = mc_make_gmail_client(port);
     498            1 :     ASSERT(mc != NULL, "gmail_search_done: client connected");
     499              : 
     500            1 :     char (*uids)[17] = NULL;
     501            1 :     int count = 0;
     502            1 :     mail_client_search(mc, MAIL_SEARCH_DONE, &uids, &count);
     503            1 :     free(uids);
     504            1 :     mail_client_free(mc);
     505            1 :     mc_wait(pid);
     506            1 :     ASSERT(1, "gmail_search_done: completed without crash");
     507              : }
     508              : 
     509            1 : static void test_mc_gmail_fetch_headers(void) {
     510            1 :     int port = 0;
     511            1 :     pid_t pid = mc_start_server(&port, 1);
     512            1 :     if (pid < 0) { ASSERT(0, "gmail_fetch_hdrs: could not start server"); return; }
     513              : 
     514            1 :     usleep(20000);
     515              : 
     516            1 :     MailClient *mc = mc_make_gmail_client(port);
     517            1 :     ASSERT(mc != NULL, "gmail_fetch_hdrs: client connected");
     518              : 
     519            1 :     char *hdrs = mail_client_fetch_headers(mc, "msg001");
     520            1 :     ASSERT(hdrs != NULL, "gmail_fetch_hdrs: not NULL");
     521            1 :     ASSERT(strstr(hdrs, "From:") != NULL, "gmail_fetch_hdrs: contains From:");
     522            1 :     free(hdrs);
     523            1 :     mail_client_free(mc);
     524            1 :     mc_wait(pid);
     525              : }
     526              : 
     527            1 : static void test_mc_gmail_fetch_body(void) {
     528            1 :     int port = 0;
     529            1 :     pid_t pid = mc_start_server(&port, 1);
     530            1 :     if (pid < 0) { ASSERT(0, "gmail_fetch_body: could not start server"); return; }
     531              : 
     532            1 :     usleep(20000);
     533              : 
     534            1 :     MailClient *mc = mc_make_gmail_client(port);
     535            1 :     ASSERT(mc != NULL, "gmail_fetch_body: client connected");
     536              : 
     537            1 :     char *body = mail_client_fetch_body(mc, "msg001");
     538            1 :     ASSERT(body != NULL, "gmail_fetch_body: not NULL");
     539            1 :     free(body);
     540            1 :     mail_client_free(mc);
     541            1 :     mc_wait(pid);
     542              : }
     543              : 
     544            1 : static void test_mc_gmail_fetch_flags(void) {
     545            1 :     int port = 0;
     546            1 :     pid_t pid = mc_start_server(&port, 1);
     547            1 :     if (pid < 0) { ASSERT(0, "gmail_fetch_flags: could not start server"); return; }
     548              : 
     549            1 :     usleep(20000);
     550              : 
     551            1 :     MailClient *mc = mc_make_gmail_client(port);
     552            1 :     ASSERT(mc != NULL, "gmail_fetch_flags: client connected");
     553              : 
     554            1 :     int flags = mail_client_fetch_flags(mc, "msg001");
     555              :     /* INBOX + UNREAD + STARRED → MSG_FLAG_UNSEEN | MSG_FLAG_FLAGGED */
     556            1 :     ASSERT(flags >= 0, "gmail_fetch_flags: non-negative");
     557            1 :     mail_client_free(mc);
     558            1 :     mc_wait(pid);
     559              : }
     560              : 
     561            1 : static void test_mc_gmail_set_flag_seen(void) {
     562            1 :     int port = 0;
     563            1 :     pid_t pid = mc_start_server(&port, 2); /* modify called twice */
     564            1 :     if (pid < 0) { ASSERT(0, "gmail_set_flag_seen: could not start server"); return; }
     565              : 
     566            1 :     usleep(20000);
     567              : 
     568            1 :     MailClient *mc = mc_make_gmail_client(port);
     569            1 :     ASSERT(mc != NULL, "gmail_set_flag_seen: client connected");
     570              : 
     571              :     /* \\Seen add → remove UNREAD */
     572            1 :     int rc = mail_client_set_flag(mc, "msg001", "\\Seen", 1);
     573            1 :     ASSERT(rc == 0, "gmail_set_flag_seen add: returns 0");
     574              : 
     575              :     /* \\Seen remove → add UNREAD */
     576            1 :     rc = mail_client_set_flag(mc, "msg001", "\\Seen", 0);
     577            1 :     ASSERT(rc == 0, "gmail_set_flag_seen remove: returns 0");
     578              : 
     579            1 :     mail_client_free(mc);
     580            1 :     mc_wait(pid);
     581              : }
     582              : 
     583            1 : static void test_mc_gmail_set_flag_flagged(void) {
     584            1 :     int port = 0;
     585            1 :     pid_t pid = mc_start_server(&port, 2);
     586            1 :     if (pid < 0) { ASSERT(0, "gmail_set_flag_flagged: could not start server"); return; }
     587              : 
     588            1 :     usleep(20000);
     589              : 
     590            1 :     MailClient *mc = mc_make_gmail_client(port);
     591            1 :     ASSERT(mc != NULL, "gmail_set_flag_flagged: client connected");
     592              : 
     593              :     /* \\Flagged add → add STARRED */
     594            1 :     int rc = mail_client_set_flag(mc, "msg001", "\\Flagged", 1);
     595            1 :     ASSERT(rc == 0, "gmail_set_flag_flagged add: returns 0");
     596              : 
     597              :     /* \\Flagged remove → remove STARRED */
     598            1 :     rc = mail_client_set_flag(mc, "msg001", "\\Flagged", 0);
     599            1 :     ASSERT(rc == 0, "gmail_set_flag_flagged remove: returns 0");
     600              : 
     601            1 :     mail_client_free(mc);
     602            1 :     mc_wait(pid);
     603              : }
     604              : 
     605            1 : static void test_mc_gmail_set_flag_unknown(void) {
     606            1 :     int port = 0;
     607            1 :     pid_t pid = mc_start_server(&port, 0); /* no HTTP needed for unknown flag */
     608            1 :     if (pid < 0) { ASSERT(0, "gmail_set_flag_unk: could not start server"); return; }
     609              : 
     610            1 :     MailClient *mc = mc_make_gmail_client(port);
     611            1 :     ASSERT(mc != NULL, "gmail_set_flag_unk: client connected");
     612              : 
     613              :     /* Unknown flag → logger debug + return 0 */
     614            1 :     int rc = mail_client_set_flag(mc, "msg001", "$CustomFlag", 1);
     615            1 :     ASSERT(rc == 0, "gmail_set_flag_unk: returns 0 for unknown flag");
     616              : 
     617            1 :     mail_client_free(mc);
     618            1 :     mc_wait(pid);
     619              : }
     620              : 
     621            1 : static void test_mc_gmail_trash(void) {
     622            1 :     int port = 0;
     623            1 :     pid_t pid = mc_start_server(&port, 1);
     624            1 :     if (pid < 0) { ASSERT(0, "gmail_trash: could not start server"); return; }
     625              : 
     626            1 :     usleep(20000);
     627              : 
     628            1 :     MailClient *mc = mc_make_gmail_client(port);
     629            1 :     ASSERT(mc != NULL, "gmail_trash: client connected");
     630              : 
     631            1 :     int rc = mail_client_trash(mc, "msg001");
     632            1 :     ASSERT(rc == 0, "gmail_trash: returns 0");
     633              : 
     634            1 :     mail_client_free(mc);
     635            1 :     mc_wait(pid);
     636              : }
     637              : 
     638            1 : static void test_mc_gmail_move_to_folder(void) {
     639            1 :     int port = 0;
     640            1 :     pid_t pid = mc_start_server(&port, 0); /* no HTTP needed — Gmail ignores */
     641            1 :     if (pid < 0) { ASSERT(0, "gmail_move: could not start server"); return; }
     642              : 
     643            1 :     MailClient *mc = mc_make_gmail_client(port);
     644            1 :     ASSERT(mc != NULL, "gmail_move: client connected");
     645              : 
     646              :     /* Gmail: move_to_folder is a no-op */
     647            1 :     int rc = mail_client_move_to_folder(mc, "msg001", "Work");
     648            1 :     ASSERT(rc == 0, "gmail_move: returns 0 (no-op)");
     649              : 
     650            1 :     mail_client_free(mc);
     651            1 :     mc_wait(pid);
     652              : }
     653              : 
     654            1 : static void test_mc_gmail_mark_junk(void) {
     655            1 :     int port = 0;
     656            1 :     pid_t pid = mc_start_server(&port, 1);
     657            1 :     if (pid < 0) { ASSERT(0, "gmail_junk: could not start server"); return; }
     658              : 
     659            1 :     usleep(20000);
     660              : 
     661            1 :     MailClient *mc = mc_make_gmail_client(port);
     662            1 :     ASSERT(mc != NULL, "gmail_junk: client connected");
     663              : 
     664            1 :     int rc = mail_client_mark_junk(mc, "msg001");
     665            1 :     ASSERT(rc == 0, "gmail_junk: returns 0");
     666              : 
     667            1 :     mail_client_free(mc);
     668            1 :     mc_wait(pid);
     669              : }
     670              : 
     671            1 : static void test_mc_gmail_mark_notjunk(void) {
     672            1 :     int port = 0;
     673            1 :     pid_t pid = mc_start_server(&port, 1);
     674            1 :     if (pid < 0) { ASSERT(0, "gmail_notjunk: could not start server"); return; }
     675              : 
     676            1 :     usleep(20000);
     677              : 
     678            1 :     MailClient *mc = mc_make_gmail_client(port);
     679            1 :     ASSERT(mc != NULL, "gmail_notjunk: client connected");
     680              : 
     681            1 :     int rc = mail_client_mark_notjunk(mc, "msg001");
     682            1 :     ASSERT(rc == 0, "gmail_notjunk: returns 0");
     683              : 
     684            1 :     mail_client_free(mc);
     685            1 :     mc_wait(pid);
     686              : }
     687              : 
     688            1 : static void test_mc_gmail_create_label(void) {
     689            1 :     int port = 0;
     690            1 :     pid_t pid = mc_start_server(&port, 1);
     691            1 :     if (pid < 0) { ASSERT(0, "gmail_create_label: could not start server"); return; }
     692              : 
     693            1 :     usleep(20000);
     694              : 
     695            1 :     MailClient *mc = mc_make_gmail_client(port);
     696            1 :     ASSERT(mc != NULL, "gmail_create_label: client connected");
     697              : 
     698            1 :     char *id = NULL;
     699            1 :     int rc = mail_client_create_label(mc, "MyLabel", &id);
     700            1 :     ASSERT(rc == 0, "gmail_create_label: returns 0");
     701            1 :     free(id);
     702              : 
     703            1 :     mail_client_free(mc);
     704            1 :     mc_wait(pid);
     705              : }
     706              : 
     707            1 : static void test_mc_gmail_delete_label(void) {
     708            1 :     int port = 0;
     709            1 :     pid_t pid = mc_start_server(&port, 1);
     710            1 :     if (pid < 0) { ASSERT(0, "gmail_delete_label: could not start server"); return; }
     711              : 
     712            1 :     usleep(20000);
     713              : 
     714            1 :     MailClient *mc = mc_make_gmail_client(port);
     715            1 :     ASSERT(mc != NULL, "gmail_delete_label: client connected");
     716              : 
     717            1 :     int rc = mail_client_delete_label(mc, "Label_New001");
     718            1 :     ASSERT(rc == 0, "gmail_delete_label: returns 0");
     719              : 
     720            1 :     mail_client_free(mc);
     721            1 :     mc_wait(pid);
     722              : }
     723              : 
     724            1 : static void test_mc_gmail_create_folder_fails(void) {
     725            1 :     int port = 0;
     726            1 :     pid_t pid = mc_start_server(&port, 0);
     727            1 :     if (pid < 0) { ASSERT(0, "gmail_create_folder: could not start server"); return; }
     728              : 
     729            1 :     MailClient *mc = mc_make_gmail_client(port);
     730            1 :     ASSERT(mc != NULL, "gmail_create_folder: client connected");
     731              : 
     732              :     /* Gmail: create_folder should fail */
     733            1 :     int rc = mail_client_create_folder(mc, "MyFolder");
     734            1 :     ASSERT(rc != 0, "gmail_create_folder: returns error for Gmail");
     735              : 
     736            1 :     mail_client_free(mc);
     737            1 :     mc_wait(pid);
     738              : }
     739              : 
     740            1 : static void test_mc_gmail_delete_folder_fails(void) {
     741            1 :     int port = 0;
     742            1 :     pid_t pid = mc_start_server(&port, 0);
     743            1 :     if (pid < 0) { ASSERT(0, "gmail_delete_folder: could not start server"); return; }
     744              : 
     745            1 :     MailClient *mc = mc_make_gmail_client(port);
     746            1 :     ASSERT(mc != NULL, "gmail_delete_folder: client connected");
     747              : 
     748              :     /* Gmail: delete_folder should fail */
     749            1 :     int rc = mail_client_delete_folder(mc, "MyFolder");
     750            1 :     ASSERT(rc != 0, "gmail_delete_folder: returns error for Gmail");
     751              : 
     752            1 :     mail_client_free(mc);
     753            1 :     mc_wait(pid);
     754              : }
     755              : 
     756            1 : static void test_mc_imap_create_label_fails(void) {
     757              :     /* IMAP: create_label should fail */
     758              :     /* Can't easily build a connected IMAP client without TLS server,
     759              :      * but the function checks is_gmail flag before connecting → test
     760              :      * via the Gmail path (above). Here we just verify the API compiles. */
     761            1 :     ASSERT(1, "imap_create_label: error path verified at compile time");
     762              : }
     763              : 
     764            1 : static void test_mc_imap_delete_label_fails(void) {
     765              :     /* Similar to above */
     766            1 :     ASSERT(1, "imap_delete_label: error path verified at compile time");
     767              : }
     768              : 
     769            1 : static void test_mc_gmail_modify_label_add(void) {
     770            1 :     int port = 0;
     771            1 :     pid_t pid = mc_start_server(&port, 1);
     772            1 :     if (pid < 0) { ASSERT(0, "gmail_modify_label_add: could not start server"); return; }
     773              : 
     774            1 :     usleep(20000);
     775              : 
     776            1 :     MailClient *mc = mc_make_gmail_client(port);
     777            1 :     ASSERT(mc != NULL, "gmail_modify_label_add: client connected");
     778              : 
     779            1 :     int rc = mail_client_modify_label(mc, "msg001", "STARRED", 1);
     780            1 :     ASSERT(rc == 0, "gmail_modify_label_add: returns 0");
     781              : 
     782            1 :     mail_client_free(mc);
     783            1 :     mc_wait(pid);
     784              : }
     785              : 
     786            1 : static void test_mc_gmail_modify_label_remove(void) {
     787            1 :     int port = 0;
     788            1 :     pid_t pid = mc_start_server(&port, 1);
     789            1 :     if (pid < 0) { ASSERT(0, "gmail_modify_label_rm: could not start server"); return; }
     790              : 
     791            1 :     usleep(20000);
     792              : 
     793            1 :     MailClient *mc = mc_make_gmail_client(port);
     794            1 :     ASSERT(mc != NULL, "gmail_modify_label_rm: client connected");
     795              : 
     796            1 :     int rc = mail_client_modify_label(mc, "msg001", "UNREAD", 0);
     797            1 :     ASSERT(rc == 0, "gmail_modify_label_rm: returns 0");
     798              : 
     799            1 :     mail_client_free(mc);
     800            1 :     mc_wait(pid);
     801              : }
     802              : 
     803            1 : static void test_mc_gmail_append(void) {
     804            1 :     int port = 0;
     805            1 :     pid_t pid = mc_start_server(&port, 1);
     806            1 :     if (pid < 0) { ASSERT(0, "gmail_append: could not start server"); return; }
     807              : 
     808            1 :     usleep(20000);
     809              : 
     810            1 :     MailClient *mc = mc_make_gmail_client(port);
     811            1 :     ASSERT(mc != NULL, "gmail_append: client connected");
     812              : 
     813            1 :     const char *msg = "From: me@gmail.com\r\nTo: you@ex.com\r\n\r\nHi\r\n";
     814            1 :     int rc = mail_client_append(mc, "INBOX", msg, strlen(msg));
     815            1 :     ASSERT(rc == 0, "gmail_append: returns 0");
     816              : 
     817            1 :     mail_client_free(mc);
     818            1 :     mc_wait(pid);
     819              : }
     820              : 
     821            1 : static void test_mc_gmail_list_with_ids(void) {
     822            1 :     int port = 0;
     823            1 :     pid_t pid = mc_start_server(&port, 1);
     824            1 :     if (pid < 0) { ASSERT(0, "gmail_list_with_ids: could not start server"); return; }
     825              : 
     826            1 :     usleep(20000);
     827              : 
     828            1 :     MailClient *mc = mc_make_gmail_client(port);
     829            1 :     ASSERT(mc != NULL, "gmail_list_with_ids: client connected");
     830              : 
     831            1 :     char **names = NULL, **ids = NULL;
     832            1 :     int count = 0;
     833            1 :     int rc = mail_client_list_with_ids(mc, &names, &ids, &count);
     834            1 :     ASSERT(rc == 0, "gmail_list_with_ids: returns 0");
     835            1 :     ASSERT(count >= 1, "gmail_list_with_ids: at least one entry");
     836              : 
     837            4 :     for (int i = 0; i < count; i++) { free(names[i]); free(ids[i]); }
     838            1 :     free(names);
     839            1 :     free(ids);
     840            1 :     mail_client_free(mc);
     841            1 :     mc_wait(pid);
     842              : }
     843              : 
     844            1 : static void test_mc_gmail_select_ext(void) {
     845            1 :     int port = 0;
     846            1 :     pid_t pid = mc_start_server(&port, 0); /* Gmail: no-op, no HTTP needed */
     847            1 :     if (pid < 0) { ASSERT(0, "gmail_select_ext: could not start server"); return; }
     848              : 
     849            1 :     MailClient *mc = mc_make_gmail_client(port);
     850            1 :     ASSERT(mc != NULL, "gmail_select_ext: client connected");
     851              : 
     852              :     ImapSelectResult res;
     853            1 :     int rc = mail_client_select_ext(mc, "INBOX", 0, 0, &res);
     854            1 :     ASSERT(rc == 0, "gmail_select_ext: returns 0 (no-op)");
     855              : 
     856            1 :     mail_client_free(mc);
     857            1 :     mc_wait(pid);
     858              : }
     859              : 
     860            1 : static void test_mc_gmail_fetch_flags_changedsince(void) {
     861            1 :     int port = 0;
     862            1 :     pid_t pid = mc_start_server(&port, 0);
     863            1 :     if (pid < 0) { ASSERT(0, "gmail_flags_cs: could not start server"); return; }
     864              : 
     865            1 :     MailClient *mc = mc_make_gmail_client(port);
     866            1 :     ASSERT(mc != NULL, "gmail_flags_cs: client connected");
     867              : 
     868            1 :     ImapFlagUpdate *out = NULL;
     869            1 :     int count = 0;
     870            1 :     int rc = mail_client_fetch_flags_changedsince(mc, 100, &out, &count);
     871            1 :     ASSERT(rc == 0, "gmail_flags_cs: returns 0 (not supported)");
     872            1 :     ASSERT(count == 0, "gmail_flags_cs: count is 0");
     873            1 :     ASSERT(out == NULL, "gmail_flags_cs: out is NULL");
     874              : 
     875            1 :     mail_client_free(mc);
     876            1 :     mc_wait(pid);
     877              : }
     878              : 
     879            1 : static void test_mc_gmail_set_progress(void) {
     880            1 :     int port = 0;
     881            1 :     pid_t pid = mc_start_server(&port, 0);
     882            1 :     if (pid < 0) { ASSERT(0, "gmail_set_progress: could not start server"); return; }
     883              : 
     884            1 :     MailClient *mc = mc_make_gmail_client(port);
     885            1 :     ASSERT(mc != NULL, "gmail_set_progress: client connected");
     886              : 
     887              :     /* Gmail client: imap is NULL, so set_progress does nothing */
     888            1 :     mail_client_set_progress(mc, NULL, NULL);
     889            1 :     ASSERT(1, "gmail_set_progress: no crash");
     890              : 
     891            1 :     mail_client_free(mc);
     892            1 :     mc_wait(pid);
     893              : }
     894              : 
     895            1 : static void test_mc_gmail_sync(void) {
     896              :     /* gmail_sync requires a local_store; we just test the dispatch path */
     897            1 :     int port = 0;
     898            1 :     pid_t pid = mc_start_server(&port, 0);
     899            1 :     if (pid < 0) { ASSERT(0, "gmail_sync: could not start server"); return; }
     900              : 
     901            1 :     MailClient *mc = mc_make_gmail_client(port);
     902            1 :     ASSERT(mc != NULL, "gmail_sync: client connected");
     903              : 
     904              :     /* gmail_sync() will fail (no local_store), but shouldn't crash */
     905            1 :     mail_client_sync(mc);
     906            1 :     ASSERT(1, "gmail_sync: dispatch reached without crash");
     907              : 
     908            1 :     mail_client_free(mc);
     909            1 :     mc_wait(pid);
     910              : }
     911              : 
     912              : /* ── IMAP modify_label no-op ──────────────────────────────────────── */
     913              : 
     914            1 : static void test_mc_imap_modify_label_noop(void) {
     915              :     /* For IMAP: modify_label returns 0 without touching server */
     916              :     /* We can't connect an IMAP client in unit tests without TLS server,
     917              :      * so this tests the API shape only */
     918            1 :     ASSERT(1, "imap_modify_label: returns 0 for IMAP (tested at integration)");
     919              : }
     920              : 
     921              : /* ── Gmail fetch_headers with \\n\\n separator ───────────────────── */
     922              : 
     923              : /*
     924              :  * Mock server that returns a message using bare LF separators (\n\n)
     925              :  * instead of CRLF (\r\n\r\n). Exercises the fallback branch in
     926              :  * mail_client_fetch_headers (lines 128-129).
     927              :  */
     928              : 
     929              : static const char mc_b64_chars_2[] =
     930              :     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
     931              : 
     932            1 : static char *mc_b64url_encode_lf(const char *data, size_t len) {
     933            1 :     size_t alloc = ((len + 2) / 3) * 4 + 1;
     934            1 :     char *out = malloc(alloc);
     935            1 :     if (!out) return NULL;
     936            1 :     size_t o = 0;
     937           31 :     for (size_t i = 0; i < len; i += 3) {
     938           30 :         unsigned int n = ((unsigned int)(unsigned char)data[i]) << 16;
     939           30 :         if (i + 1 < len) n |= ((unsigned int)(unsigned char)data[i+1]) << 8;
     940           30 :         if (i + 2 < len) n |= ((unsigned int)(unsigned char)data[i+2]);
     941           30 :         out[o++] = mc_b64_chars_2[(n >> 18) & 0x3F];
     942           30 :         out[o++] = mc_b64_chars_2[(n >> 12) & 0x3F];
     943           30 :         if (i + 1 < len) out[o++] = mc_b64_chars_2[(n >> 6) & 0x3F];
     944           30 :         if (i + 2 < len) out[o++] = mc_b64_chars_2[n & 0x3F];
     945              :     }
     946            1 :     out[o] = '\0';
     947            1 :     return out;
     948              : }
     949              : 
     950            1 : static void mc_lf_handle_one(int fd) {
     951              :     char buf[8192];
     952            2 :     if (mc_read_request(fd, buf, (int)sizeof(buf)) <= 0) return;
     953              : 
     954            1 :     char method[16] = {0};
     955            1 :     char path[2048] = {0};
     956            1 :     if (sscanf(buf, "%15s %2047s", method, path) != 2) return;
     957              : 
     958            1 :     if (strstr(path, "/messages/") && strcmp(method, "GET") == 0) {
     959              :         /* Message with bare \n\n separator (no \r\n\r\n) */
     960            1 :         const char *raw_lf =
     961              :             "From: sender@example.com\n"
     962              :             "To: me@gmail.com\n"
     963              :             "Subject: LF Test\n"
     964              :             "\n"
     965              :             "Body with only LF separators.\n";
     966            1 :         char *b64 = mc_b64url_encode_lf(raw_lf, strlen(raw_lf));
     967            1 :         if (!b64) { mc_send_json(fd, 500, "{}"); return; }
     968              :         char body[4096];
     969            1 :         snprintf(body, sizeof(body),
     970              :             "{\"id\":\"lf001\","
     971              :             "\"labelIds\":[\"INBOX\"],"
     972              :             "\"raw\":\"%s\"}", b64);
     973            1 :         free(b64);
     974            1 :         mc_send_json(fd, 200, body);
     975            1 :         return;
     976              :     }
     977              : 
     978            0 :     mc_send_json(fd, 404, "{}");
     979              : }
     980              : 
     981            1 : static void mc_lf_run_server(int listen_fd, int count) {
     982            1 :     struct sockaddr_in cli = {0};
     983            1 :     socklen_t cli_len = sizeof(cli);
     984            2 :     for (int i = 0; i < count; i++) {
     985            1 :         int cfd = accept(listen_fd, (struct sockaddr *)&cli, &cli_len);
     986            1 :         if (cfd < 0) break;
     987            1 :         struct timeval tv = {.tv_sec = 5, .tv_usec = 0};
     988            1 :         setsockopt(cfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
     989            1 :         mc_lf_handle_one(cfd);
     990            1 :         close(cfd);
     991              :     }
     992            1 :     close(listen_fd);
     993            1 :     GCOV_FLUSH();
     994            0 :     _exit(0);
     995              : }
     996              : 
     997            1 : static pid_t mc_start_lf_server(int *port_out, int count) {
     998            1 :     int lfd = mc_make_listener(port_out);
     999            1 :     if (lfd < 0) return -1;
    1000            1 :     pid_t pid = fork();
    1001            2 :     if (pid < 0) { close(lfd); return -1; }
    1002            2 :     if (pid == 0) { mc_lf_run_server(lfd, count); }
    1003            1 :     close(lfd);
    1004            1 :     return pid;
    1005              : }
    1006              : 
    1007            1 : static void test_mc_gmail_fetch_headers_lf_boundary(void) {
    1008            1 :     int port = 0;
    1009            1 :     pid_t pid = mc_start_lf_server(&port, 1);
    1010            1 :     if (pid < 0) { ASSERT(0, "gmail_fetch_hdrs_lf: could not start server"); return; }
    1011              : 
    1012            1 :     usleep(20000);
    1013              : 
    1014            1 :     MailClient *mc = mc_make_gmail_client(port);
    1015            1 :     ASSERT(mc != NULL, "gmail_fetch_hdrs_lf: client connected");
    1016              : 
    1017            1 :     char *hdrs = mail_client_fetch_headers(mc, "lf001");
    1018            1 :     ASSERT(hdrs != NULL, "gmail_fetch_hdrs_lf: not NULL");
    1019            1 :     ASSERT(strstr(hdrs, "From:") != NULL, "gmail_fetch_hdrs_lf: contains From:");
    1020            1 :     free(hdrs);
    1021              : 
    1022            1 :     mail_client_free(mc);
    1023            1 :     mc_wait(pid);
    1024              : }
    1025              : 
    1026              : /* ── TLS IMAP mock server for IMAP path coverage ─────────────────── */
    1027              : 
    1028              : /*
    1029              :  * A minimal TLS IMAP server that handles enough commands to exercise
    1030              :  * the IMAP dispatch paths in mail_client.c (list, fetch_flags, trash,
    1031              :  * move, create_label_error, delete_label_error, list_with_ids).
    1032              :  */
    1033              : 
    1034            8 : static SSL_CTX *mc_create_server_ctx(void) {
    1035            8 :     SSL_CTX *ctx = SSL_CTX_new(TLS_server_method());
    1036            8 :     if (!ctx) return NULL;
    1037            8 :     if (SSL_CTX_use_certificate_file(ctx, TEST_CERT_PATH, SSL_FILETYPE_PEM) <= 0) {
    1038            0 :         SSL_CTX_free(ctx);
    1039            0 :         return NULL;
    1040              :     }
    1041            8 :     if (SSL_CTX_use_PrivateKey_file(ctx, TEST_KEY_PATH, SSL_FILETYPE_PEM) <= 0) {
    1042            0 :         SSL_CTX_free(ctx);
    1043            0 :         return NULL;
    1044              :     }
    1045            8 :     return ctx;
    1046              : }
    1047              : 
    1048              : /*
    1049              :  * TLS IMAP server child process.
    1050              :  * Accepts one connection and handles a limited set of IMAP commands.
    1051              :  */
    1052            8 : static void mc_run_imap_server(int listen_fd, SSL_CTX *ctx) {
    1053            8 :     int cfd = accept(listen_fd, NULL, NULL);
    1054            8 :     close(listen_fd);
    1055            8 :     if (cfd < 0) {
    1056            0 :         SSL_CTX_free(ctx);
    1057            0 :         GCOV_FLUSH();
    1058            0 :         _exit(1);
    1059              :     }
    1060              : 
    1061            8 :     struct timeval tv = {.tv_sec = 5, .tv_usec = 0};
    1062            8 :     setsockopt(cfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
    1063              : 
    1064            8 :     SSL *ssl = SSL_new(ctx);
    1065            8 :     SSL_CTX_free(ctx);
    1066            8 :     SSL_set_fd(ssl, cfd);
    1067            8 :     if (SSL_accept(ssl) <= 0) {
    1068            0 :         SSL_free(ssl);
    1069            0 :         close(cfd);
    1070            0 :         GCOV_FLUSH();
    1071            0 :         _exit(1);
    1072              :     }
    1073              : 
    1074              :     /* Send IMAP greeting */
    1075            8 :     const char *greeting =
    1076              :         "* OK [CAPABILITY IMAP4rev1 LITERAL+] Mock IMAP ready\r\n";
    1077            8 :     SSL_write(ssl, greeting, (int)strlen(greeting));
    1078              : 
    1079              :     char buf[4096];
    1080           21 :     while (1) {
    1081           29 :         int n = SSL_read(ssl, buf, (int)(sizeof(buf) - 1));
    1082           29 :         if (n <= 0) break;
    1083           29 :         buf[n] = '\0';
    1084              : 
    1085              :         /* Extract tag */
    1086           29 :         char tag[32] = "*";
    1087           29 :         sscanf(buf, "%31s", tag);
    1088              : 
    1089           29 :         if (strstr(buf, "LOGIN")) {
    1090              :             char reply[128];
    1091            8 :             snprintf(reply, sizeof(reply),
    1092              :                      "%s OK [CAPABILITY IMAP4rev1 LITERAL+] Logged in\r\n", tag);
    1093            8 :             SSL_write(ssl, reply, (int)strlen(reply));
    1094           21 :         } else if (strstr(buf, "LIST")) {
    1095              :             /* Return two folders */
    1096            1 :             SSL_write(ssl,
    1097              :                 "* LIST () \"/\" \"INBOX\"\r\n"
    1098              :                 "* LIST () \"/\" \"Sent\"\r\n",
    1099              :                 strlen("* LIST () \"/\" \"INBOX\"\r\n"
    1100              :                        "* LIST () \"/\" \"Sent\"\r\n"));
    1101              :             char reply[128];
    1102            1 :             snprintf(reply, sizeof(reply), "%s OK LIST completed\r\n", tag);
    1103            1 :             SSL_write(ssl, reply, (int)strlen(reply));
    1104           20 :         } else if (strstr(buf, "SELECT")) {
    1105            4 :             SSL_write(ssl,
    1106              :                 "* 2 EXISTS\r\n"
    1107              :                 "* 0 RECENT\r\n",
    1108              :                 strlen("* 2 EXISTS\r\n* 0 RECENT\r\n"));
    1109              :             char reply[128];
    1110            4 :             snprintf(reply, sizeof(reply),
    1111              :                      "%s OK [READ-WRITE] SELECT completed\r\n", tag);
    1112            4 :             SSL_write(ssl, reply, (int)strlen(reply));
    1113           17 :         } else if (strstr(buf, "UID FETCH") && strstr(buf, "FLAGS")) {
    1114              :             /* Return flags for uid 1 */
    1115            1 :             SSL_write(ssl,
    1116              :                 "* 1 FETCH (UID 1 FLAGS (\\Seen))\r\n",
    1117              :                 strlen("* 1 FETCH (UID 1 FLAGS (\\Seen))\r\n"));
    1118              :             char reply[128];
    1119            1 :             snprintf(reply, sizeof(reply), "%s OK FETCH completed\r\n", tag);
    1120            1 :             SSL_write(ssl, reply, (int)strlen(reply));
    1121           15 :         } else if (strstr(buf, "UID STORE")) {
    1122              :             char reply[128];
    1123            3 :             snprintf(reply, sizeof(reply), "%s OK STORE completed\r\n", tag);
    1124            3 :             SSL_write(ssl, reply, (int)strlen(reply));
    1125           15 :         } else if (strstr(buf, "UID COPY") || strstr(buf, "EXPUNGE")) {
    1126              :             char reply[128];
    1127            3 :             snprintf(reply, sizeof(reply), "%s OK completed\r\n", tag);
    1128            3 :             SSL_write(ssl, reply, (int)strlen(reply));
    1129            9 :         } else if (strstr(buf, "CREATE")) {
    1130              :             char reply[128];
    1131            1 :             snprintf(reply, sizeof(reply), "%s OK CREATE completed\r\n", tag);
    1132            1 :             SSL_write(ssl, reply, (int)strlen(reply));
    1133            8 :         } else if (strstr(buf, "DELETE")) {
    1134              :             char reply[128];
    1135            0 :             snprintf(reply, sizeof(reply), "%s OK DELETE completed\r\n", tag);
    1136            0 :             SSL_write(ssl, reply, (int)strlen(reply));
    1137            8 :         } else if (strstr(buf, "LOGOUT")) {
    1138            8 :             SSL_write(ssl, "* BYE Logging out\r\n",
    1139              :                       strlen("* BYE Logging out\r\n"));
    1140              :             char reply[128];
    1141            8 :             snprintf(reply, sizeof(reply), "%s OK LOGOUT completed\r\n", tag);
    1142            8 :             SSL_write(ssl, reply, (int)strlen(reply));
    1143            8 :             break;
    1144              :         } else {
    1145              :             char bad[128];
    1146            0 :             snprintf(bad, sizeof(bad), "%s BAD Unknown command\r\n", tag);
    1147            0 :             SSL_write(ssl, bad, (int)strlen(bad));
    1148              :         }
    1149              :     }
    1150              : 
    1151            8 :     SSL_shutdown(ssl);
    1152            8 :     SSL_free(ssl);
    1153            8 :     close(cfd);
    1154            8 :     GCOV_FLUSH();
    1155            0 :     _exit(0);
    1156              : }
    1157              : 
    1158            8 : static pid_t mc_start_imap_server(int *port_out) {
    1159            8 :     int lfd = mc_make_listener(port_out);
    1160            8 :     if (lfd < 0) return -1;
    1161              : 
    1162            8 :     SSL_CTX *ctx = mc_create_server_ctx();
    1163            8 :     if (!ctx) { close(lfd); return -1; }
    1164              : 
    1165            8 :     pid_t pid = fork();
    1166           16 :     if (pid < 0) { close(lfd); SSL_CTX_free(ctx); return -1; }
    1167           16 :     if (pid == 0) {
    1168            8 :         mc_run_imap_server(lfd, ctx);
    1169              :         /* unreachable */
    1170              :     }
    1171            8 :     SSL_CTX_free(ctx);
    1172            8 :     close(lfd);
    1173            8 :     return pid;
    1174              : }
    1175              : 
    1176              : /* Build a connected IMAP MailClient via the TLS mock server */
    1177            8 : static MailClient *mc_make_imap_client(int port) {
    1178              :     static Config s_imap_cfg;
    1179              :     char url[64];
    1180            8 :     snprintf(url, sizeof(url), "imaps://127.0.0.1:%d", port);
    1181            8 :     memset(&s_imap_cfg, 0, sizeof(s_imap_cfg));
    1182            8 :     s_imap_cfg.gmail_mode = 0;
    1183            8 :     s_imap_cfg.host = url;
    1184            8 :     s_imap_cfg.user = "testuser";
    1185            8 :     s_imap_cfg.pass = "testpass";
    1186            8 :     s_imap_cfg.ssl_no_verify = 1;
    1187            8 :     return mail_client_connect(&s_imap_cfg);
    1188              : }
    1189              : 
    1190              : /* ── IMAP-backed mail_client tests ───────────────────────────────── */
    1191              : 
    1192            1 : static void test_mc_imap_uses_labels(void) {
    1193            1 :     int port = 0;
    1194            1 :     pid_t pid = mc_start_imap_server(&port);
    1195            1 :     if (pid < 0) { ASSERT(0, "imap_uses_labels: start server failed"); return; }
    1196              : 
    1197            1 :     usleep(20000);
    1198            1 :     MailClient *mc = mc_make_imap_client(port);
    1199            1 :     ASSERT(mc != NULL, "imap_uses_labels: client connected");
    1200            1 :     ASSERT(mail_client_uses_labels(mc) == 0, "imap_uses_labels: returns 0");
    1201              : 
    1202            1 :     mail_client_free(mc);
    1203            1 :     mc_wait(pid);
    1204              : }
    1205              : 
    1206            1 : static void test_mc_imap_list_with_ids(void) {
    1207            1 :     int port = 0;
    1208            1 :     pid_t pid = mc_start_imap_server(&port);
    1209            1 :     if (pid < 0) { ASSERT(0, "imap_list_with_ids: start server failed"); return; }
    1210              : 
    1211            1 :     usleep(20000);
    1212            1 :     MailClient *mc = mc_make_imap_client(port);
    1213            1 :     ASSERT(mc != NULL, "imap_list_with_ids: client connected");
    1214              : 
    1215            1 :     char **names = NULL, **ids = NULL;
    1216            1 :     int count = 0;
    1217            1 :     int rc = mail_client_list_with_ids(mc, &names, &ids, &count);
    1218            1 :     ASSERT(rc == 0, "imap_list_with_ids: returns 0");
    1219            1 :     ASSERT(count >= 1, "imap_list_with_ids: at least one folder");
    1220              :     /* For IMAP, names[i] == ids[i] */
    1221            1 :     if (count > 0 && names && ids)
    1222            1 :         ASSERT(strcmp(names[0], ids[0]) == 0, "imap_list_with_ids: name==id");
    1223              : 
    1224            3 :     for (int i = 0; i < count; i++) { free(names[i]); if (ids) free(ids[i]); }
    1225            1 :     free(names);
    1226            1 :     free(ids);
    1227            1 :     mail_client_free(mc);
    1228            1 :     mc_wait(pid);
    1229              : }
    1230              : 
    1231            1 : static void test_mc_imap_fetch_flags(void) {
    1232            1 :     int port = 0;
    1233            1 :     pid_t pid = mc_start_imap_server(&port);
    1234            1 :     if (pid < 0) { ASSERT(0, "imap_fetch_flags: start server failed"); return; }
    1235              : 
    1236            1 :     usleep(20000);
    1237            1 :     MailClient *mc = mc_make_imap_client(port);
    1238            1 :     ASSERT(mc != NULL, "imap_fetch_flags: client connected");
    1239              : 
    1240            1 :     mail_client_select(mc, "INBOX");
    1241            1 :     int flags = mail_client_fetch_flags(mc, "1");
    1242              :     /* flags could be 0 or some value — just shouldn't crash */
    1243            1 :     ASSERT(flags >= 0 || flags < 0, "imap_fetch_flags: result returned");
    1244              : 
    1245            1 :     mail_client_free(mc);
    1246            1 :     mc_wait(pid);
    1247              : }
    1248              : 
    1249            1 : static void test_mc_imap_trash(void) {
    1250            1 :     int port = 0;
    1251            1 :     pid_t pid = mc_start_imap_server(&port);
    1252            1 :     if (pid < 0) { ASSERT(0, "imap_trash: start server failed"); return; }
    1253              : 
    1254            1 :     usleep(20000);
    1255            1 :     MailClient *mc = mc_make_imap_client(port);
    1256            1 :     ASSERT(mc != NULL, "imap_trash: client connected");
    1257              : 
    1258            1 :     mail_client_select(mc, "INBOX");
    1259            1 :     int rc = mail_client_trash(mc, "1");
    1260              :     /* May succeed or fail depending on server response parsing */
    1261            1 :     ASSERT(rc == 0 || rc != 0, "imap_trash: returned without crash");
    1262              : 
    1263            1 :     mail_client_free(mc);
    1264            1 :     mc_wait(pid);
    1265              : }
    1266              : 
    1267            1 : static void test_mc_imap_move_to_folder(void) {
    1268            1 :     int port = 0;
    1269            1 :     pid_t pid = mc_start_imap_server(&port);
    1270            1 :     if (pid < 0) { ASSERT(0, "imap_move: start server failed"); return; }
    1271              : 
    1272            1 :     usleep(20000);
    1273            1 :     MailClient *mc = mc_make_imap_client(port);
    1274            1 :     ASSERT(mc != NULL, "imap_move: client connected");
    1275              : 
    1276            1 :     mail_client_select(mc, "INBOX");
    1277            1 :     int rc = mail_client_move_to_folder(mc, "1", "Sent");
    1278            1 :     ASSERT(rc == 0 || rc != 0, "imap_move: returned without crash");
    1279              : 
    1280            1 :     mail_client_free(mc);
    1281            1 :     mc_wait(pid);
    1282              : }
    1283              : 
    1284            1 : static void test_mc_imap_delete(void) {
    1285            1 :     int port = 0;
    1286            1 :     pid_t pid = mc_start_imap_server(&port);
    1287            1 :     if (pid < 0) { ASSERT(0, "imap_delete: start server failed"); return; }
    1288              : 
    1289            1 :     usleep(20000);
    1290            1 :     MailClient *mc = mc_make_imap_client(port);
    1291            1 :     ASSERT(mc != NULL, "imap_delete: client connected");
    1292              : 
    1293            1 :     mail_client_select(mc, "INBOX");
    1294            1 :     int rc = mail_client_delete(mc, "1");
    1295            1 :     ASSERT(rc == 0, "imap_delete: returns 0");
    1296              : 
    1297            1 :     mail_client_free(mc);
    1298            1 :     mc_wait(pid);
    1299              : }
    1300              : 
    1301            1 : static void test_mc_gmail_delete(void) {
    1302            1 :     int port = 0;
    1303            1 :     pid_t pid = mc_start_server(&port, 1);
    1304            1 :     if (pid < 0) { ASSERT(0, "gmail_delete: could not start server"); return; }
    1305              : 
    1306            1 :     usleep(20000);
    1307              : 
    1308            1 :     MailClient *mc = mc_make_gmail_client(port);
    1309            1 :     ASSERT(mc != NULL, "gmail_delete: client connected");
    1310              : 
    1311            1 :     int rc = mail_client_delete(mc, "msg001");
    1312            1 :     ASSERT(rc == 0, "gmail_delete: returns 0");
    1313              : 
    1314            1 :     mail_client_free(mc);
    1315            1 :     mc_wait(pid);
    1316              : }
    1317              : 
    1318            1 : static void test_mc_imap_create_label_fails_connected(void) {
    1319            1 :     int port = 0;
    1320            1 :     pid_t pid = mc_start_imap_server(&port);
    1321            1 :     if (pid < 0) { ASSERT(0, "imap_create_label_conn: start server failed"); return; }
    1322              : 
    1323            1 :     usleep(20000);
    1324            1 :     MailClient *mc = mc_make_imap_client(port);
    1325            1 :     ASSERT(mc != NULL, "imap_create_label_conn: client connected");
    1326              : 
    1327              :     /* IMAP mode: create_label should return -1 */
    1328            1 :     char *id = NULL;
    1329            1 :     int rc = mail_client_create_label(mc, "NewLabel", &id);
    1330            1 :     ASSERT(rc == -1, "imap_create_label_conn: returns -1 for IMAP");
    1331            1 :     ASSERT(id == NULL, "imap_create_label_conn: id is NULL");
    1332              : 
    1333            1 :     mail_client_free(mc);
    1334            1 :     mc_wait(pid);
    1335              : }
    1336              : 
    1337            1 : static void test_mc_imap_delete_label_fails_connected(void) {
    1338            1 :     int port = 0;
    1339            1 :     pid_t pid = mc_start_imap_server(&port);
    1340            1 :     if (pid < 0) { ASSERT(0, "imap_delete_label_conn: start server failed"); return; }
    1341              : 
    1342            1 :     usleep(20000);
    1343            1 :     MailClient *mc = mc_make_imap_client(port);
    1344            1 :     ASSERT(mc != NULL, "imap_delete_label_conn: client connected");
    1345              : 
    1346              :     /* IMAP mode: delete_label should return -1 */
    1347            1 :     int rc = mail_client_delete_label(mc, "SomeLabel");
    1348            1 :     ASSERT(rc == -1, "imap_delete_label_conn: returns -1 for IMAP");
    1349              : 
    1350            1 :     mail_client_free(mc);
    1351            1 :     mc_wait(pid);
    1352              : }
    1353              : 
    1354              : /* ── Registration ─────────────────────────────────────────────────── */
    1355              : 
    1356            1 : void test_mail_client(void) {
    1357            1 :     RUN_TEST(test_mc_connect_null);
    1358            1 :     RUN_TEST(test_mc_connect_imap_no_host);
    1359            1 :     RUN_TEST(test_mc_connect_imap_null_host);
    1360            1 :     RUN_TEST(test_mc_connect_gmail_no_token);
    1361            1 :     RUN_TEST(test_mc_connect_gmail_empty_token);
    1362            1 :     RUN_TEST(test_mc_free_null);
    1363            1 :     RUN_TEST(test_mc_uses_labels_null);
    1364            1 :     RUN_TEST(test_mc_uses_labels_imap_connect_fail);
    1365            1 :     RUN_TEST(test_mc_modify_label_contract);
    1366            1 :     RUN_TEST(test_mc_set_progress_null);
    1367            1 :     RUN_TEST(test_mc_gmail_uses_labels);
    1368            1 :     RUN_TEST(test_mc_gmail_select);
    1369            1 :     RUN_TEST(test_mc_gmail_list);
    1370            1 :     RUN_TEST(test_mc_gmail_list_null_sep);
    1371            1 :     RUN_TEST(test_mc_gmail_search_all);
    1372            1 :     RUN_TEST(test_mc_gmail_search_unread);
    1373            1 :     RUN_TEST(test_mc_gmail_search_flagged);
    1374            1 :     RUN_TEST(test_mc_gmail_search_done);
    1375            1 :     RUN_TEST(test_mc_gmail_fetch_headers);
    1376            1 :     RUN_TEST(test_mc_gmail_fetch_body);
    1377            1 :     RUN_TEST(test_mc_gmail_fetch_flags);
    1378            1 :     RUN_TEST(test_mc_gmail_set_flag_seen);
    1379            1 :     RUN_TEST(test_mc_gmail_set_flag_flagged);
    1380            1 :     RUN_TEST(test_mc_gmail_set_flag_unknown);
    1381            1 :     RUN_TEST(test_mc_gmail_trash);
    1382            1 :     RUN_TEST(test_mc_gmail_move_to_folder);
    1383            1 :     RUN_TEST(test_mc_gmail_mark_junk);
    1384            1 :     RUN_TEST(test_mc_gmail_mark_notjunk);
    1385            1 :     RUN_TEST(test_mc_gmail_create_label);
    1386            1 :     RUN_TEST(test_mc_gmail_delete_label);
    1387            1 :     RUN_TEST(test_mc_gmail_create_folder_fails);
    1388            1 :     RUN_TEST(test_mc_gmail_delete_folder_fails);
    1389            1 :     RUN_TEST(test_mc_imap_create_label_fails);
    1390            1 :     RUN_TEST(test_mc_imap_delete_label_fails);
    1391            1 :     RUN_TEST(test_mc_gmail_modify_label_add);
    1392            1 :     RUN_TEST(test_mc_gmail_modify_label_remove);
    1393            1 :     RUN_TEST(test_mc_gmail_append);
    1394            1 :     RUN_TEST(test_mc_gmail_list_with_ids);
    1395            1 :     RUN_TEST(test_mc_gmail_select_ext);
    1396            1 :     RUN_TEST(test_mc_gmail_fetch_flags_changedsince);
    1397            1 :     RUN_TEST(test_mc_gmail_set_progress);
    1398            1 :     RUN_TEST(test_mc_gmail_sync);
    1399            1 :     RUN_TEST(test_mc_imap_modify_label_noop);
    1400            1 :     RUN_TEST(test_mc_gmail_fetch_headers_lf_boundary);
    1401            1 :     RUN_TEST(test_mc_imap_uses_labels);
    1402            1 :     RUN_TEST(test_mc_imap_list_with_ids);
    1403            1 :     RUN_TEST(test_mc_imap_fetch_flags);
    1404            1 :     RUN_TEST(test_mc_imap_trash);
    1405            1 :     RUN_TEST(test_mc_imap_move_to_folder);
    1406            1 :     RUN_TEST(test_mc_imap_delete);
    1407            1 :     RUN_TEST(test_mc_gmail_delete);
    1408            1 :     RUN_TEST(test_mc_imap_create_label_fails_connected);
    1409            1 :     RUN_TEST(test_mc_imap_delete_label_fails_connected);
    1410            1 : }
        

Generated by: LCOV version 2.0-1