LCOV - code coverage report
Current view: top level - tests/pty - test_compose_pty.c (source / functions) Coverage Total Hit
Test: coverage-functional.info Lines: 97.0 % 299 290
Test Date: 2026-05-07 15:53:08 Functions: 100.0 % 27 27

            Line data    Source code
       1              : /**
       2              :  * @file test_compose_pty.c
       3              :  * @brief PTY tests for email-tui compose/reply/send commands.
       4              :  *
       5              :  * Usage: test-pty-compose <email-tui-bin> <mock-smtp-server-bin> <email-cli-bin>
       6              :  */
       7              : 
       8              : #include "ptytest.h"
       9              : #include "pty_assert.h"
      10              : #include <stdio.h>
      11              : #include <stdlib.h>
      12              : #include <string.h>
      13              : #include <unistd.h>
      14              : #include <signal.h>
      15              : #include <fcntl.h>
      16              : #include <sys/stat.h>
      17              : #include <sys/wait.h>
      18              : #include <sys/socket.h>
      19              : #include <arpa/inet.h>
      20              : #include <errno.h>
      21              : 
      22              : /* ── Test infrastructure ─────────────────────────────────────────────── */
      23              : 
      24              : #define COLS 120
      25              : #define ROWS  50
      26              : #define WAIT_MS  4000
      27              : #define SETTLE_MS 300
      28              : 
      29              : static int  g_tests_run    = 0;
      30              : static int  g_tests_failed = 0;
      31              : 
      32              : static char g_tui_bin[512];
      33              : static char g_cli_bin[512];
      34              : static char g_smtp_mock_bin[512];
      35              : static char g_test_home[512];
      36              : static pid_t g_smtp_pid = -1;
      37              : 
      38              : #define RUN_TEST(fn) \
      39              :     do { \
      40              :         fprintf(stdout, "  %s...\n", #fn); \
      41              :         fflush(stdout); \
      42              :         fn(); \
      43              :     } while(0)
      44              : 
      45              : /* ── Config helpers ──────────────────────────────────────────────────── */
      46              : 
      47           90 : static void write_config(void) {
      48              :     char d1[600], d2[620], d3[640], d4[660], path[700];
      49           90 :     snprintf(d1, sizeof(d1), "%s/.config", g_test_home);
      50           90 :     snprintf(d2, sizeof(d2), "%s/.config/email-cli", g_test_home);
      51           90 :     snprintf(d3, sizeof(d3), "%s/.config/email-cli/accounts", g_test_home);
      52           90 :     snprintf(d4, sizeof(d4), "%s/.config/email-cli/accounts/testuser", g_test_home);
      53           90 :     mkdir(g_test_home, 0700);
      54           90 :     mkdir(d1, 0700);
      55           90 :     mkdir(d2, 0700);
      56           90 :     mkdir(d3, 0700);
      57           90 :     mkdir(d4, 0700);
      58           90 :     snprintf(path, sizeof(path), "%s/config.ini", d4);
      59           90 :     FILE *fp = fopen(path, "w");
      60           90 :     if (!fp) return;
      61           90 :     fprintf(fp,
      62              :         "EMAIL_HOST=imaps://localhost:9993\n"
      63              :         "EMAIL_USER=testuser\n"
      64              :         "EMAIL_PASS=testpass\n"
      65              :         "EMAIL_FOLDER=INBOX\n"
      66              :         "SMTP_HOST=smtps://localhost:9025\n"
      67              :         "SMTP_PORT=9025\n"
      68              :         "SMTP_USER=testuser\n"
      69              :         "SMTP_PASS=testpass\n"
      70              :         "SSL_NO_VERIFY=1\n");   /* permit self-signed cert from mock server */
      71           90 :     fclose(fp);
      72           90 :     chmod(path, 0600);
      73              : }
      74              : 
      75              : /* ── Mock SMTP server management ──────────────────────────────────────── */
      76              : 
      77           57 : static int probe_smtp(void) {
      78           57 :     int fd = socket(AF_INET, SOCK_STREAM, 0);
      79           57 :     if (fd < 0) return -1;
      80           57 :     struct sockaddr_in addr = {
      81              :         .sin_family = AF_INET,
      82           57 :         .sin_port   = htons(9025),
      83           57 :         .sin_addr.s_addr = htonl(INADDR_LOOPBACK)
      84              :     };
      85           57 :     int ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
      86           57 :     close(fd);
      87           57 :     return ret;
      88              : }
      89              : 
      90           57 : static void start_smtp_server(void) {
      91           57 :     g_smtp_pid = fork();
      92           63 :     if (g_smtp_pid < 0) return;
      93           63 :     if (g_smtp_pid == 0) {
      94            6 :         int devnull = open("/dev/null", O_WRONLY);
      95            6 :         if (devnull >= 0) { dup2(devnull, 2); close(devnull); }
      96            6 :         execl(g_smtp_mock_bin, "mock_smtp_server", (char *)NULL);
      97            6 :         _exit(127);
      98              :     }
      99           57 :     usleep(600000);
     100              : }
     101              : 
     102           41 : static void restart_smtp(void) {
     103           41 :     if (g_smtp_pid > 0) {
     104           41 :         kill(g_smtp_pid, SIGKILL);
     105           41 :         waitpid(g_smtp_pid, NULL, 0);
     106           41 :         g_smtp_pid = -1;
     107              :     }
     108           41 :     usleep(200000);
     109           41 :     start_smtp_server();
     110           41 :     for (int i = 0; i < 30 && probe_smtp() != 0; i++)
     111            0 :         usleep(100000);
     112           41 : }
     113              : 
     114            1 : static void stop_smtp_server(void) {
     115            1 :     if (g_smtp_pid > 0) {
     116            1 :         kill(g_smtp_pid, SIGKILL);
     117            1 :         waitpid(g_smtp_pid, NULL, 0);
     118            1 :         g_smtp_pid = -1;
     119              :     }
     120            1 : }
     121              : 
     122              : /* ── Runner helper ───────────────────────────────────────────────────── */
     123              : 
     124          126 : static PtySession *tui_open_size(int cols, int rows, const char **extra_args) {
     125              :     const char *args[32];
     126          126 :     int n = 0;
     127          126 :     args[n++] = g_tui_bin;
     128          126 :     if (extra_args)
     129          534 :         for (int i = 0; extra_args[i] && n < 31; i++)
     130          408 :             args[n++] = extra_args[i];
     131          126 :     args[n] = NULL;
     132              : 
     133          126 :     PtySession *s = pty_open(cols, rows);
     134          126 :     if (!s) return NULL;
     135          126 :     if (pty_run(s, args) != 0) { pty_close(s); return NULL; }
     136          114 :     return s;
     137              : }
     138              : 
     139           58 : static PtySession *tui_run(const char **extra_args) {
     140           58 :     return tui_open_size(COLS, ROWS, extra_args);
     141              : }
     142              : 
     143            9 : static PtySession *cli_open_size(int cols, int rows, const char **extra_args) {
     144              :     const char *args[32];
     145            9 :     int n = 0;
     146            9 :     args[n++] = g_cli_bin;
     147            9 :     if (extra_args)
     148           29 :         for (int i = 0; extra_args[i] && n < 31; i++)
     149           20 :             args[n++] = extra_args[i];
     150            9 :     args[n] = NULL;
     151              : 
     152            9 :     PtySession *s = pty_open(cols, rows);
     153            9 :     if (!s) return NULL;
     154            9 :     if (pty_run(s, args) != 0) { pty_close(s); return NULL; }
     155            6 :     return s;
     156              : }
     157              : 
     158            4 : static PtySession *cli_run(const char **extra_args) {
     159            4 :     return cli_open_size(COLS, ROWS, extra_args);
     160              : }
     161              : 
     162              : /* ── Help page tests ─────────────────────────────────────────────────── */
     163              : 
     164           16 : static void test_help_compose(void) {
     165           16 :     const char *a[] = {"compose", "--help", NULL};
     166           16 :     PtySession *s = tui_open_size(120, 50, a);
     167           15 :     ASSERT(s != NULL, "help compose: opens");
     168           15 :     ASSERT_WAIT_FOR(s, "Usage: email-tui", WAIT_MS);
     169           15 :     pty_settle(s, SETTLE_MS);
     170           15 :     ASSERT_SCREEN_CONTAINS(s, "To");
     171           15 :     ASSERT_SCREEN_CONTAINS(s, "Subject");
     172           15 :     pty_close(s);
     173              : }
     174              : 
     175           15 : static void test_help_send(void) {
     176           15 :     const char *a[] = {"send", "--help", NULL};
     177           15 :     PtySession *s = tui_open_size(120, 50, a);
     178           14 :     ASSERT(s != NULL, "help send: opens");
     179           14 :     ASSERT_WAIT_FOR(s, "Usage: email-tui", WAIT_MS);
     180           14 :     pty_settle(s, SETTLE_MS);
     181           14 :     ASSERT_SCREEN_CONTAINS(s, "--to");
     182           14 :     ASSERT_SCREEN_CONTAINS(s, "--subject");
     183           14 :     ASSERT_SCREEN_CONTAINS(s, "--body");
     184           14 :     pty_close(s);
     185              : }
     186              : 
     187           14 : static void test_help_reply(void) {
     188           14 :     const char *a[] = {"reply", "--help", NULL};
     189           14 :     PtySession *s = tui_open_size(120, 50, a);
     190           13 :     ASSERT(s != NULL, "help reply: opens");
     191           13 :     ASSERT_WAIT_FOR(s, "Usage: email-tui", WAIT_MS);
     192           13 :     pty_settle(s, SETTLE_MS);
     193           13 :     ASSERT_SCREEN_CONTAINS(s, "<uid>");
     194           13 :     pty_close(s);
     195              : }
     196              : 
     197              : /* ── Batch send tests ──────────────────────────────────────────────────── */
     198              : 
     199           13 : static void test_send_missing_to(void) {
     200           13 :     write_config();
     201           13 :     const char *a[] = {"--batch", "send", "--subject", "Hi", "--body", "Hello", NULL};
     202           13 :     PtySession *s = tui_open_size(120, 50, a);
     203           12 :     ASSERT(s != NULL, "send missing to: opens");
     204           12 :     ASSERT_WAIT_FOR(s, "required", WAIT_MS);
     205           12 :     pty_close(s);
     206              : }
     207              : 
     208           12 : static void test_send_batch_ok(void) {
     209           12 :     write_config();
     210           12 :     restart_smtp();
     211           12 :     const char *a[] = {"--batch", "send",
     212              :         "--to",      "recipient@example.com",
     213              :         "--subject", "Test Subject",
     214              :         "--body",    "Hello from PTY test",
     215              :         NULL};
     216           12 :     PtySession *s = tui_run(a);
     217           11 :     ASSERT(s != NULL, "batch send: opens");
     218           11 :     ASSERT_WAIT_FOR(s, "Message sent", WAIT_MS);
     219           11 :     pty_close(s);
     220              : }
     221              : 
     222           11 : static void test_send_no_smtp_config(void) {
     223              :     /* Write config without SMTP settings */
     224              :     char d1[600], d2[620], d3[640], d4[660], path[700];
     225           11 :     snprintf(d1, sizeof(d1), "%s/.config", g_test_home);
     226           11 :     snprintf(d2, sizeof(d2), "%s/.config/email-cli", g_test_home);
     227           11 :     snprintf(d3, sizeof(d3), "%s/.config/email-cli/accounts", g_test_home);
     228           11 :     snprintf(d4, sizeof(d4), "%s/.config/email-cli/accounts/testuser", g_test_home);
     229           11 :     mkdir(g_test_home, 0700); mkdir(d1, 0700); mkdir(d2, 0700);
     230           11 :     mkdir(d3, 0700); mkdir(d4, 0700);
     231           11 :     snprintf(path, sizeof(path), "%s/config.ini", d4);
     232           11 :     FILE *fp = fopen(path, "w");
     233           11 :     if (fp) {
     234           11 :         fprintf(fp,
     235              :             "EMAIL_HOST=imaps://localhost:9993\n"
     236              :             "EMAIL_USER=testuser\n"
     237              :             "EMAIL_PASS=testpass\n"
     238              :             "EMAIL_FOLDER=INBOX\n"
     239              :             "SSL_NO_VERIFY=1\n");  /* permit non-TLS mock server in tests */
     240           11 :         fclose(fp);
     241           11 :         chmod(path, 0600);
     242              :     }
     243           11 :     const char *a[] = {"--batch", "send",
     244              :         "--to", "x@x.com", "--subject", "X", "--body", "X", NULL};
     245           11 :     PtySession *s = tui_run(a);
     246           10 :     ASSERT(s != NULL, "no smtp cfg: opens");
     247              :     /* Should still attempt; derives SMTP from IMAP host */
     248              :     /* We just check it exits (doesn't hang) */
     249           10 :     pty_settle(s, SETTLE_MS * 5);
     250           10 :     pty_close(s);
     251              : }
     252              : 
     253              : /* ── Compose TUI tests (editor-based) ───────────────────────────────── */
     254              : 
     255              : /**
     256              :  * Helper: write a shell script that replaces the draft file ($1) with a
     257              :  * complete message and returns.  Sets $EDITOR to that script path.
     258              :  * Returns the script path (caller must unlink when done).
     259              :  */
     260              : static char g_editor_script[256];
     261              : 
     262           24 : static void setup_editor_mock(const char *from, const char *to,
     263              :                               const char *subject, const char *body) {
     264           24 :     snprintf(g_editor_script, sizeof(g_editor_script),
     265           24 :              "/tmp/test_editor_%d.sh", (int)getpid());
     266           24 :     FILE *ef = fopen(g_editor_script, "w");
     267           24 :     if (!ef) return;
     268           24 :     fprintf(ef, "#!/bin/sh\n");
     269           24 :     fprintf(ef,
     270              :             "printf 'From: %s\\nTo: %s\\nSubject: %s\\n\\n%s\\n' > \"$1\"\n",
     271              :             from, to, subject, body);
     272           24 :     fclose(ef);
     273           24 :     chmod(g_editor_script, 0755);
     274           24 :     setenv("EDITOR", g_editor_script, 1);
     275              : }
     276              : 
     277           21 : static void cleanup_editor_mock(void) {
     278           21 :     unlink(g_editor_script);
     279           21 :     g_editor_script[0] = '\0';
     280           21 : }
     281              : 
     282              : /**
     283              :  * compose abort: EDITOR=true → editor exits without changing To: → "Aborted"
     284              :  */
     285           10 : static void test_compose_abort_no_to(void) {
     286           10 :     write_config();
     287           10 :     setenv("EDITOR", "true", 1);  /* no-op editor: exits without saving */
     288           10 :     const char *a[] = {"compose", NULL};
     289           10 :     PtySession *s = tui_open_size(120, 50, a);
     290            9 :     ASSERT(s != NULL, "compose abort no-to: opens");
     291            9 :     ASSERT_WAIT_FOR(s, "Aborted", WAIT_MS);
     292            9 :     pty_close(s);
     293              : }
     294              : 
     295              : /**
     296              :  * compose success: mock editor fills in all headers + body → confirm 'y' → "Message sent"
     297              :  */
     298            9 : static void test_compose_editor_send(void) {
     299            9 :     write_config();
     300            9 :     restart_smtp();
     301            9 :     setup_editor_mock("testuser@example.com", "recipient@example.com",
     302              :                       "PTY test subject", "Hello from editor mock test");
     303            9 :     const char *a[] = {"compose", NULL};
     304            9 :     PtySession *s = tui_run(a);
     305            8 :     ASSERT(s != NULL, "compose editor send: opens");
     306            8 :     ASSERT_WAIT_FOR(s, "Send?", WAIT_MS * 2);
     307            8 :     pty_send_str(s, "y");
     308            8 :     ASSERT_WAIT_FOR(s, "Message sent", WAIT_MS);
     309            8 :     pty_close(s);
     310            8 :     cleanup_editor_mock();
     311              : }
     312              : 
     313              : /**
     314              :  * compose cancel: mock editor fills in all headers + body → confirm 'n' → "Cancelled"
     315              :  */
     316            8 : static void test_compose_confirm_cancel(void) {
     317            8 :     write_config();
     318            8 :     restart_smtp();
     319            8 :     setup_editor_mock("testuser@example.com", "recipient@example.com",
     320              :                       "Cancelled test subject", "Body of cancelled message");
     321            8 :     const char *a[] = {"compose", NULL};
     322            8 :     PtySession *s = tui_run(a);
     323            7 :     ASSERT(s != NULL, "compose confirm cancel: opens");
     324            7 :     ASSERT_WAIT_FOR(s, "Send?", WAIT_MS * 2);
     325            7 :     pty_send_str(s, "n");
     326            7 :     ASSERT_WAIT_FOR(s, "Cancelled", WAIT_MS);
     327            7 :     pty_close(s);
     328            7 :     cleanup_editor_mock();
     329              : }
     330              : 
     331              : /**
     332              :  * \r stripping (US-20): reply quote must not contain carriage-return bytes.
     333              :  *
     334              :  * We set up an editor that captures the draft to a temp file for inspection.
     335              :  * The mock IMAP server delivers messages with CRLF line endings (RFC 2822);
     336              :  * after quote extraction those CRs must be stripped before the draft is
     337              :  * written.  The editor mock simply outputs a fixed reply so the send
     338              :  * completes; we inspect the captured draft file instead of the sent message.
     339              :  */
     340            5 : static void test_reply_no_cr_in_quote(void) {
     341            5 :     write_config();
     342            5 :     restart_smtp();
     343              : 
     344              :     /* Editor mock: capture draft to a known path, then fill in a valid reply */
     345              :     char capture_path[256];
     346            5 :     snprintf(capture_path, sizeof(capture_path),
     347            5 :              "/tmp/draft_capture_%d.txt", (int)getpid());
     348              : 
     349              :     char editor_script[300];
     350            5 :     snprintf(editor_script, sizeof(editor_script),
     351            5 :              "/tmp/test_editor_cr_%d.sh", (int)getpid());
     352            5 :     FILE *ef = fopen(editor_script, "w");
     353            5 :     if (ef) {
     354            5 :         fprintf(ef, "#!/bin/sh\n");
     355              :         /* Copy draft to capture path for inspection */
     356            5 :         fprintf(ef, "cp \"$1\" '%s'\n", capture_path);
     357              :         /* Fill valid To: so compose sends */
     358            5 :         fprintf(ef,
     359              :             "printf 'From: testuser@example.com\\n"
     360              :             "To: recipient@example.com\\n"
     361              :             "Subject: Re: Test\\n\\nReply body\\n' > \"$1\"\n");
     362            5 :         fclose(ef);
     363            5 :         chmod(editor_script, 0755);
     364              :     }
     365            5 :     setenv("EDITOR", editor_script, 1);
     366              : 
     367              :     /* Launch as batch reply UID 1 (mock server has message UID 1) */
     368            5 :     const char *a[] = {"reply", "1", NULL};
     369            5 :     PtySession *s = tui_run(a);
     370            4 :     ASSERT(s != NULL, "no CR in quote: opens");
     371              :     /* Wait until the editor has run and the draft was captured */
     372            4 :     pty_settle(s, WAIT_MS);
     373            4 :     pty_close(s);
     374              : 
     375              :     /* Inspect captured draft: must not contain \r */
     376            4 :     FILE *fp = fopen(capture_path, "r");
     377            4 :     if (fp) {
     378            0 :         int found_cr = 0;
     379              :         int c;
     380            0 :         while ((c = fgetc(fp)) != EOF)
     381            0 :             if (c == '\r') { found_cr = 1; break; }
     382            0 :         fclose(fp);
     383            0 :         ASSERT(!found_cr, "no CR in quoted body: draft must not contain \\r");
     384            0 :         unlink(capture_path);
     385              :     }
     386              :     /* cleanup */
     387            4 :     unlink(editor_script);
     388              : }
     389              : 
     390              : /**
     391              :  * reply abort: batch reply with EDITOR=true → empty To: → "Aborted"
     392              :  * (cmd_reply is invoked; the raw message may not exist → error before editor)
     393              :  */
     394            6 : static void test_reply_missing_uid(void) {
     395            6 :     write_config();
     396            6 :     const char *a[] = {"--batch", "reply", NULL};
     397            6 :     PtySession *s = tui_run(a);
     398            5 :     ASSERT(s != NULL, "reply missing uid: opens");
     399            5 :     ASSERT_WAIT_FOR(s, "requires", WAIT_MS);
     400            5 :     pty_close(s);
     401              : }
     402              : 
     403              : /**
     404              :  * compose sent folder (US-20): after a successful send, "Saving to Sent folder"
     405              :  * appears in output confirming the sent copy is stored locally/remotely.
     406              :  */
     407            7 : static void test_compose_sent_folder(void) {
     408            7 :     write_config();
     409            7 :     restart_smtp();
     410            7 :     setup_editor_mock("testuser@example.com", "recipient@example.com",
     411              :                       "Sent folder test", "Body for sent folder test");
     412            7 :     const char *a[] = {"compose", NULL};
     413            7 :     PtySession *s = tui_run(a);
     414            6 :     ASSERT(s != NULL, "compose sent folder: opens");
     415            6 :     ASSERT_WAIT_FOR(s, "Send?", WAIT_MS * 2);
     416            6 :     pty_send_str(s, "y");
     417            6 :     ASSERT_WAIT_FOR(s, "Saved locally", WAIT_MS);
     418            6 :     pty_close(s);
     419            6 :     cleanup_editor_mock();
     420              : }
     421              : 
     422              : /* ── Config command tests (US-24) ────────────────────────────────────── */
     423              : 
     424              : /**
     425              :  * config show: batch mode prints IMAP settings including user and folder.
     426              :  * Passwords are masked; the account user and folder must be visible.
     427              :  */
     428            4 : static void test_config_show(void) {
     429            4 :     write_config();
     430            4 :     const char *a[] = {"config", "show", NULL};
     431            4 :     PtySession *s = cli_run(a);
     432            3 :     ASSERT(s != NULL, "config show: opens");
     433            3 :     ASSERT_WAIT_FOR(s, "IMAP", WAIT_MS);
     434            3 :     pty_settle(s, SETTLE_MS);
     435            3 :     ASSERT_SCREEN_CONTAINS(s, "testuser");
     436            3 :     ASSERT_SCREEN_CONTAINS(s, "INBOX");
     437            3 :     pty_close(s);
     438              : }
     439              : 
     440              : /**
     441              :  * config --help: must mention imap, smtp, and --account subcommands.
     442              :  */
     443            3 : static void test_config_help(void) {
     444            3 :     const char *a[] = {"config", "--help", NULL};
     445            3 :     PtySession *s = cli_open_size(120, 50, a);
     446            2 :     ASSERT(s != NULL, "config help: opens");
     447            2 :     ASSERT_WAIT_FOR(s, "Usage: email-cli", WAIT_MS);
     448            2 :     pty_settle(s, SETTLE_MS);
     449            2 :     ASSERT_SCREEN_CONTAINS(s, "imap");
     450            2 :     ASSERT_SCREEN_CONTAINS(s, "smtp");
     451            2 :     ASSERT_SCREEN_CONTAINS(s, "account");
     452            2 :     pty_close(s);
     453              : }
     454              : 
     455              : /**
     456              :  * config imap --help: must print usage information and exit.
     457              :  */
     458            2 : static void test_config_imap_help(void) {
     459            2 :     const char *a[] = {"config", "imap", "--help", NULL};
     460            2 :     PtySession *s = cli_open_size(120, 50, a);
     461            1 :     ASSERT(s != NULL, "config imap help: opens");
     462            1 :     ASSERT_WAIT_FOR(s, "Usage: email-cli", WAIT_MS);
     463            1 :     pty_settle(s, SETTLE_MS);
     464            1 :     ASSERT_SCREEN_CONTAINS(s, "imap");
     465            1 :     pty_close(s);
     466              : }
     467              : 
     468              : /* ── Main ────────────────────────────────────────────────────────────── */
     469              : 
     470           16 : int main(int argc, char *argv[]) {
     471           16 :     if (argc < 4) {
     472            0 :         fprintf(stderr, "Usage: %s <email-tui> <mock-smtp-server> <email-cli>\n", argv[0]);
     473            0 :         return 1;
     474              :     }
     475              : 
     476           16 :     snprintf(g_tui_bin,       sizeof(g_tui_bin),       "%s", argv[1]);
     477           16 :     snprintf(g_smtp_mock_bin, sizeof(g_smtp_mock_bin),  "%s", argv[2]);
     478           16 :     snprintf(g_cli_bin,       sizeof(g_cli_bin),        "%s", argv[3]);
     479              : 
     480              :     /* Isolated HOME for tests */
     481           16 :     snprintf(g_test_home, sizeof(g_test_home), "/tmp/email_cli_compose_test_%d", getpid());
     482           16 :     mkdir(g_test_home, 0700);
     483           16 :     setenv("HOME", g_test_home, 1);
     484           16 :     unsetenv("XDG_CONFIG_HOME");
     485           16 :     unsetenv("XDG_CACHE_HOME");
     486           16 :     unsetenv("XDG_DATA_HOME");
     487              : 
     488           16 :     write_config();
     489           16 :     start_smtp_server();
     490           16 :     for (int i = 0; i < 30 && probe_smtp() != 0; i++) usleep(100000);
     491              : 
     492           16 :     printf("\n--- Compose/send: help pages ---\n");
     493           16 :     RUN_TEST(test_help_compose);
     494           15 :     RUN_TEST(test_help_send);
     495           14 :     RUN_TEST(test_help_reply);
     496              : 
     497           13 :     printf("\n--- Compose/send: batch send ---\n");
     498           13 :     RUN_TEST(test_send_missing_to);
     499           12 :     RUN_TEST(test_send_batch_ok);
     500           11 :     RUN_TEST(test_send_no_smtp_config);
     501              : 
     502           10 :     printf("\n--- Compose/send: interactive compose ---\n");
     503           10 :     RUN_TEST(test_compose_abort_no_to);
     504            9 :     RUN_TEST(test_compose_editor_send);
     505            8 :     RUN_TEST(test_compose_confirm_cancel);
     506            7 :     RUN_TEST(test_compose_sent_folder);
     507            6 :     RUN_TEST(test_reply_missing_uid);
     508            5 :     RUN_TEST(test_reply_no_cr_in_quote);
     509              : 
     510            4 :     printf("\n--- Config command (US-24) ---\n");
     511            4 :     RUN_TEST(test_config_show);
     512            3 :     RUN_TEST(test_config_help);
     513            2 :     RUN_TEST(test_config_imap_help);
     514              : 
     515            1 :     stop_smtp_server();
     516              : 
     517            1 :     printf("\n--- PTY Compose Test Results ---\n");
     518            1 :     printf("Tests Run:    %d\n", g_tests_run);
     519            1 :     printf("Tests Passed: %d\n", g_tests_run - g_tests_failed);
     520            1 :     printf("Tests Failed: %d\n", g_tests_failed);
     521              : 
     522            1 :     return g_tests_failed > 0 ? 1 : 0;
     523              : }
        

Generated by: LCOV version 2.0-1