LCOV - code coverage report
Current view: top level - tests/src - test0040.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 95.7 % 211 202
Test Date: 2026-10-20 14:27:43 Functions: 100.0 % 16 16
Branches: 45.0 % 20 9

             Branch data     Line data    Source code
       1                 :             : #include "sute.h"
       2                 :             : 
       3                 :             : #define DRY_RUN_ARGUMENTS "--dry-run --database=0040.db tests/fixtures/diffs/diff1/2"
       4                 :             : 
       5                 :             : struct color_environment_backup {
       6                 :             :         char *no_color;
       7                 :             :         char *term;
       8                 :             :         bool snapshot_is_valid;
       9                 :             : };
      10                 :             : 
      11                 :             : static const char *remembered_value = "remembered";
      12                 :             : static LOGMODES remembered_log_level = REGULAR | UNDECOR | REMEMBER;
      13                 :             : 
      14                 :             : /**
      15                 :             :  * @brief Write one decorated message through the REMEMBER logger path
      16                 :             :  */
      17                 :           4 : static void capture_remembered_message(void)
      18                 :             : {
      19                 :           4 :         slog(remembered_log_level,
      20                 :             :                 "@{bold}@{red}%s %zu@{reset}\n",
      21                 :             :                 remembered_value,
      22                 :             :                 (size_t)7U);
      23                 :           4 : }
      24                 :             : 
      25                 :             : /**
      26                 :             :  * @brief Save environment values that affect automatic color output
      27                 :             :  *
      28                 :             :  * @param backup Zero-initialized storage for duplicated environment values
      29                 :             :  * @return SUCCESS when the values were saved, otherwise FAILURE
      30                 :             :  */
      31                 :           6 : static Return save_color_environment(struct color_environment_backup *backup)
      32                 :             : {
      33                 :             :         /* Status returned by this function through deliver()
      34                 :             :            Default value assumes successful completion */
      35                 :           6 :         Return status = SUCCESS;
      36                 :           6 :         const char *no_color = getenv("NO_COLOR");
      37                 :           6 :         const char *term = getenv("TERM");
      38                 :             : 
      39                 :             :         /* Copy existing values so an unset variable remains distinct from an empty one */
      40         [ -  + ]:           6 :         if(no_color != NULL)
      41                 :             :         {
      42                 :           0 :                 backup->no_color = strdup(no_color);
      43                 :           0 :                 ASSERT(backup->no_color != NULL);
      44                 :             :         }
      45                 :             : 
      46   [ +  -  -  + ]:           6 :         if(SUCCESS == status && term != NULL)
      47                 :             :         {
      48                 :           0 :                 backup->term = strdup(term);
      49                 :           0 :                 ASSERT(backup->term != NULL);
      50                 :             :         }
      51                 :             : 
      52                 :             :         /* An incomplete snapshot must never replace the original environment */
      53                 :           6 :         backup->snapshot_is_valid = SUCCESS == status;
      54                 :             : 
      55                 :           6 :         deliver(status);
      56                 :             : }
      57                 :             : 
      58                 :             : /**
      59                 :             :  * @brief Restore environment values saved before a color output test
      60                 :             :  *
      61                 :             :  * @param backup Saved environment values to restore and release
      62                 :             :  * @return SUCCESS when the environment was restored, otherwise FAILURE
      63                 :             :  */
      64                 :           6 : static Return restore_color_environment(struct color_environment_backup *backup)
      65                 :             : {
      66                 :             :         /* Status returned by this function through deliver()
      67                 :             :            Default value assumes successful completion */
      68                 :           6 :         Return status = SUCCESS;
      69                 :             : 
      70                 :             :         /* Attempt both restorations before checking their results */
      71         [ +  - ]:           6 :         if(backup->snapshot_is_valid == true)
      72                 :             :         {
      73                 :           6 :                 int no_color_result = 0;
      74                 :           6 :                 int term_result = 0;
      75                 :             : 
      76         [ -  + ]:           6 :                 if(backup->no_color != NULL)
      77                 :             :                 {
      78                 :           0 :                         no_color_result = setenv("NO_COLOR",backup->no_color,1);
      79                 :             :                 } else {
      80                 :           6 :                         no_color_result = unsetenv("NO_COLOR");
      81                 :             :                 }
      82                 :             : 
      83         [ -  + ]:           6 :                 if(backup->term != NULL)
      84                 :             :                 {
      85                 :           0 :                         term_result = setenv("TERM",backup->term,1);
      86                 :             :                 } else {
      87                 :           6 :                         term_result = unsetenv("TERM");
      88                 :             :                 }
      89                 :             : 
      90                 :           6 :                 ASSERT(no_color_result == 0);
      91                 :           6 :                 ASSERT(term_result == 0);
      92                 :             :         }
      93                 :             : 
      94                 :             :         /* Release complete or partial snapshots after any restoration result */
      95                 :           6 :         free(backup->term);
      96                 :           6 :         free(backup->no_color);
      97                 :           6 :         memset(backup,0,sizeof(*backup));
      98                 :             : 
      99                 :           6 :         deliver(status);
     100                 :             : }
     101                 :             : 
     102                 :             : /**
     103                 :             :  * @brief Check the most recent message stored in the real REMEMBER table
     104                 :             :  *
     105                 :             :  * @param[in] database Database containing the temporary remember_history table
     106                 :             :  * @param[in] expected_message Exact plain message expected in the last row
     107                 :             :  * @param expected_length Number of bytes in @p expected_message
     108                 :             :  * @return SUCCESS when the latest row contains the expected bytes, otherwise FAILURE
     109                 :             :  */
     110                 :           4 : static Return check_last_remembered_message(
     111                 :             :         sqlite3    *database,
     112                 :             :         const char *expected_message,
     113                 :             :         size_t     expected_length)
     114                 :             : {
     115                 :             :         /* Status returned by this function through deliver()
     116                 :             :            Default value assumes successful completion */
     117                 :           4 :         Return status = SUCCESS;
     118                 :           4 :         sqlite3_stmt *statement = NULL;
     119                 :             : 
     120                 :             :         /* Read the most recently stored message from the real SQLite table */
     121                 :           4 :         ASSERT(SQLITE_OK == sqlite3_prepare_v2(database,
     122                 :             :                 "SELECT message FROM temp.remember_history ORDER BY id DESC LIMIT 1;",
     123                 :             :                 -1,&statement,NULL));
     124                 :           4 :         ASSERT(SQLITE_ROW == sqlite3_step(statement));
     125                 :             : 
     126                 :             :         /* Require the exact byte count and text, including prefixes and the newline */
     127         [ +  - ]:           4 :         if(SUCCESS == status)
     128                 :             :         {
     129                 :           4 :                 const unsigned char *stored_message = sqlite3_column_text(statement,0);
     130                 :           4 :                 ASSERT(stored_message != NULL);
     131                 :           4 :                 ASSERT(sqlite3_column_bytes(statement,0) == (int)expected_length);
     132                 :           4 :                 ASSERT(memcmp(stored_message,expected_message,expected_length) == 0);
     133                 :             :         }
     134                 :             : 
     135                 :             :         /* Release the statement even when a query or content check failed */
     136                 :           4 :         const int finalize_result = sqlite3_finalize(statement);
     137                 :           4 :         ASSERT(finalize_result == SQLITE_OK);
     138                 :             : 
     139                 :           4 :         deliver(status);
     140                 :             : }
     141                 :             : 
     142                 :             : /**
     143                 :             :  * @brief Run a deterministic dry run and compare its complete captured output
     144                 :             :  *
     145                 :             :  * @param arguments Complete dry-run command-line arguments
     146                 :             :  * @param stdout_pattern_file Full stdout template for the selected color mode
     147                 :             :  * @param expected_color_mode Exact mode name to substitute into the diagnostic template
     148                 :             :  * @param colors_expected True when captured stdout must contain terminal color sequences
     149                 :             :  * @return SUCCESS when stdout matches and stderr is empty, otherwise FAILURE
     150                 :             :  */
     151                 :          40 : static Return assert_output(
     152                 :             :         const char *arguments,
     153                 :             :         const char *stdout_pattern_file,
     154                 :             :         const char *expected_color_mode,
     155                 :             :         const bool colors_expected)
     156                 :             : {
     157                 :             :         /* Status returned by this function through deliver()
     158                 :             :            Default value assumes successful completion */
     159                 :          40 :         Return status = SUCCESS;
     160                 :             : 
     161                 :          40 :         m_create(char,stdout_result,MEMORY_STRING);
     162                 :          40 :         m_create(char,stderr_result,MEMORY_STRING);
     163                 :          40 :         m_create(char,stdout_pattern,MEMORY_STRING);
     164                 :             : 
     165                 :             :         /* Run with test diagnostics enabled and collect both output streams */
     166                 :          40 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     167                 :          40 :         ASSERT(SUCCESS == runit(arguments,stdout_result,stderr_result,COMPLETED,STDERR_ALLOW));
     168                 :             : 
     169                 :             :         /* Check the selected mode, complete output, styling, and absence of errors */
     170                 :          40 :         ASSERT(SUCCESS == get_file_content(stdout_pattern_file,stdout_pattern));
     171                 :          40 :         ASSERT(SUCCESS == replace_placeholder(stdout_pattern,"%COLOR_MODE%",expected_color_mode));
     172                 :          40 :         ASSERT(SUCCESS == match_pattern(stdout_result,stdout_pattern,stdout_pattern_file));
     173                 :          40 :         ASSERT((strchr(m_text(stdout_result),'\033') != NULL) == colors_expected);
     174                 :          40 :         ASSERT(stderr_result->length == 0U);
     175                 :             : 
     176                 :             :         /* Release capture buffers and the template after any assertion result */
     177                 :          40 :         call(m_del(stdout_pattern));
     178                 :          40 :         call(m_del(stderr_result));
     179                 :          40 :         call(m_del(stdout_result));
     180                 :             : 
     181                 :          40 :         deliver(status);
     182                 :             : }
     183                 :             : 
     184                 :             : /**
     185                 :             :  * @brief Check the test runner's forced color default without a color argument
     186                 :             :  *
     187                 :             :  * The runner sets TESTITALL_TEST_ENV_COLOR_MODE=always so captured golden
     188                 :             :  * output retains styling. This case checks that test setting; the application's
     189                 :             :  * ordinary automatic default is checked separately in test0040_7()
     190                 :             :  *
     191                 :             :  * @return Return describing success or failure
     192                 :             :  */
     193                 :           2 : static Return test0040_1(void)
     194                 :             : {
     195                 :           2 :         INITTEST;
     196                 :             : 
     197                 :             :         /* Leave --color unspecified so the test-only override determines the mode */
     198                 :           2 :         ASSERT(SUCCESS == assert_output(
     199                 :             :                 DRY_RUN_ARGUMENTS,
     200                 :             :                 "templates/0040_001.txt",
     201                 :             :                 "always",
     202                 :             :                 true));
     203                 :             : 
     204                 :           2 :         RETURN_STATUS;
     205                 :             : }
     206                 :             : 
     207                 :             : /**
     208                 :             :  * @brief Check all long color mode arguments and their configuration equivalents
     209                 :             :  *
     210                 :             :  * Each --color value must appear in the parsed configuration and produce the
     211                 :             :  * expected output. Always retains styling; never and auto omit it from the
     212                 :             :  * redirected stream. Repeating the option must apply its last value
     213                 :             :  *
     214                 :             :  * @return Return describing success or failure
     215                 :             :  */
     216                 :           2 : static Return test0040_2(void)
     217                 :             : {
     218                 :           2 :         INITTEST;
     219                 :             : 
     220                 :             :         /* Forced styling must survive output redirection */
     221                 :           2 :         ASSERT(SUCCESS == assert_output(
     222                 :             :                 "--color=always " DRY_RUN_ARGUMENTS,
     223                 :             :                 "templates/0040_001.txt",
     224                 :             :                 "always",
     225                 :             :                 true));
     226                 :             : 
     227                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0040_001.conf","0040_001.conf"));
     228                 :           2 :         ASSERT(SUCCESS == assert_output(
     229                 :             :                 "--config=0040_001.conf",
     230                 :             :                 "templates/0040_001.txt",
     231                 :             :                 "always",
     232                 :             :                 true));
     233                 :           2 :         call(delete_path_if_present("0040_001.conf"));
     234                 :             : 
     235                 :             :         /* Explicitly disabling styling must produce plain text */
     236                 :           2 :         ASSERT(SUCCESS == assert_output(
     237                 :             :                 "--color=never " DRY_RUN_ARGUMENTS,
     238                 :             :                 "templates/0040_002.txt",
     239                 :             :                 "never",
     240                 :             :                 false));
     241                 :             : 
     242                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0040_002.conf","0040_002.conf"));
     243                 :           2 :         ASSERT(SUCCESS == assert_output(
     244                 :             :                 "--config=0040_002.conf",
     245                 :             :                 "templates/0040_002.txt",
     246                 :             :                 "never",
     247                 :             :                 false));
     248                 :           2 :         call(delete_path_if_present("0040_002.conf"));
     249                 :             : 
     250                 :             :         /* Auto must select its own mode while keeping redirected output plain */
     251                 :           2 :         ASSERT(SUCCESS == assert_output(
     252                 :             :                 "--color=auto " DRY_RUN_ARGUMENTS,
     253                 :             :                 "templates/0040_002.txt",
     254                 :             :                 "auto",
     255                 :             :                 false));
     256                 :             : 
     257                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0040_010.conf","0040_010.conf"));
     258                 :           2 :         ASSERT(SUCCESS == assert_output(
     259                 :             :                 "--config=0040_010.conf",
     260                 :             :                 "templates/0040_002.txt",
     261                 :             :                 "auto",
     262                 :             :                 false));
     263                 :           2 :         call(delete_path_if_present("0040_010.conf"));
     264                 :             : 
     265                 :             :         /* The last value replaces the earlier choice in either source */
     266                 :           2 :         ASSERT(SUCCESS == assert_output(
     267                 :             :                 "--color=always --color=never " DRY_RUN_ARGUMENTS,
     268                 :             :                 "templates/0040_002.txt",
     269                 :             :                 "never",
     270                 :             :                 false));
     271                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0040_002_1.conf","0040_002_1.conf"));
     272                 :           2 :         ASSERT(SUCCESS == assert_output(
     273                 :             :                 "--config=0040_002_1.conf",
     274                 :             :                 "templates/0040_002.txt",
     275                 :             :                 "never",
     276                 :             :                 false));
     277                 :           2 :         call(delete_path_if_present("0040_002_1.conf"));
     278                 :             : 
     279                 :           2 :         RETURN_STATUS;
     280                 :             : }
     281                 :             : 
     282                 :             : /**
     283                 :             :  * @brief Check all short color mode arguments with captured output
     284                 :             :  *
     285                 :             :  * Each -S value must select the same mode and output as its --color equivalent
     286                 :             :  *
     287                 :             :  * @return Return describing success or failure
     288                 :             :  */
     289                 :           2 : static Return test0040_3(void)
     290                 :             : {
     291                 :           2 :         INITTEST;
     292                 :             : 
     293                 :             :         /* Forced styling must survive output redirection */
     294                 :           2 :         ASSERT(SUCCESS == assert_output(
     295                 :             :                 "-S always " DRY_RUN_ARGUMENTS,
     296                 :             :                 "templates/0040_001.txt",
     297                 :             :                 "always",
     298                 :             :                 true));
     299                 :             : 
     300                 :             :         /* Explicitly disabling styling must produce plain text */
     301                 :           2 :         ASSERT(SUCCESS == assert_output(
     302                 :             :                 "-S never " DRY_RUN_ARGUMENTS,
     303                 :             :                 "templates/0040_002.txt",
     304                 :             :                 "never",
     305                 :             :                 false));
     306                 :             : 
     307                 :             :         /* Auto must select its own mode while keeping redirected output plain */
     308                 :           2 :         ASSERT(SUCCESS == assert_output(
     309                 :             :                 "-S auto " DRY_RUN_ARGUMENTS,
     310                 :             :                 "templates/0040_002.txt",
     311                 :             :                 "auto",
     312                 :             :                 false));
     313                 :             : 
     314                 :           2 :         RETURN_STATUS;
     315                 :             : }
     316                 :             : 
     317                 :             : /**
     318                 :             :  * @brief Check that forced color output is preserved with NO_COLOR set
     319                 :             :  *
     320                 :             :  * Captured auto output would be plain even without NO_COLOR because stdout
     321                 :             :  * is redirected. The always run must retain styling even when NO_COLOR is
     322                 :             :  * present with an empty value
     323                 :             :  *
     324                 :             :  * @return Return describing success or failure
     325                 :             :  */
     326                 :           2 : static Return test0040_4(void)
     327                 :             : {
     328                 :           2 :         INITTEST;
     329                 :           2 :         struct color_environment_backup environment_backup = {0};
     330                 :             : 
     331                 :             :         /* Save the environment and set NO_COLOR to an explicitly empty value */
     332                 :           2 :         ASSERT(SUCCESS == save_color_environment(&environment_backup));
     333                 :           2 :         ASSERT(0 == setenv("NO_COLOR","",1));
     334                 :           2 :         ASSERT(0 == setenv("TERM","xterm",1));
     335                 :             : 
     336                 :             :         /* Redirected auto output remains plain under this environment setting */
     337                 :           2 :         ASSERT(SUCCESS == assert_output(
     338                 :             :                 "--color=auto " DRY_RUN_ARGUMENTS,
     339                 :             :                 "templates/0040_002.txt",
     340                 :             :                 "auto",
     341                 :             :                 false));
     342                 :             : 
     343                 :             :         /* Always must preserve styling despite the presence of NO_COLOR */
     344                 :           2 :         ASSERT(SUCCESS == assert_output(
     345                 :             :                 "--color=always " DRY_RUN_ARGUMENTS,
     346                 :             :                 "templates/0040_001.txt",
     347                 :             :                 "always",
     348                 :             :                 true));
     349                 :             : 
     350                 :             :         /* Restore the original environment even when an assertion failed */
     351                 :           2 :         call(restore_color_environment(&environment_backup));
     352                 :             : 
     353                 :           2 :         RETURN_STATUS;
     354                 :             : }
     355                 :             : 
     356                 :             : /**
     357                 :             :  * @brief Check that forced color output is preserved with TERM=dumb
     358                 :             :  *
     359                 :             :  * Captured auto output would be plain even without TERM=dumb because stdout
     360                 :             :  * is redirected. The always run must retain styling even when TERM identifies
     361                 :             :  * a dumb terminal
     362                 :             :  *
     363                 :             :  * @return Return describing success or failure
     364                 :             :  */
     365                 :           2 : static Return test0040_5(void)
     366                 :             : {
     367                 :           2 :         INITTEST;
     368                 :           2 :         struct color_environment_backup environment_backup = {0};
     369                 :             : 
     370                 :             :         /* Save the environment and isolate TERM=dumb from NO_COLOR */
     371                 :           2 :         ASSERT(SUCCESS == save_color_environment(&environment_backup));
     372                 :           2 :         ASSERT(0 == unsetenv("NO_COLOR"));
     373                 :           2 :         ASSERT(0 == setenv("TERM","dumb",1));
     374                 :             : 
     375                 :             :         /* Redirected auto output remains plain under this environment setting */
     376                 :           2 :         ASSERT(SUCCESS == assert_output(
     377                 :             :                 "--color=auto " DRY_RUN_ARGUMENTS,
     378                 :             :                 "templates/0040_002.txt",
     379                 :             :                 "auto",
     380                 :             :                 false));
     381                 :             : 
     382                 :             :         /* Always must preserve styling despite TERM=dumb */
     383                 :           2 :         ASSERT(SUCCESS == assert_output(
     384                 :             :                 "--color=always " DRY_RUN_ARGUMENTS,
     385                 :             :                 "templates/0040_001.txt",
     386                 :             :                 "always",
     387                 :             :                 true));
     388                 :             : 
     389                 :             :         /* Restore the original environment even when an assertion failed */
     390                 :           2 :         call(restore_color_environment(&environment_backup));
     391                 :             : 
     392                 :           2 :         RETURN_STATUS;
     393                 :             : }
     394                 :             : 
     395                 :             : /**
     396                 :             :  * @brief Check immediate message styling and plain-text storage in SQLite
     397                 :             :  *
     398                 :             :  * Messages logged with REMEMBER are displayed immediately and stored for later
     399                 :             :  * reporting. In always mode, markup adds terminal styling only to the displayed
     400                 :             :  * copy; the stored row contains plain text. Markup supplied through a formatted
     401                 :             :  * argument is also expanded, while raw terminal control bytes become visible
     402                 :             :  * hexadecimal escapes. In never mode, the displayed message and stored row
     403                 :             :  * must both contain the same plain text
     404                 :             :  *
     405                 :             :  * @return Return describing success or failure
     406                 :             :  */
     407                 :           2 : static Return test0040_6(void)
     408                 :             : {
     409                 :           2 :         INITTEST;
     410                 :             : 
     411                 :             :         /* Preserve the logger settings used by the surrounding test suite */
     412                 :           2 :         const RATIONAL_COLOR_MODE initial_color_mode = atomic_load_explicit(&rational_color_mode,memory_order_relaxed);
     413                 :           2 :         const LOGMODES initial_logger_mode = atomic_load_explicit(&rational_logger_mode,memory_order_relaxed);
     414                 :             : 
     415                 :             :         /* Markup in the formatted argument must expand, while raw ANSI bytes must
     416                 :             :            remain visible as escaped text in both the output and the stored message */
     417                 :             :         static const char styled_value[] =
     418                 :             :                 "always @{green}dynamic@{reset} " BOLD "inside" RESET "\033[2Jblocked";
     419                 :             :         static const char expected_styled_output[] = "TESTING:" BOLD RED "always "
     420                 :             :                 GREEN "dynamic" RESET " \\x1B[1minside\\x1B[0m\\x1B[2Jblocked 7"
     421                 :             :                 RESET "\n";
     422                 :             :         static const char expected_stored_styled_message[] =
     423                 :             :                 "TESTING:always dynamic \\x1B[1minside\\x1B[0m\\x1B[2Jblocked 7\n";
     424                 :             :         static const char expected_plain_output[] = "never 7\n";
     425                 :             :         static const char create_table_sql[] =
     426                 :             :                 "CREATE TEMP TABLE remember_history ("
     427                 :             :                 "id INTEGER PRIMARY KEY AUTOINCREMENT,"
     428                 :             :                 "message TEXT NOT NULL"
     429                 :             :                 ");";
     430                 :           2 :         Config *initial_config = config;
     431                 :           2 :         Config remember_config = {0};
     432                 :             : 
     433                 :           2 :         m_create(char,captured_stdout,MEMORY_STRING);
     434                 :           2 :         m_create(char,captured_stderr,MEMORY_STRING);
     435                 :             : 
     436                 :             :         /* Give the real REMEMBER callback an isolated in-memory SQLite table */
     437                 :           2 :         ASSERT(SQLITE_OK == sqlite3_open(":memory:",&remember_config.db));
     438                 :           2 :         config = &remember_config;
     439                 :           2 :         ASSERT(SQLITE_OK == sqlite3_exec(remember_config.db,create_table_sql,NULL,NULL,NULL));
     440                 :             : 
     441                 :             :         /* Capture a styled message with a TESTING prefix */
     442                 :           2 :         rational_logger_mode = TESTING;
     443                 :           2 :         rational_color_mode = COLOR_MODE_ALWAYS;
     444                 :           2 :         remembered_log_level = TESTING | REMEMBER;
     445                 :           2 :         remembered_value = styled_value;
     446                 :           2 :         ASSERT(SUCCESS == function_capture(
     447                 :             :                 capture_remembered_message,
     448                 :             :                 captured_stdout,
     449                 :             :                 captured_stderr));
     450                 :             : 
     451                 :             :         /* Check expanded markup and escaped raw controls in the displayed line */
     452                 :           2 :         ASSERT(0 == strcmp(m_text(captured_stdout),expected_styled_output));
     453                 :           2 :         ASSERT(captured_stderr->length == 0U);
     454                 :             : 
     455                 :             :         /* SQLite must receive the same text and prefix without terminal styling */
     456                 :           2 :         ASSERT(SUCCESS == check_last_remembered_message(
     457                 :             :                 remember_config.db,
     458                 :             :                 expected_stored_styled_message,
     459                 :             :                 sizeof(expected_stored_styled_message) - 1U));
     460                 :             : 
     461                 :             :         /* Disable styling and capture an ordinary message without a log prefix */
     462                 :           2 :         rational_logger_mode = REGULAR;
     463                 :           2 :         rational_color_mode = COLOR_MODE_NEVER;
     464                 :           2 :         remembered_log_level = REGULAR | UNDECOR | REMEMBER;
     465                 :           2 :         remembered_value = "never";
     466                 :           2 :         ASSERT(SUCCESS == function_capture(
     467                 :             :                 capture_remembered_message,
     468                 :             :                 captured_stdout,
     469                 :             :                 captured_stderr));
     470                 :             : 
     471                 :             :         /* Both the immediate output and stored row must contain plain text */
     472                 :           2 :         ASSERT(0 == strcmp(m_text(captured_stdout),expected_plain_output));
     473                 :           2 :         ASSERT(NULL == strchr(m_text(captured_stdout),'\033'));
     474                 :           2 :         ASSERT(captured_stderr->length == 0U);
     475                 :           2 :         ASSERT(SUCCESS == check_last_remembered_message(
     476                 :             :                 remember_config.db,
     477                 :             :                 expected_plain_output,
     478                 :             :                 sizeof(expected_plain_output) - 1U));
     479                 :             : 
     480                 :             :         /* Restore shared state and close the isolated database after any result */
     481                 :           2 :         rational_logger_mode = initial_logger_mode;
     482                 :           2 :         rational_color_mode = initial_color_mode;
     483                 :           2 :         remembered_log_level = REGULAR | UNDECOR | REMEMBER;
     484                 :           2 :         remembered_value = "remembered";
     485                 :             : 
     486                 :           2 :         config = initial_config;
     487                 :           2 :         const int close_result = sqlite3_close(remember_config.db);
     488                 :           2 :         ASSERT(close_result == SQLITE_OK);
     489                 :             : 
     490                 :             :         /* Release both captured output streams */
     491                 :           2 :         call(m_del(captured_stdout));
     492                 :           2 :         call(m_del(captured_stderr));
     493                 :             : 
     494                 :           2 :         RETURN_STATUS;
     495                 :             : }
     496                 :             : 
     497                 :             : /**
     498                 :             :  * @brief Check default automatic color output without the test-only override
     499                 :             :  *
     500                 :             :  * Removing TESTITALL_TEST_ENV_COLOR_MODE exposes the application's normal
     501                 :             :  * auto default. NO_COLOR is absent and TERM is xterm, so the captured output
     502                 :             :  * must be plain because it is redirected to a file. The runner mode and all
     503                 :             :  * changed environment values are restored after the check
     504                 :             :  *
     505                 :             :  * @return Return describing success or failure
     506                 :             :  */
     507                 :           2 : static Return test0040_7(void)
     508                 :             : {
     509                 :           2 :         INITTEST;
     510                 :           2 :         const enum run_mode initial_run_mode = testitall_runit_mode;
     511                 :           2 :         struct color_environment_backup environment_backup = {0};
     512                 :           2 :         const char *test_color_override = getenv("TESTITALL_TEST_ENV_COLOR_MODE");
     513                 :           2 :         const bool test_color_override_was_set = test_color_override != NULL;
     514                 :           2 :         char *saved_test_color_override = NULL;
     515                 :             : 
     516                 :             :         /* Copy the override before unsetenv invalidates its environment storage */
     517         [ +  - ]:           2 :         if(test_color_override_was_set == true)
     518                 :             :         {
     519                 :           2 :                 saved_test_color_override = strdup(test_color_override);
     520                 :           2 :                 ASSERT(saved_test_color_override != NULL);
     521                 :             :         }
     522                 :             : 
     523                 :             :         /* Remove forced styling and environment settings that could disable auto */
     524                 :           2 :         ASSERT(SUCCESS == save_color_environment(&environment_backup));
     525                 :           2 :         ASSERT(0 == unsetenv("NO_COLOR"));
     526                 :           2 :         ASSERT(0 == setenv("TERM","xterm",1));
     527                 :           2 :         ASSERT(0 == unsetenv("TESTITALL_TEST_ENV_COLOR_MODE"));
     528                 :             : 
     529                 :             :         /* Use the standalone executable to check its default color behavior.
     530                 :             :            With stdout redirected to a file, auto must produce plain text */
     531                 :           2 :         testitall_runit_mode = EXTERNAL_CALL;
     532                 :           2 :         ASSERT(SUCCESS == assert_output(
     533                 :             :                 DRY_RUN_ARGUMENTS,
     534                 :             :                 "templates/0040_002.txt",
     535                 :             :                 "auto",
     536                 :             :                 false));
     537                 :             : 
     538                 :             :         /* Restore shared test state even when an assertion failed */
     539                 :           2 :         testitall_runit_mode = initial_run_mode;
     540                 :             : 
     541         [ +  - ]:           2 :         if(saved_test_color_override != NULL)
     542                 :             :         {
     543                 :           2 :                 const int restore_result = setenv("TESTITALL_TEST_ENV_COLOR_MODE",saved_test_color_override,1);
     544                 :           2 :                 ASSERT(restore_result == 0);
     545         [ #  # ]:           0 :         } else if(test_color_override_was_set == false){
     546                 :           0 :                 const int restore_result = unsetenv("TESTITALL_TEST_ENV_COLOR_MODE");
     547                 :           0 :                 ASSERT(restore_result == 0);
     548                 :             :         }
     549                 :             : 
     550                 :             :         /* Release the override copy and restore the remaining environment values */
     551                 :           2 :         free(saved_test_color_override);
     552                 :           2 :         call(restore_color_environment(&environment_backup));
     553                 :             : 
     554                 :           2 :         RETURN_STATUS;
     555                 :             : }
     556                 :             : 
     557                 :             : /**
     558                 :             :  * @brief Check that CLI and configuration color options reject unsupported and empty values
     559                 :             :  *
     560                 :             :  * @return Return describing success or failure
     561                 :             :  */
     562                 :           2 : static Return test0040_8(void)
     563                 :             : {
     564                 :           2 :         INITTEST;
     565                 :             : 
     566                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     567                 :             : 
     568                 :           2 :         const struct argument_test_case cases[] = {
     569                 :             :                 /* Both option spellings must reject an unsupported mode name */
     570                 :             :                 {"--color=invalid-value",FAILURE,"templates/0040_003.txt","templates/0040_004.txt"},
     571                 :             :                 {"-S invalid-value",FAILURE,"templates/0040_003.txt","templates/0040_004.txt"},
     572                 :             : 
     573                 :             :                 /* An explicitly empty value must also be rejected by both spellings */
     574                 :             :                 {"--color=",FAILURE,"templates/0040_003.txt","templates/0040_005.txt"},
     575                 :             :                 {"-S ''",FAILURE,"templates/0040_003.txt","templates/0040_005.txt"},
     576                 :             :                 {0}
     577                 :             :         };
     578                 :             : 
     579                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     580                 :             : 
     581                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0040_004.conf","0040_004.conf"));
     582                 :           2 :         ASSERT(SUCCESS == copy_path("tests/templates/0040_005.conf","0040_005.conf"));
     583                 :           2 :         const struct argument_test_case file_cases[] = {
     584                 :             :                 {"--config=0040_004.conf",FAILURE,"templates/0003_013.txt","templates/0040_004_1.txt"},
     585                 :             :                 {"--config=0040_005.conf",FAILURE,"templates/0003_013.txt","templates/0040_005_1.txt"},
     586                 :             :                 {0}
     587                 :             :         };
     588                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(file_cases,NULL,NULL));
     589                 :           2 :         call(delete_path_if_present("0040_004.conf"));
     590                 :           2 :         call(delete_path_if_present("0040_005.conf"));
     591                 :             : 
     592                 :           2 :         RETURN_STATUS;
     593                 :             : }
     594                 :             : 
     595                 :             : /**
     596                 :             :  * @brief Check that long and short color options require a value
     597                 :             :  *
     598                 :             :  * @return Return describing success or failure
     599                 :             :  */
     600                 :           2 : static Return test0040_9(void)
     601                 :             : {
     602                 :           2 :         INITTEST;
     603                 :             : 
     604                 :           2 :         ASSERT(SUCCESS == set_environment_variable("TESTING","true"));
     605                 :             : 
     606                 :           2 :         const struct argument_test_case cases[] = {
     607                 :             :                 /* Check the parser diagnostic for a long option with no following value */
     608                 :             :                 {"--color",FAILURE,"templates/0040_003.txt","templates/0040_006.txt"},
     609                 :             :                 /* Check the corresponding diagnostic for the short option */
     610                 :             :                 {"-S",FAILURE,"templates/0040_003.txt","templates/0040_007.txt"},
     611                 :             :                 {0}
     612                 :             :         };
     613                 :             : 
     614                 :           2 :         ASSERT(SUCCESS == run_and_match_templates(cases,NULL,NULL));
     615                 :             : 
     616                 :           2 :         RETURN_STATUS;
     617                 :             : }
     618                 :             : 
     619                 :             : /**
     620                 :             :  * @brief Check that verbose diagnostics report each selected color mode
     621                 :             :  *
     622                 :             :  * @return Return describing success or failure
     623                 :             :  */
     624                 :           2 : static Return test0040_10(void)
     625                 :             : {
     626                 :           2 :         INITTEST;
     627                 :             : 
     628                 :             :         /* Verbose diagnostics must name always and retain styled messages */
     629                 :           2 :         ASSERT(SUCCESS == assert_output(
     630                 :             :                 "--verbose --color=always " DRY_RUN_ARGUMENTS,
     631                 :             :                 "templates/0040_008.txt",
     632                 :             :                 "always",
     633                 :             :                 true));
     634                 :             : 
     635                 :             :         /* Verbose diagnostics must name never and contain no terminal styling */
     636                 :           2 :         ASSERT(SUCCESS == assert_output(
     637                 :             :                 "--verbose --color=never " DRY_RUN_ARGUMENTS,
     638                 :             :                 "templates/0040_009.txt",
     639                 :             :                 "never",
     640                 :             :                 false));
     641                 :             : 
     642                 :             :         /* Auto must be reported distinctly while redirected output stays plain */
     643                 :           2 :         ASSERT(SUCCESS == assert_output(
     644                 :             :                 "--verbose --color=auto " DRY_RUN_ARGUMENTS,
     645                 :             :                 "templates/0040_009.txt",
     646                 :             :                 "auto",
     647                 :             :                 false));
     648                 :             : 
     649                 :           2 :         RETURN_STATUS;
     650                 :             : }
     651                 :             : 
     652                 :             : #undef DRY_RUN_ARGUMENTS
     653                 :             : 
     654                 :             : /**
     655                 :             :  * @brief Check color output modes and plain-text storage of remembered messages
     656                 :             :  *
     657                 :             :  * Successful command-line cases scan a small directory fixture in dry-run mode
     658                 :             :  * and compare the captured output with full templates. They check --color and
     659                 :             :  * -S, the test runner's forced color setting, the application's automatic
     660                 :             :  * default, forced styling with NO_COLOR or TERM=dumb, and verbose diagnostics.
     661                 :             :  * Invalid, empty, and missing option values must produce the expected errors.
     662                 :             :  *
     663                 :             :  * A separate logger case stores messages in a real temporary SQLite table.
     664                 :             :  * Displayed messages may contain styling, while stored messages must remain
     665                 :             :  * plain text with raw terminal control bytes escaped
     666                 :             :  *
     667                 :             :  * @return SUCCESS when all color output and message-storage scenarios pass
     668                 :             :  */
     669                 :           2 : Return test0040(void)
     670                 :             : {
     671                 :           2 :         INITTEST;
     672                 :             : 
     673                 :             :         /* Check the runner default, both option spellings, and forced styling */
     674                 :           2 :         TEST(test0040_1,"Test runs preserve colored dry-run output by default");
     675                 :           2 :         TEST(test0040_2,"Long color options control captured dry-run output");
     676                 :           2 :         TEST(test0040_3,"Short color options control captured dry-run output");
     677                 :           2 :         TEST(test0040_4,"Forced color output is preserved with NO_COLOR set");
     678                 :           2 :         TEST(test0040_5,"Forced color output is preserved with TERM=dumb");
     679                 :             : 
     680                 :             :         /* Compare immediate message formatting with the bytes stored in SQLite */
     681                 :           2 :         TEST(test0040_6,"REMEMBER stores plain text while immediate output uses color markup");
     682                 :             : 
     683                 :             :         /* Check the application default without the runner override */
     684                 :           2 :         TEST(test0040_7,"Default auto mode omits color from captured output without the test override");
     685                 :             : 
     686                 :             :         /* Verify rejected values and the mode reported in verbose diagnostics */
     687                 :           2 :         TEST(test0040_8,"Color options reject unsupported and empty values");
     688                 :           2 :         TEST(test0040_9,"Color options reject missing values");
     689                 :           2 :         TEST(test0040_10,"Verbose diagnostics report the selected color mode");
     690                 :             : 
     691                 :           2 :         RETURN_STATUS;
     692                 :             : }
        

Generated by: LCOV version 2.0-1