LCOV - code coverage report
Current view: top level - libs/rational/tests/src - test_librational_0004.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 99.5 % 379 377
Test Date: 2026-10-20 14:27:43 Functions: 100.0 % 38 38
Branches: 62.5 % 8 5

             Branch data     Line data    Source code
       1                 :             : #include "test_librational_all.h"
       2                 :             : #include <errno.h>
       3                 :             : 
       4                 :             : static const char expected_report_stderr_pattern[] =
       5                 :             :         "\\A"
       6                 :             :         "ERROR: [^\\n]*src/test_librational_0004\\.c:report_test:\\d+ Memory reallocation failed with bytes 10 Errno: [^\\n]+ \\(errno: [0-9]+\\)\n"
       7                 :             :         "ERROR: [^\\n]*src/test_librational_0004\\.c:report_test:\\d+ Buffer overflow at position 42 with value overflow Errno: [^\\n]+ \\(errno: [0-9]+\\)\\Z";
       8                 :             : 
       9                 :             : /* Regex fragments for slog() output fields that change between source layouts and test runs */
      10                 :             : #define SLOG_TIME_PATTERN "[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}:[0-9]{3} "
      11                 :             : #define SLOG_SOURCE_PATTERN "[^\\n]*src/test_librational_0004\\.c:[0-9]+:slog_test:"
      12                 :             : #define SLOG_DECORATED_PATTERN SLOG_TIME_PATTERN SLOG_SOURCE_PATTERN
      13                 :             : 
      14                 :             : static const char expected_slog_stdout_pattern[] =
      15                 :             :         "\\A"
      16                 :             :         "All available combinations:\n"
      17                 :             :         "REGULAR\n"
      18                 :             :         "VERBOSE\n"
      19                 :             :         "TESTING\n"
      20                 :             :         "SILENT\n"
      21                 :             :         "REGULAR\\|VERBOSE\n"
      22                 :             :         "REGULAR\\|TESTING\n"
      23                 :             :         "VERBOSE\\|TESTING\n"
      24                 :             :         "REGULAR\\|VERBOSE\\|TESTING\n"
      25                 :             :         "ERROR\n"
      26                 :             :         "UNDECOR\n"
      27                 :             :         "EVERY\\|UNDECOR\n"
      28                 :             :         "ERROR\\|UNDECOR\n"
      29                 :             :         "VISIBLE_IN_SILENT\n"
      30                 :             :         "Mode: REGULAR\n"
      31                 :             :         "1\\.  Must print:true\n"
      32                 :             :         "2\\. Won't print:\n"
      33                 :             :         "3\\. Won't print:\n"
      34                 :             :         "4\\.  Must print:ERROR: true\n"
      35                 :             :         "Mode: VERBOSE\n"
      36                 :             :         "5\\. Won't print:\n"
      37                 :             :         "6\\.  Must print:" SLOG_DECORATED_PATTERN "true\n"
      38                 :             :         "7\\. Won't print:\n"
      39                 :             :         "8\\.  Must print:" SLOG_DECORATED_PATTERN "ERROR:true\n"
      40                 :             :         "Mode: TESTING\n"
      41                 :             :         "9\\.  Won't print:\n"
      42                 :             :         "10\\. Won't print:\n"
      43                 :             :         "11\\.  Must print:TESTING:true\n"
      44                 :             :         "12\\.  Must print:ERROR:true\n"
      45                 :             :         "Mode: SILENT\n"
      46                 :             :         "13\\. Won't print:\n"
      47                 :             :         "14\\. Won't print:\n"
      48                 :             :         "15\\. Won't print:\n"
      49                 :             :         "16\\. Won't print:\n"
      50                 :             :         "Mode: REGULAR \\| VERBOSE\n"
      51                 :             :         "17\\.  Must print:true\n"
      52                 :             :         "18\\.  Must print:" SLOG_DECORATED_PATTERN "true\n"
      53                 :             :         "19\\. Won't print:\n"
      54                 :             :         "20\\.  Must print:" SLOG_DECORATED_PATTERN "ERROR: true\n"
      55                 :             :         "Mode: REGULAR \\| TESTING\n"
      56                 :             :         "21\\.  Must print:true\n"
      57                 :             :         "22\\. Won't print:\n"
      58                 :             :         "23\\.  Must print:TESTING:true\n"
      59                 :             :         "24\\.  Must print:ERROR: true\n"
      60                 :             :         "Mode: VERBOSE \\| TESTING\n"
      61                 :             :         "25\\. Won't print:\n"
      62                 :             :         "26\\.  Must print:" SLOG_DECORATED_PATTERN "true\n"
      63                 :             :         "27\\.  Must print:TESTING:true\n"
      64                 :             :         "28\\.  Must print:" SLOG_DECORATED_PATTERN "ERROR:true\n"
      65                 :             :         "Mode: REGULAR \\| VERBOSE \\| TESTING\n"
      66                 :             :         "29\\. Must print:true\n"
      67                 :             :         "30\\. Must print:" SLOG_DECORATED_PATTERN "true\n"
      68                 :             :         "31\\. Must print:TESTING:true\n"
      69                 :             :         "32\\. Must print:" SLOG_DECORATED_PATTERN "ERROR: true\n"
      70                 :             :         "Mode: ERROR\n"
      71                 :             :         "33\\. Won't print:\n"
      72                 :             :         "34\\. Won't print:\n"
      73                 :             :         "35\\. Won't print:\n"
      74                 :             :         "36\\.  Must print:ERROR: true\n"
      75                 :             :         "Mode: REGULAR \\| VERBOSE \\| TESTING \\| ERROR\n"
      76                 :             :         "37\\. Must print no prefixes:\\|true\\|\n"
      77                 :             :         "38\\. Must print no ERROR prefix:\\|true\\|\n"
      78                 :             :         "Mode: VERBOSE\n"
      79                 :             :         "39\\. Must print no time/file/line/func:\\|true\\|\n"
      80                 :             :         "Mode: TESTING\n"
      81                 :             :         "40\\. Must print no TESTING prefix:\\|true\\|\n"
      82                 :             :         "Mode: REGULAR\n"
      83                 :             :         "41\\. Must not print \\(VERBOSE not enabled\\):\\|\\|\n"
      84                 :             :         "Mode: SILENT\n"
      85                 :             :         "42\\. Must print in SILENT without prefixes:\\|true\\|\n"
      86                 :             :         "43\\. Must print no ERROR prefix in SILENT:\\|true\\|\\Z";
      87                 :             : 
      88                 :             : static const char expected_serp_stderr_pattern[] =
      89                 :             :         "\\A"
      90                 :             :         "ERROR: Failed to open file \\[File: [^\\n]*src/test_librational_0004\\.c, Function: serp_case\\] Errno: \\(2\\) No such file or directory\\Z";
      91                 :             : 
      92                 :             : #ifndef EVIL_EMPIRE_OS
      93                 :             : static const char expected_write_fallback_stderr_pattern[] =
      94                 :             :         "\\AERROR: Failed to write error message\n\\Z";
      95                 :             : 
      96                 :             : static const char expected_logger_time_failure_stdout_pattern[] =
      97                 :             :         "\\A"
      98                 :             :         " [^\\n]*src/test_librational_0004\\.c:[0-9]+:logger_time_failure_cases:gettimeofday failure\n"
      99                 :             :         " [^\\n]*src/test_librational_0004\\.c:[0-9]+:logger_time_failure_cases:localtime_r failure\n"
     100                 :             :         " [^\\n]*src/test_librational_0004\\.c:[0-9]+:logger_time_failure_cases:snprintf failure\\Z";
     101                 :             : #endif
     102                 :             : 
     103                 :             : /**
     104                 :             :  * @brief Emit two report() messages with controlled errno values
     105                 :             :  */
     106                 :           1 : static void report_test(void)
     107                 :             : {
     108                 :           1 :         errno = ENOMEM;
     109                 :             : 
     110                 :             :         /* report() must include the formatted message and decoded errno */
     111                 :           1 :         report("Memory reallocation failed with bytes %d",10);
     112                 :             : 
     113                 :           1 :         errno = EOVERFLOW;
     114                 :             : 
     115                 :             :         /* The second call verifies that several printf arguments are preserved */
     116                 :           1 :         report("Buffer overflow at position %d with value %s",42,"overflow");
     117                 :           1 : }
     118                 :             : 
     119                 :             : /**
     120                 :             :  * @brief Emit the complete slog() mode matrix used by the strict stdout pattern
     121                 :             :  */
     122                 :           1 : static void slog_test(void)
     123                 :             : {
     124                 :           1 :         printf("All available combinations:\n");
     125                 :           1 :         printf("%s\n",rational_convert(REGULAR));
     126                 :           1 :         printf("%s\n",rational_convert(VERBOSE));
     127                 :           1 :         printf("%s\n",rational_convert(TESTING));
     128                 :           1 :         printf("%s\n",rational_convert(SILENT));
     129                 :           1 :         printf("%s\n",rational_convert(REGULAR|VERBOSE));
     130                 :           1 :         printf("%s\n",rational_convert(REGULAR|TESTING));
     131                 :           1 :         printf("%s\n",rational_convert(VERBOSE|TESTING));
     132                 :           1 :         printf("%s\n",rational_convert(REGULAR|VERBOSE|TESTING));
     133                 :           1 :         printf("%s\n",rational_convert(ERROR));
     134                 :           1 :         printf("%s\n",rational_convert(UNDECOR));
     135                 :           1 :         printf("%s\n",rational_convert(EVERY|UNDECOR));
     136                 :           1 :         printf("%s\n",rational_convert(ERROR|UNDECOR));
     137                 :           1 :         printf("%s\n",rational_convert(VISIBLE_IN_SILENT));
     138                 :             : 
     139                 :             :         /* Test REGULAR mode combinations */
     140                 :           1 :         rational_logger_mode = REGULAR;
     141                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     142                 :           1 :         printf("1.  Must print:"); slog(REGULAR,"true"); printf("\n");
     143                 :           1 :         printf("2. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
     144                 :           1 :         printf("3. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
     145                 :           1 :         printf("4.  Must print:");   slog(ERROR,"true"); printf("\n");
     146                 :             : 
     147                 :             :         /* Test VERBOSE mode combinations */
     148                 :           1 :         rational_logger_mode = VERBOSE;
     149                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     150                 :           1 :         printf("5. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
     151                 :           1 :         printf("6.  Must print:"); slog(VERBOSE,"true"); printf("\n");
     152                 :           1 :         printf("7. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
     153                 :           1 :         printf("8.  Must print:");   slog(ERROR,"true"); printf("\n");
     154                 :             : 
     155                 :             :         /* Test TESTING mode combinations */
     156                 :           1 :         rational_logger_mode = TESTING;
     157                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     158                 :           1 :         printf("9.  Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
     159                 :           1 :         printf("10. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
     160                 :           1 :         printf("11.  Must print:"); slog(TESTING,"true"); printf("\n");
     161                 :           1 :         printf("12.  Must print:");   slog(ERROR,"true"); printf("\n");
     162                 :             : 
     163                 :             :         /* Test SILENT mode combinations */
     164                 :           1 :         rational_logger_mode = SILENT;
     165                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     166                 :           1 :         printf("13. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
     167                 :           1 :         printf("14. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
     168                 :           1 :         printf("15. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
     169                 :           1 :         printf("16. Won't print:");   slog(ERROR,"but printed!"); printf("\n");
     170                 :             : 
     171                 :             :         /* Test REGULAR|VERBOSE combinations */
     172                 :           1 :         rational_logger_mode = REGULAR|VERBOSE;
     173                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     174                 :           1 :         printf("17.  Must print:"); slog(REGULAR,"true"); printf("\n");
     175                 :           1 :         printf("18.  Must print:"); slog(VERBOSE,"true"); printf("\n");
     176                 :           1 :         printf("19. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
     177                 :           1 :         printf("20.  Must print:");   slog(ERROR,"true"); printf("\n");
     178                 :             : 
     179                 :             :         /* Test REGULAR|TESTING combinations */
     180                 :           1 :         rational_logger_mode = REGULAR|TESTING;
     181                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     182                 :           1 :         printf("21.  Must print:"); slog(REGULAR,"true"); printf("\n");
     183                 :           1 :         printf("22. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
     184                 :           1 :         printf("23.  Must print:"); slog(TESTING,"true"); printf("\n");
     185                 :           1 :         printf("24.  Must print:"); slog(ERROR,"true"); printf("\n");
     186                 :             : 
     187                 :             :         /* Test VERBOSE|TESTING combinations */
     188                 :           1 :         rational_logger_mode = VERBOSE|TESTING;
     189                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     190                 :           1 :         printf("25. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
     191                 :           1 :         printf("26.  Must print:"); slog(VERBOSE,"true"); printf("\n");
     192                 :           1 :         printf("27.  Must print:"); slog(TESTING,"true"); printf("\n");
     193                 :           1 :         printf("28.  Must print:");   slog(ERROR,"true"); printf("\n");
     194                 :             : 
     195                 :             :         /* Test REGULAR|VERBOSE|TESTING combinations */
     196                 :           1 :         rational_logger_mode = REGULAR|VERBOSE|TESTING;
     197                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     198                 :           1 :         printf("29. Must print:"); slog(REGULAR,"true"); printf("\n");
     199                 :           1 :         printf("30. Must print:"); slog(VERBOSE,"true"); printf("\n");
     200                 :           1 :         printf("31. Must print:"); slog(TESTING,"true"); printf("\n");
     201                 :           1 :         printf("32. Must print:");   slog(ERROR,"true"); printf("\n");
     202                 :             : 
     203                 :             :         /* Test ERROR mode combinations */
     204                 :           1 :         rational_logger_mode = ERROR;
     205                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     206                 :           1 :         printf("33. Won't print:"); slog(REGULAR,"but printed!"); printf("\n");
     207                 :           1 :         printf("34. Won't print:"); slog(VERBOSE,"but printed!"); printf("\n");
     208                 :           1 :         printf("35. Won't print:"); slog(TESTING,"but printed!"); printf("\n");
     209                 :           1 :         printf("36.  Must print:");   slog(ERROR,"true"); printf("\n");
     210                 :             : 
     211                 :             :         /*
     212                 :             :          * Test UNDECOR flag: suppress logger prefixes (TESTING:, time/file/line/func, ERROR:)
     213                 :             :          * The output between the '|' markers should contain only the message payload
     214                 :             :          */
     215                 :           1 :         rational_logger_mode = EVERY|ERROR;
     216                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     217                 :           1 :         printf("37. Must print no prefixes:|"); slog(EVERY|UNDECOR,"true"); printf("|\n");
     218                 :           1 :         printf("38. Must print no ERROR prefix:|"); slog(ERROR|UNDECOR,"true"); printf("|\n");
     219                 :             : 
     220                 :           1 :         rational_logger_mode = VERBOSE;
     221                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     222                 :           1 :         printf("39. Must print no time/file/line/func:|"); slog(VERBOSE|UNDECOR,"true"); printf("|\n");
     223                 :             : 
     224                 :           1 :         rational_logger_mode = TESTING;
     225                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     226                 :           1 :         printf("40. Must print no TESTING prefix:|"); slog(TESTING|UNDECOR,"true"); printf("|\n");
     227                 :             : 
     228                 :           1 :         rational_logger_mode = REGULAR;
     229                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     230                 :           1 :         printf("41. Must not print (VERBOSE not enabled):|"); slog(VERBOSE|UNDECOR,"but printed!"); printf("|\n");
     231                 :             : 
     232                 :           1 :         rational_logger_mode = SILENT;
     233                 :           1 :         printf("Mode: %s\n",rational_reconvert(rational_logger_mode));
     234                 :           1 :         printf("42. Must print in SILENT without prefixes:|"); slog(EVERY|VISIBLE_IN_SILENT,"true"); printf("|\n");
     235                 :           1 :         printf("43. Must print no ERROR prefix in SILENT:|"); slog(ERROR|VISIBLE_IN_SILENT,"true"); printf("|\n");
     236                 :           1 : }
     237                 :             : 
     238                 :             : /**
     239                 :             :  * @brief Emit one serp() message with a controlled missing-file errno
     240                 :             :  */
     241                 :           1 : static void serp_case(void)
     242                 :             : {
     243                 :           1 :         errno = ENOENT;
     244                 :           1 :         serp("Failed to open file");
     245                 :           1 : }
     246                 :             : 
     247                 :             : /**
     248                 :             :  * @brief Do nothing while function_capture() checks pending stdout behavior
     249                 :             :  */
     250                 :           1 : static void no_output_test(void)
     251                 :             : {
     252                 :           1 : }
     253                 :             : 
     254                 :             : #ifndef EVIL_EMPIRE_OS
     255                 :             : /**
     256                 :             :  * @brief Exercise slog() when timestamp construction helpers fail
     257                 :             :  */
     258                 :           1 : static void logger_time_failure_cases(void)
     259                 :             : {
     260                 :           1 :         rational_logger_mode = VERBOSE;
     261                 :             : 
     262                 :             :         /* Missing timestamp data should leave an empty prefix slot but keep the message */
     263                 :           1 :         testmocking_gettimeofday_fail_next(1);
     264                 :           1 :         slog(VERBOSE,"gettimeofday failure");
     265                 :           1 :         printf("\n");
     266                 :           1 :         testmocking_gettimeofday_disable();
     267                 :             : 
     268                 :             :         /* localtime_r() failure follows the same fallback path */
     269                 :           1 :         testmocking_localtime_r_fail_next(1);
     270                 :           1 :         slog(VERBOSE,"localtime_r failure");
     271                 :           1 :         printf("\n");
     272                 :           1 :         testmocking_localtime_r_disable();
     273                 :             : 
     274                 :             :         /* snprintf() failure while formatting the timestamp must not hide the payload */
     275                 :           1 :         testmocking_snprintf_fail_next(1);
     276                 :           1 :         slog(VERBOSE,"snprintf failure");
     277                 :           1 :         testmocking_snprintf_disable();
     278                 :             : 
     279                 :           1 :         rational_logger_mode = REGULAR;
     280                 :           1 : }
     281                 :             : 
     282                 :             : /**
     283                 :             :  * @brief Emit one report() message with a controlled I/O errno
     284                 :             :  */
     285                 :           4 : static void report_single_case(void)
     286                 :             : {
     287                 :           4 :         errno = EIO;
     288                 :           4 :         report("Mocked report output");
     289                 :           4 : }
     290                 :             : 
     291                 :             : /**
     292                 :             :  * @brief Emit one report() message while snprintf is forced to truncate
     293                 :             :  */
     294                 :           1 : static void report_truncated_case(void)
     295                 :             : {
     296                 :           1 :         testmocking_snprintf_truncate_next(1);
     297                 :           1 :         report_single_case();
     298                 :           1 :         testmocking_snprintf_disable();
     299                 :           1 : }
     300                 :             : 
     301                 :             : /**
     302                 :             :  * @brief Capture slog() output when line allocation or sanitization fails
     303                 :             :  *
     304                 :             :  * @return Return describing success or failure
     305                 :             :  */
     306                 :           1 : static Return capture_librational_logger_allocation_failures(void)
     307                 :             : {
     308                 :           1 :         INITTEST;
     309                 :           1 :         const RATIONAL_COLOR_MODE initial_color_mode = atomic_load_explicit(&rational_color_mode,memory_order_relaxed);
     310                 :           1 :         const LOGMODES initial_logger_mode = atomic_load_explicit(&rational_logger_mode,memory_order_relaxed);
     311                 :             : 
     312                 :             :         /* The logger should stay silent when it cannot grow the output line */
     313                 :           1 :         testmocking_realloc_fail_next(1);
     314                 :           1 :         rational_logger_mode = REGULAR;
     315                 :           1 :         slog(REGULAR|UNDECOR,"hidden");
     316                 :           1 :         testmocking_realloc_disable();
     317                 :             : 
     318                 :             :         /* Failed sanitization must not expose raw terminal controls */
     319                 :           1 :         testmocking_malloc_fail_next(1);
     320                 :           1 :         rational_color_mode = COLOR_MODE_NEVER;
     321                 :           1 :         slog(REGULAR|UNDECOR,"%s","\033[31mhidden");
     322                 :           1 :         testmocking_malloc_disable();
     323                 :             : 
     324                 :           1 :         rational_color_mode = initial_color_mode;
     325                 :           1 :         rational_logger_mode = initial_logger_mode;
     326                 :             : 
     327                 :           1 :         deliver(status);
     328                 :             : }
     329                 :             : 
     330                 :             : /**
     331                 :             :  * @brief Require prefix failures to stop further formatting and allocation
     332                 :             :  *
     333                 :             :  * @return Return describing success or failure
     334                 :             :  */
     335                 :           1 : static Return capture_librational_logger_prefix_failures(void)
     336                 :             : {
     337                 :           1 :         INITTEST;
     338                 :           1 :         const LOGMODES initial_logger_mode = atomic_load_explicit(&rational_logger_mode,memory_order_relaxed);
     339                 :           1 :         const int initial_errno = errno;
     340                 :             :         static const struct {
     341                 :             :                 LOGMODES mode;
     342                 :             :                 LOGMODES level;
     343                 :             :         } prefix_cases[] = {
     344                 :             :                 {TESTING,TESTING},
     345                 :             :                 {VERBOSE,VERBOSE},
     346                 :             :                 {REGULAR,ERROR},
     347                 :             :                 {TESTING,ERROR},
     348                 :             :                 {TESTING|VERBOSE,TESTING|VERBOSE}
     349                 :             :         };
     350                 :             : 
     351         [ +  + ]:           6 :         for(size_t i = 0U; i < sizeof(prefix_cases) / sizeof(prefix_cases[0]); i++)
     352                 :             :         {
     353                 :             :                 /* A prefix size failure must consume only one mocked call. The next
     354                 :             :                    undecorated message consumes the second failure and also stays silent.
     355                 :             :                    Fail timestamp formatting separately so its snprintf wrapper does not
     356                 :             :                    consume either vsnprintf failure */
     357                 :           5 :                 rational_logger_mode = prefix_cases[i].mode;
     358                 :           5 :                 testmocking_snprintf_fail_next(1);
     359                 :           5 :                 testmocking_vsnprintf_fail_next(2);
     360                 :           5 :                 errno = EDOM;
     361                 :           5 :                 slog(prefix_cases[i].level,"hidden payload");
     362                 :           5 :                 const int formatting_errno = errno;
     363                 :           5 :                 testmocking_snprintf_disable();
     364                 :           5 :                 rational_logger_mode = REGULAR;
     365                 :           5 :                 slog(REGULAR|UNDECOR,"unexpected formatting after prefix failure");
     366                 :           5 :                 testmocking_vsnprintf_disable();
     367                 :           5 :                 ASSERT(formatting_errno == EDOM);
     368                 :             : 
     369                 :             :                 /* The same short-circuit contract applies when prefix allocation fails */
     370                 :           5 :                 rational_logger_mode = prefix_cases[i].mode;
     371                 :           5 :                 testmocking_realloc_fail_next(2);
     372                 :           5 :                 errno = EDOM;
     373                 :           5 :                 slog(prefix_cases[i].level,"hidden payload");
     374                 :           5 :                 const int allocation_errno = errno;
     375                 :           5 :                 rational_logger_mode = REGULAR;
     376                 :           5 :                 slog(REGULAR|UNDECOR,"unexpected allocation after prefix failure");
     377                 :           5 :                 testmocking_realloc_disable();
     378                 :           5 :                 ASSERT(allocation_errno == EDOM);
     379                 :             :         }
     380                 :             : 
     381                 :           1 :         rational_logger_mode = initial_logger_mode;
     382                 :           1 :         errno = initial_errno;
     383                 :             : 
     384                 :           1 :         deliver(status);
     385                 :             : }
     386                 :             : 
     387                 :             : /**
     388                 :             :  * @brief Capture slog() output from timestamp failure scenarios
     389                 :             :  *
     390                 :             :  * @return Return describing success or failure
     391                 :             :  */
     392                 :           1 : static Return capture_librational_logger_time_failures(void)
     393                 :             : {
     394                 :           1 :         INITTEST;
     395                 :             : 
     396                 :             :         /* Cleanup disables every mock in case the captured scenario exits early */
     397                 :           1 :         logger_time_failure_cases();
     398                 :           1 :         rational_logger_mode = REGULAR;
     399                 :           1 :         testmocking_gettimeofday_disable();
     400                 :           1 :         testmocking_localtime_r_disable();
     401                 :           1 :         testmocking_snprintf_disable();
     402                 :           1 :         testmocking_write_disable();
     403                 :             : 
     404                 :           1 :         deliver(status);
     405                 :             : }
     406                 :             : 
     407                 :             : /**
     408                 :             :  * @brief Capture slog() output when vsnprintf fails before line allocation
     409                 :             :  *
     410                 :             :  * @return Return describing success or failure
     411                 :             :  */
     412                 :           1 : static Return capture_librational_logger_vsnprintf_failure(void)
     413                 :             : {
     414                 :           1 :         INITTEST;
     415                 :             : 
     416                 :             :         /* A failed payload size calculation should suppress the incomplete line */
     417                 :           1 :         testmocking_vsnprintf_fail_next(1);
     418                 :           1 :         rational_logger_mode = REGULAR;
     419                 :           1 :         slog(REGULAR|UNDECOR,"hidden");
     420                 :           1 :         rational_logger_mode = REGULAR;
     421                 :           1 :         testmocking_vsnprintf_disable();
     422                 :             : 
     423                 :           1 :         deliver(status);
     424                 :             : }
     425                 :             : #endif
     426                 :             : 
     427                 :             : /**
     428                 :             :  * @brief Capture an ERROR log while no logger mode accepts it
     429                 :             :  *
     430                 :             :  * @return Return describing success or failure
     431                 :             :  */
     432                 :           1 : static Return capture_librational_logger_disabled_error_mode(void)
     433                 :             : {
     434                 :           1 :         INITTEST;
     435                 :             : 
     436                 :             :         /* With no active mode bits, even ERROR must stay silent */
     437                 :           1 :         rational_logger_mode = (LOGMODES)0;
     438                 :           1 :         slog(ERROR,"hidden");
     439                 :           1 :         rational_logger_mode = REGULAR;
     440                 :             : 
     441                 :           1 :         deliver(status);
     442                 :             : }
     443                 :             : 
     444                 :             : #ifndef EVIL_EMPIRE_OS
     445                 :             : /**
     446                 :             :  * @brief Capture REMEMBER calls that have no usable payload
     447                 :             :  *
     448                 :             :  * @return Return describing success or failure
     449                 :             :  */
     450                 :           1 : static Return capture_librational_logger_remember_without_payload(void)
     451                 :             : {
     452                 :           1 :         INITTEST;
     453                 :             : 
     454                 :             :         /* Empty payload produces no remembered or printed line */
     455                 :           1 :         rational_logger_mode = REGULAR;
     456                 :           1 :         slog(REGULAR|UNDECOR|REMEMBER,"");
     457                 :             : 
     458                 :             :         /* Failed formatting also leaves REMEMBER without a payload to deliver */
     459                 :           1 :         testmocking_vsnprintf_fail_next(1);
     460                 :           1 :         slog(REGULAR|UNDECOR|REMEMBER,"hidden");
     461                 :           1 :         testmocking_vsnprintf_disable();
     462                 :             : 
     463                 :           1 :         rational_logger_mode = REGULAR;
     464                 :             : 
     465                 :           1 :         deliver(status);
     466                 :             : }
     467                 :             : #endif
     468                 :             : 
     469                 :             : /**
     470                 :             :  * @brief Capture the basic report() stderr contract
     471                 :             :  *
     472                 :             :  * @return Return describing success or failure
     473                 :             :  */
     474                 :           1 : static Return capture_librational_report_test(void)
     475                 :             : {
     476                 :           1 :         INITTEST;
     477                 :             : 
     478                 :           1 :         report_test();
     479                 :             : 
     480                 :           1 :         deliver(status);
     481                 :             : }
     482                 :             : 
     483                 :             : /**
     484                 :             :  * @brief Capture the full slog() mode matrix
     485                 :             :  *
     486                 :             :  * @return Return describing success or failure
     487                 :             :  */
     488                 :           1 : static Return capture_librational_slog_test(void)
     489                 :             : {
     490                 :           1 :         INITTEST;
     491                 :             : 
     492                 :             :         /* Reset the global logger mode after the matrix leaves it in SILENT */
     493                 :           1 :         slog_test();
     494                 :           1 :         rational_logger_mode = REGULAR;
     495                 :             : 
     496                 :           1 :         deliver(status);
     497                 :             : }
     498                 :             : 
     499                 :             : /**
     500                 :             :  * @brief Capture a log line that contains unsafe terminal bytes
     501                 :             :  */
     502                 :           1 : static void librational_logger_terminal_safety_case(void)
     503                 :             : {
     504                 :             :         /*
     505                 :             :          * The payload mixes terminal-hostile bytes with ordinary UTF-8 text.
     506                 :             :          * Sanitization renders raw ESC bytes as printable text before formatted
     507                 :             :          * markup is interpreted
     508                 :             :          */
     509                 :             :         static const char payload[] =
     510                 :             :                 "ascii"
     511                 :             :                 "\001"
     512                 :             :                 "del"
     513                 :             :                 "\177"
     514                 :             :                 "c1"
     515                 :             :                 "\302\220"
     516                 :             :                 "invalid"
     517                 :             :                 "\303("
     518                 :             :                 "overlong"
     519                 :             :                 "\300\257"
     520                 :             :                 "keep"
     521                 :             :                 "\t\r\n"
     522                 :             :                 "esc"
     523                 :             :                 "\033[1m"
     524                 :             :                 "utf8"
     525                 :             :                 "°"
     526                 :             :                 "à"
     527                 :             :                 "Привет"
     528                 :             :                 "天地玄黄宇宙洪荒日月盈昃辰宿列張"
     529                 :             :                 "いろはにほへとちりぬるを"
     530                 :             :                 "日本語漢字仮名交じり文"
     531                 :             :                 "four-byte😀"
     532                 :             :                 "isolated\200"
     533                 :             :                 "surrogate\355\240\200"
     534                 :             :                 "out-of-range\364\220\200\200"
     535                 :             :                 "truncated\342\202";
     536                 :             : 
     537                 :           1 :         rational_logger_mode = REGULAR;
     538                 :             :         /* Embedded NUL is part of the formatted payload and must not hide later
     539                 :             :            bytes. The final three bytes form an incomplete four-byte UTF-8 sequence */
     540                 :           1 :         slog(REGULAR|UNDECOR,"%s%cafter-null\360\237\230",payload,'\0');
     541                 :           1 :         rational_logger_mode = REGULAR;
     542                 :           1 : }
     543                 :             : 
     544                 :             : /**
     545                 :             :  * @brief Capture one serp() stderr message
     546                 :             :  *
     547                 :             :  * @return Return describing success or failure
     548                 :             :  */
     549                 :           1 : static Return capture_librational_serp_case(void)
     550                 :             : {
     551                 :           1 :         INITTEST;
     552                 :             : 
     553                 :           1 :         serp_case();
     554                 :             : 
     555                 :           1 :         deliver(status);
     556                 :             : }
     557                 :             : 
     558                 :             : #ifndef EVIL_EMPIRE_OS
     559                 :             : /**
     560                 :             :  * @brief Capture report() when formatting the final message fails
     561                 :             :  *
     562                 :             :  * @return Return describing success or failure
     563                 :             :  */
     564                 :           1 : static Return capture_librational_report_snprintf_failure(void)
     565                 :             : {
     566                 :           1 :         INITTEST;
     567                 :             : 
     568                 :             :         /* REPORT() should avoid writing a malformed message after snprintf failure */
     569                 :           1 :         testmocking_snprintf_fail_next(1);
     570                 :           1 :         report_single_case();
     571                 :           1 :         testmocking_snprintf_disable();
     572                 :             : 
     573                 :           1 :         deliver(status);
     574                 :             : }
     575                 :             : 
     576                 :             : /**
     577                 :             :  * @brief Capture report() when the primary write fails but fallback write works
     578                 :             :  *
     579                 :             :  * @return Return describing success or failure
     580                 :             :  */
     581                 :           1 : static Return capture_librational_report_write_fallback(void)
     582                 :             : {
     583                 :           1 :         INITTEST;
     584                 :             : 
     585                 :             :         /* The first write fails, so REPORT() should emit its fixed fallback text */
     586                 :           1 :         testmocking_write_fail_next(1,EIO);
     587                 :           1 :         report_single_case();
     588                 :           1 :         testmocking_write_disable();
     589                 :             : 
     590                 :           1 :         deliver(status);
     591                 :             : }
     592                 :             : 
     593                 :             : /**
     594                 :             :  * @brief Capture report() when both primary and fallback writes fail
     595                 :             :  *
     596                 :             :  * @return Return describing success or failure
     597                 :             :  */
     598                 :           1 : static Return capture_librational_report_write_full_failure(void)
     599                 :             : {
     600                 :           1 :         INITTEST;
     601                 :             : 
     602                 :             :         /* Both writes fail, so REPORT() must give up without leaking partial output */
     603                 :           1 :         testmocking_write_fail_next(2,EIO);
     604                 :           1 :         report_single_case();
     605                 :           1 :         testmocking_write_disable();
     606                 :             : 
     607                 :           1 :         deliver(status);
     608                 :             : }
     609                 :             : #endif
     610                 :             : 
     611                 :             : /**
     612                 :             :  * @brief Check report() formatting with errno and source location
     613                 :             :  *
     614                 :             :  * @return Return describing success or failure
     615                 :             :  */
     616                 :           1 : static Return test_librational_0004_1(void)
     617                 :             : {
     618                 :           1 :         INITTEST;
     619                 :             : 
     620                 :             :         /* report() output goes to stderr and includes file, function, line, message, and errno */
     621                 :           1 :         ASSERT(SUCCESS == match_function_output(
     622                 :             :                 NULL,
     623                 :             :                 expected_report_stderr_pattern,
     624                 :             :                 capture_librational_report_test));
     625                 :             : 
     626                 :           1 :         RETURN_STATUS;
     627                 :             : }
     628                 :             : 
     629                 :             : /**
     630                 :             :  * @brief Check slog() mode filtering, prefixes, and silent visibility
     631                 :             :  *
     632                 :             :  * @return Return describing success or failure
     633                 :             :  */
     634                 :           1 : static Return test_librational_0004_2(void)
     635                 :             : {
     636                 :           1 :         INITTEST;
     637                 :             : 
     638                 :             :         /* The pattern covers every printed line; variable fields are limited to time, path, and line */
     639                 :           1 :         ASSERT(SUCCESS == match_function_output(
     640                 :             :                 expected_slog_stdout_pattern,
     641                 :             :                 NULL,
     642                 :             :                 capture_librational_slog_test));
     643                 :             : 
     644                 :           1 :         RETURN_STATUS;
     645                 :             : }
     646                 :             : 
     647                 :             : /**
     648                 :             :  * @brief Check serp() formatting with errno and source location
     649                 :             :  *
     650                 :             :  * @return Return describing success or failure
     651                 :             :  */
     652                 :           1 : static Return test_librational_0004_3(void)
     653                 :             : {
     654                 :           1 :         INITTEST;
     655                 :             : 
     656                 :             :         /* serp() writes directly to stderr without printf-style formatting */
     657                 :           1 :         ASSERT(SUCCESS == match_function_output(
     658                 :             :                 NULL,
     659                 :             :                 expected_serp_stderr_pattern,
     660                 :             :                 capture_librational_serp_case));
     661                 :             : 
     662                 :           1 :         RETURN_STATUS;
     663                 :             : }
     664                 :             : 
     665                 :             : #ifndef EVIL_EMPIRE_OS
     666                 :             : /**
     667                 :             :  * @brief Check that report() stays silent when message formatting fails
     668                 :             :  *
     669                 :             :  * @return Return describing success or failure
     670                 :             :  */
     671                 :           1 : static Return test_librational_0004_4(void)
     672                 :             : {
     673                 :           1 :         INITTEST;
     674                 :             : 
     675                 :             :         /* NULL patterns require both captured streams to stay empty */
     676                 :           1 :         ASSERT(SUCCESS == match_function_output(
     677                 :             :                 NULL,
     678                 :             :                 NULL,
     679                 :             :                 capture_librational_report_snprintf_failure));
     680                 :             : 
     681                 :           1 :         RETURN_STATUS;
     682                 :             : }
     683                 :             : 
     684                 :             : /**
     685                 :             :  * @brief Check the exact buffer state after report() truncation
     686                 :             :  *
     687                 :             :  * @return Return describing success or failure
     688                 :             :  */
     689                 :           1 : static Return test_librational_0004_5(void)
     690                 :             : {
     691                 :           1 :         INITTEST;
     692                 :             : 
     693                 :           1 :         m_create(char,captured_stdout,MEMORY_STRING);
     694                 :           1 :         m_create(char,captured_stderr,MEMORY_STRING);
     695                 :             : 
     696                 :             :         /* This test inspects raw buffer lengths, so it uses function_capture() directly */
     697                 :           1 :         ASSERT(SUCCESS == function_capture(
     698                 :             :                 report_truncated_case,
     699                 :             :                 captured_stdout,
     700                 :             :                 captured_stderr));
     701                 :             : 
     702                 :           1 :         ASSERT(captured_stdout->length == 0U);
     703                 :           1 :         ASSERT(captured_stderr->length == MAX_CHARACTERS + 1U);
     704                 :           1 :         ASSERT(captured_stderr->string_length == MAX_CHARACTERS);
     705                 :             : 
     706                 :           1 :         const char *stderr_data = m_data_ro(char,captured_stderr);
     707                 :           1 :         ASSERT(stderr_data != NULL);
     708                 :             : 
     709                 :           1 :         IF(stderr_data != NULL)
     710                 :             :         {
     711                 :           1 :                 const char expected_tail[MAX_CHARACTERS] = {0};
     712                 :             : 
     713                 :             :                 /* The snprintf mock writes one visible byte and leaves the rest zero-filled */
     714                 :           1 :                 ASSERT(stderr_data[0] == 'T');
     715                 :           1 :                 ASSERT(0 == memcmp(stderr_data + 1,expected_tail,MAX_CHARACTERS));
     716                 :             :         }
     717                 :             : 
     718                 :           1 :         call(m_del(captured_stdout));
     719                 :           1 :         call(m_del(captured_stderr));
     720                 :             : 
     721                 :           1 :         RETURN_STATUS;
     722                 :             : }
     723                 :             : 
     724                 :             : /**
     725                 :             :  * @brief Check the report() fallback message after primary write failure
     726                 :             :  *
     727                 :             :  * @return Return describing success or failure
     728                 :             :  */
     729                 :           1 : static Return test_librational_0004_6(void)
     730                 :             : {
     731                 :           1 :         INITTEST;
     732                 :             : 
     733                 :           1 :         ASSERT(SUCCESS == match_function_output(
     734                 :             :                 NULL,
     735                 :             :                 expected_write_fallback_stderr_pattern,
     736                 :             :                 capture_librational_report_write_fallback));
     737                 :             : 
     738                 :           1 :         RETURN_STATUS;
     739                 :             : }
     740                 :             : 
     741                 :             : /**
     742                 :             :  * @brief Check that report() stays silent when both writes fail
     743                 :             :  *
     744                 :             :  * @return Return describing success or failure
     745                 :             :  */
     746                 :           1 : static Return test_librational_0004_7(void)
     747                 :             : {
     748                 :           1 :         INITTEST;
     749                 :             : 
     750                 :           1 :         ASSERT(SUCCESS == match_function_output(
     751                 :             :                 NULL,
     752                 :             :                 NULL,
     753                 :             :                 capture_librational_report_write_full_failure));
     754                 :             : 
     755                 :           1 :         RETURN_STATUS;
     756                 :             : }
     757                 :             : #endif
     758                 :             : 
     759                 :             : /**
     760                 :             :  * @brief Check rational_reconvert() edge cases around empty and REMEMBER modes
     761                 :             :  *
     762                 :             :  * @return Return describing success or failure
     763                 :             :  */
     764                 :           1 : static Return test_librational_0004_8(void)
     765                 :             : {
     766                 :           1 :         INITTEST;
     767                 :             : 
     768                 :           1 :         ASSERT(0 == strcmp(rational_reconvert(0),""));
     769                 :           1 :         ASSERT(0 == strcmp(rational_reconvert(REMEMBER),"REMEMBER"));
     770                 :           1 :         ASSERT(0 == strcmp(
     771                 :             :                 rational_reconvert(REGULAR|REMEMBER|VISIBLE_IN_SILENT),
     772                 :             :                 "REGULAR | REMEMBER | VISIBLE_IN_SILENT"));
     773                 :             : 
     774                 :           1 :         RETURN_STATUS;
     775                 :             : }
     776                 :             : 
     777                 :             : /**
     778                 :             :  * @brief Check rational_reconvert() names every logger mode flag
     779                 :             :  *
     780                 :             :  * @return Return describing success or failure
     781                 :             :  */
     782                 :           1 : static Return test_librational_0004_9(void)
     783                 :             : {
     784                 :           1 :         INITTEST;
     785                 :             : 
     786                 :             :         /* Pin all single-flag names and the full mapping order used in diagnostics */
     787                 :           1 :         ASSERT(0 == strcmp(rational_reconvert(REGULAR),"REGULAR"));
     788                 :           1 :         ASSERT(0 == strcmp(rational_reconvert(VERBOSE),"VERBOSE"));
     789                 :           1 :         ASSERT(0 == strcmp(rational_reconvert(TESTING),"TESTING"));
     790                 :           1 :         ASSERT(0 == strcmp(rational_reconvert(ERROR),"ERROR"));
     791                 :           1 :         ASSERT(0 == strcmp(rational_reconvert(SILENT),"SILENT"));
     792                 :           1 :         ASSERT(0 == strcmp(rational_reconvert(UNDECOR),"UNDECOR"));
     793                 :           1 :         ASSERT(0 == strcmp(rational_reconvert(REMEMBER),"REMEMBER"));
     794                 :           1 :         ASSERT(0 == strcmp(rational_reconvert(VISIBLE_IN_SILENT),"VISIBLE_IN_SILENT"));
     795                 :           1 :         ASSERT(0 == strcmp(
     796                 :             :                 rational_reconvert(REGULAR|VERBOSE|TESTING|ERROR|SILENT|UNDECOR|REMEMBER|VISIBLE_IN_SILENT),
     797                 :             :                 "REGULAR | VERBOSE | TESTING | ERROR | SILENT | UNDECOR | REMEMBER | VISIBLE_IN_SILENT"));
     798                 :             : 
     799                 :           1 :         RETURN_STATUS;
     800                 :             : }
     801                 :             : 
     802                 :             : #ifndef EVIL_EMPIRE_OS
     803                 :             : /**
     804                 :             :  * @brief Check that slog() fails closed when line allocation or sanitization fails
     805                 :             :  *
     806                 :             :  * @return Return describing success or failure
     807                 :             :  */
     808                 :           1 : static Return test_librational_0004_10(void)
     809                 :             : {
     810                 :           1 :         INITTEST;
     811                 :             : 
     812                 :           1 :         ASSERT(SUCCESS == match_function_output(
     813                 :             :                 NULL,
     814                 :             :                 NULL,
     815                 :             :                 capture_librational_logger_allocation_failures));
     816                 :             : 
     817                 :           1 :         RETURN_STATUS;
     818                 :             : }
     819                 :             : 
     820                 :             : /**
     821                 :             :  * @brief Check slog() output when timestamp helper calls fail
     822                 :             :  *
     823                 :             :  * @return Return describing success or failure
     824                 :             :  */
     825                 :           1 : static Return test_librational_0004_11(void)
     826                 :             : {
     827                 :           1 :         INITTEST;
     828                 :             : 
     829                 :           1 :         ASSERT(SUCCESS == match_function_output(
     830                 :             :                 expected_logger_time_failure_stdout_pattern,
     831                 :             :                 NULL,
     832                 :             :                 capture_librational_logger_time_failures));
     833                 :             : 
     834                 :           1 :         RETURN_STATUS;
     835                 :             : }
     836                 :             : 
     837                 :             : /**
     838                 :             :  * @brief Check that slog() stays silent when vsnprintf fails
     839                 :             :  *
     840                 :             :  * @return Return describing success or failure
     841                 :             :  */
     842                 :           1 : static Return test_librational_0004_12(void)
     843                 :             : {
     844                 :           1 :         INITTEST;
     845                 :             : 
     846                 :           1 :         ASSERT(SUCCESS == match_function_output(
     847                 :             :                 NULL,
     848                 :             :                 NULL,
     849                 :             :                 capture_librational_logger_vsnprintf_failure));
     850                 :             : 
     851                 :           1 :         RETURN_STATUS;
     852                 :             : }
     853                 :             : #endif
     854                 :             : 
     855                 :             : /**
     856                 :             :  * @brief Check that slog(ERROR) stays silent when no mode accepts ERROR
     857                 :             :  *
     858                 :             :  * @return Return describing success or failure
     859                 :             :  */
     860                 :           1 : static Return test_librational_0004_13(void)
     861                 :             : {
     862                 :           1 :         INITTEST;
     863                 :             : 
     864                 :           1 :         ASSERT(SUCCESS == match_function_output(
     865                 :             :                 NULL,
     866                 :             :                 NULL,
     867                 :             :                 capture_librational_logger_disabled_error_mode));
     868                 :             : 
     869                 :           1 :         RETURN_STATUS;
     870                 :             : }
     871                 :             : 
     872                 :             : #ifndef EVIL_EMPIRE_OS
     873                 :             : /**
     874                 :             :  * @brief Check that REMEMBER has no output when no payload is available
     875                 :             :  *
     876                 :             :  * @return Return describing success or failure
     877                 :             :  */
     878                 :           1 : static Return test_librational_0004_14(void)
     879                 :             : {
     880                 :           1 :         INITTEST;
     881                 :             : 
     882                 :           1 :         ASSERT(SUCCESS == match_function_output(
     883                 :             :                 NULL,
     884                 :             :                 NULL,
     885                 :             :                 capture_librational_logger_remember_without_payload));
     886                 :             : 
     887                 :           1 :         RETURN_STATUS;
     888                 :             : }
     889                 :             : #endif
     890                 :             : 
     891                 :             : /**
     892                 :             :  * @brief Check that slog() escapes terminal-hostile text without damaging normal UTF-8
     893                 :             :  *
     894                 :             :  * @return Return describing success or failure
     895                 :             :  */
     896                 :           1 : static Return test_librational_0004_15(void)
     897                 :             : {
     898                 :           1 :         INITTEST;
     899                 :           1 :         const RATIONAL_COLOR_MODE initial_color_mode = atomic_load_explicit(&rational_color_mode,memory_order_relaxed);
     900                 :             : 
     901                 :           1 :         m_create(char,captured_stdout,MEMORY_STRING);
     902                 :           1 :         m_create(char,captured_stderr,MEMORY_STRING);
     903                 :             : 
     904                 :             :         static const char expected_stdout[] =
     905                 :             :                 "ascii"
     906                 :             :                 "\\x01"
     907                 :             :                 "del"
     908                 :             :                 "\\x7F"
     909                 :             :                 "c1"
     910                 :             :                 "\\xC2\\x90"
     911                 :             :                 "invalid"
     912                 :             :                 "\\xC3("
     913                 :             :                 "overlong"
     914                 :             :                 "\\xC0\\xAF"
     915                 :             :                 "keep"
     916                 :             :                 "\t\r\n"
     917                 :             :                 "esc"
     918                 :             :                 "\\x1B[1m"
     919                 :             :                 "utf8"
     920                 :             :                 "°"
     921                 :             :                 "à"
     922                 :             :                 "Привет"
     923                 :             :                 "天地玄黄宇宙洪荒日月盈昃辰宿列張"
     924                 :             :                 "いろはにほへとちりぬるを"
     925                 :             :                 "日本語漢字仮名交じり文"
     926                 :             :                 "four-byte😀"
     927                 :             :                 "isolated\\x80"
     928                 :             :                 "surrogate\\xED\\xA0\\x80"
     929                 :             :                 "out-of-range\\xF4\\x90\\x80\\x80"
     930                 :             :                 "truncated\\xE2\\x82"
     931                 :             :                 "\\x00after-null\\xF0\\x9F\\x98";
     932                 :             : 
     933                 :           1 :         rational_color_mode = COLOR_MODE_NEVER;
     934                 :           1 :         ASSERT(SUCCESS == function_capture(
     935                 :             :                 librational_logger_terminal_safety_case,
     936                 :             :                 captured_stdout,
     937                 :             :                 captured_stderr));
     938                 :             : 
     939                 :           1 :         ASSERT(0 == strcmp(m_text(captured_stdout),expected_stdout));
     940                 :           1 :         ASSERT(captured_stderr->length == 0U);
     941                 :             : 
     942                 :           1 :         rational_color_mode = initial_color_mode;
     943                 :           1 :         call(m_del(captured_stdout));
     944                 :           1 :         call(m_del(captured_stderr));
     945                 :             : 
     946                 :           1 :         RETURN_STATUS;
     947                 :             : }
     948                 :             : 
     949                 :             : #ifndef EVIL_EMPIRE_OS
     950                 :             : /**
     951                 :             :  * @brief Check that a failed prefix stops processing the rest of its message
     952                 :             :  *
     953                 :             :  * @return Return describing success or failure
     954                 :             :  */
     955                 :           1 : static Return test_librational_0004_17(void)
     956                 :             : {
     957                 :           1 :         INITTEST;
     958                 :             : 
     959                 :           1 :         ASSERT(SUCCESS == match_function_output(
     960                 :             :                 NULL,
     961                 :             :                 NULL,
     962                 :             :                 capture_librational_logger_prefix_failures));
     963                 :             : 
     964                 :           1 :         RETURN_STATUS;
     965                 :             : }
     966                 :             : #endif
     967                 :             : 
     968                 :             : /**
     969                 :             :  * @brief Check that function_capture() flushes pending stdout before redirection
     970                 :             :  *
     971                 :             :  * @return Return describing success or failure
     972                 :             :  */
     973                 :           1 : static Return test_librational_0004_capture_flush(void)
     974                 :             : {
     975                 :           1 :         INITTEST;
     976                 :             : 
     977                 :           1 :         m_create(char,captured_stdout,MEMORY_STRING);
     978                 :           1 :         m_create(char,captured_stderr,MEMORY_STRING);
     979                 :           1 :         int saved_stdout_fd = -1;
     980                 :           1 :         FILE *stdout_tmp = NULL;
     981                 :             : 
     982                 :           1 :         saved_stdout_fd = dup(STDOUT_FILENO);
     983                 :           1 :         ASSERT(saved_stdout_fd != -1);
     984                 :             : 
     985                 :             :         /* Redirect stdout away from the terminal, then leave text pending in stdio */
     986         [ +  - ]:           1 :         if(SUCCESS == status)
     987                 :             :         {
     988                 :           1 :                 stdout_tmp = tmpfile();
     989                 :             :         }
     990                 :             : 
     991                 :           1 :         ASSERT(stdout_tmp != NULL);
     992                 :           1 :         ASSERT(0 == fflush(stdout));
     993                 :           1 :         ASSERT(dup2(fileno(stdout_tmp),STDOUT_FILENO) != -1);
     994                 :           1 :         ASSERT(fprintf(stdout,"pending stdout before function_capture") > 0);
     995                 :             : 
     996                 :             :         /* function_capture() should flush that pending text before installing its own redirection */
     997                 :           1 :         ASSERT(SUCCESS == function_capture(no_output_test,captured_stdout,captured_stderr));
     998                 :           1 :         ASSERT(captured_stdout->length == 0);
     999                 :           1 :         ASSERT(captured_stderr->length == 0);
    1000                 :             : 
    1001                 :           1 :         IF(stdout_tmp != NULL)
    1002                 :             :         {
    1003                 :           1 :                 (void)fflush(stdout);
    1004                 :             :         }
    1005                 :             : 
    1006         [ +  - ]:           1 :         if(saved_stdout_fd != -1)
    1007                 :             :         {
    1008         [ -  + ]:           1 :                 if(dup2(saved_stdout_fd,STDOUT_FILENO) == -1)
    1009                 :             :                 {
    1010                 :           0 :                         testitall_failure_location_record(__FILE__,__func__,__LINE__);
    1011                 :           0 :                         status = FAILURE;
    1012                 :             :                 }
    1013                 :             : 
    1014                 :           1 :                 (void)close(saved_stdout_fd);
    1015                 :             :         }
    1016                 :             : 
    1017                 :           1 :         IF(stdout_tmp != NULL)
    1018                 :             :         {
    1019                 :           1 :                 (void)fclose(stdout_tmp);
    1020                 :             :         }
    1021                 :             : 
    1022                 :           1 :         call(m_del(captured_stdout));
    1023                 :           1 :         call(m_del(captured_stderr));
    1024                 :             : 
    1025                 :           1 :         RETURN_STATUS;
    1026                 :             : }
    1027                 :             : 
    1028                 :             : /**
    1029                 :             :  * @brief Exercise report(), serp(), slog(), and logger mode conversion
    1030                 :             :  *
    1031                 :             :  * The suite checks formatted report output, plain errno reporting through serp(),
    1032                 :             :  * slog() mode filtering and decoration, logger conversion helpers, failure paths
    1033                 :             :  * for formatting/allocation/time/write helpers, and the output-capture guard that
    1034                 :             :  * flushes pending stdout before redirecting streams
    1035                 :             :  *
    1036                 :             :  * @return SUCCESS when all subtests pass
    1037                 :             :  */
    1038                 :           1 : Return test_librational_0004(void)
    1039                 :             : {
    1040                 :           1 :         INITTEST;
    1041                 :             : 
    1042                 :           1 :         TEST(test_librational_0004_capture_flush,"function_capture() flushes pending stdout before redirection");
    1043                 :           1 :         TEST(test_librational_0004_1,"report() writes formatted errno messages with source location");
    1044                 :           1 :         TEST(test_librational_0004_2,"slog() honors logger modes, decorations and silent visibility");
    1045                 :           1 :         TEST(test_librational_0004_3,"serp() writes errno, source file and function name");
    1046                 :             : #ifndef EVIL_EMPIRE_OS
    1047                 :           1 :         TEST(test_librational_0004_4,"report() stays silent when final message formatting fails");
    1048                 :           1 :         TEST(test_librational_0004_5,"report() keeps a truncated fixed-size message buffer terminated");
    1049                 :           1 :         TEST(test_librational_0004_6,"report() writes fallback text after primary write failure");
    1050                 :           1 :         TEST(test_librational_0004_7,"report() stays silent when primary and fallback writes fail");
    1051                 :             : #endif
    1052                 :           1 :         TEST(test_librational_0004_8,"rational_reconvert() covers empty and REMEMBER edge cases");
    1053                 :           1 :         TEST(test_librational_0004_9,"rational_reconvert() names every logger flag");
    1054                 :             : #ifndef EVIL_EMPIRE_OS
    1055                 :           1 :         TEST(test_librational_0004_10,"slog() fails closed when line allocation or sanitization fails");
    1056                 :           1 :         TEST(test_librational_0004_11,"slog() keeps payloads visible when time formatting fails");
    1057                 :           1 :         TEST(test_librational_0004_12,"slog() stays silent when vsnprintf fails");
    1058                 :             : #endif
    1059                 :           1 :         TEST(test_librational_0004_13,"slog(ERROR) stays silent when no mode accepts ERROR");
    1060                 :             : #ifndef EVIL_EMPIRE_OS
    1061                 :           1 :         TEST(test_librational_0004_14,"slog(REMEMBER) skips empty or unformatted payloads");
    1062                 :             : #endif
    1063                 :           1 :         TEST(test_librational_0004_15,"slog() escapes unsafe terminal bytes while preserving normal UTF-8");
    1064                 :             : #ifndef EVIL_EMPIRE_OS
    1065                 :           1 :         TEST(test_librational_0004_17,"slog() stops formatting and allocation after a prefix failure");
    1066                 :             : #endif
    1067                 :             : 
    1068                 :           1 :         RETURN_STATUS;
    1069                 :             : }
        

Generated by: LCOV version 2.0-1