LCOV - code coverage report
Current view: top level - libs/testitall/tests/src - test_libtestitall_0009.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 122 122
Test Date: 2026-10-20 14:27:43 Functions: 100.0 % 2 2
Branches: 62.5 % 40 25

             Branch data     Line data    Source code
       1                 :             : #include "test_libtestitall_all.h"
       2                 :             : 
       3                 :             : #include <errno.h>
       4                 :             : 
       5                 :             : /**
       6                 :             :  * @brief Require a launch error rather than an accepted child exit code
       7                 :             :  *
       8                 :             :  * The caller buffers start with stale content and must be cleared even when
       9                 :             :  * no child starts. A requested exit code of 127 must not make a spawn error
      10                 :             :  * successful. The complete diagnostic remains in the shared STDERR buffer
      11                 :             :  *
      12                 :             :  * @param expected_errno Platform error code returned when the executable cannot start
      13                 :             :  * @return SUCCESS when the launch failure and output buffers match expectations
      14                 :             :  */
      15                 :           3 : static Return check_external_spawn_error(int expected_errno)
      16                 :             : {
      17                 :             :         /* Status returned by this function through provide()
      18                 :             :            Default value assumes successful completion */
      19                 :           3 :         Return status = SUCCESS;
      20                 :           3 :         m_create(char,captured_stdout,MEMORY_STRING);
      21                 :           3 :         m_create(char,captured_stderr,MEMORY_STRING);
      22                 :             :         char expected_error_pattern[128];
      23                 :             : 
      24                 :           3 :         ASSERT(SUCCESS == m_copy_literal(captured_stdout,"stale stdout"));
      25                 :           3 :         ASSERT(SUCCESS == m_copy_literal(captured_stderr,"stale stderr"));
      26                 :           3 :         ASSERT(FAILURE & runit("",captured_stdout,captured_stderr,127,STDERR_ALLOW));
      27                 :           3 :         ASSERT(captured_stdout->string_length == 0U);
      28                 :           3 :         ASSERT(captured_stderr->string_length == 0U);
      29                 :           3 :         ASSERT(STDOUT->string_length == 0U);
      30                 :           3 :         ASSERT(snprintf(expected_error_pattern,sizeof(expected_error_pattern),
      31                 :             :                 "\\AFailed to spawn process for EXTERNAL_CALL \\(errno %d\\)\\z",expected_errno) > 0);
      32                 :           3 :         ASSERT(SUCCESS == assert_stderr_matches_pattern(expected_error_pattern));
      33                 :             : 
      34                 :           3 :         call(m_del(captured_stderr));
      35                 :           3 :         call(m_del(captured_stdout));
      36                 :             : 
      37                 :           3 :         deliver(status);
      38                 :             : }
      39                 :             : 
      40                 :             : /**
      41                 :             :  * @brief Check external runit execution context and executable launch failures
      42                 :             :  *
      43                 :             :  * A temporary precizer script runs /bin/sh to expose the child's working
      44                 :             :  * directory, inherited environment, and quoted arguments through captured
      45                 :             :  * output. A real exit code of 127 is accepted when requested, while missing,
      46                 :             :  * nonexecutable, and unsupported executable files remain launch failures.
      47                 :             :  * The test restores the runner mode, environment, and parent working directory
      48                 :             :  * and removes its temporary files even after a failed assertion
      49                 :             :  *
      50                 :             :  * @return Return status code
      51                 :             :  */
      52                 :           1 : Return test_libtestitall_0009(void)
      53                 :             : {
      54                 :           1 :         INITTEST;
      55                 :             : 
      56                 :             :         static const char *const environment_names[] = {
      57                 :             :                 "BINDIR","TMPDIR","TESTITALL_SPAWN_TEST_VALUE"
      58                 :             :         };
      59                 :           1 :         const char inherited_value[] = "inherited value ; $literal";
      60                 :           1 :         const char arguments[] =
      61                 :             :                 "-c 'pwd -P; "
      62                 :             :                 "printf \"env=<%s>\\nargc=<%s>\\narg1=<%s>\\narg2=<%s>\\narg3=<%s>\\n\" "
      63                 :             :                 "\"$TESTITALL_SPAWN_TEST_VALUE\" \"$#\" \"$1\" \"$2\" \"$3\"; "
      64                 :             :                 "printf \"captured stderr\\n\" >&2' "
      65                 :             :                 "child 'two words' '' 'literal ; | & > $dollar *'";
      66                 :           1 :         char *saved_environment[3] = {NULL,NULL,NULL};
      67                 :           1 :         const size_t environment_count = sizeof(environment_names) / sizeof(environment_names[0]);
      68                 :           1 :         const enum run_mode saved_run_mode = testitall_runit_mode;
      69                 :           1 :         bool environment_snapshot_ready = false;
      70                 :           1 :         bool directory_created = false;
      71                 :           1 :         bool program_created = false;
      72                 :           1 :         char *original_directory = NULL;
      73                 :           1 :         char *current_directory = NULL;
      74                 :           1 :         char *canonical_temporary_directory = NULL;
      75                 :           1 :         char *expected_stdout = NULL;
      76                 :           1 :         FILE *program_file = NULL;
      77                 :           1 :         m_create(char,temporary_directory,MEMORY_STRING);
      78                 :           1 :         m_create(char,program_path,MEMORY_STRING);
      79                 :           1 :         m_create(char,captured_stdout,MEMORY_STRING);
      80                 :           1 :         m_create(char,captured_stderr,MEMORY_STRING);
      81                 :             : 
      82   [ +  +  +  - ]:           4 :         for(size_t index = 0U; index < environment_count && SUCCESS == status; ++index)
      83                 :             :         {
      84                 :           3 :                 const char *value = getenv(environment_names[index]);
      85                 :             : 
      86         [ +  + ]:           3 :                 if(value != NULL)
      87                 :             :                 {
      88                 :           2 :                         saved_environment[index] = strdup(value);
      89                 :           2 :                         ASSERT(saved_environment[index] != NULL);
      90                 :             :                 }
      91                 :             :         }
      92                 :             : 
      93         [ +  - ]:           1 :         if(SUCCESS == status)
      94                 :             :         {
      95                 :           1 :                 environment_snapshot_ready = true;
      96                 :           1 :                 original_directory = getcwd(NULL,0);
      97                 :           1 :                 ASSERT(original_directory != NULL);
      98                 :             :         }
      99                 :             : 
     100                 :           1 :         ASSERT(SUCCESS == create_tmpdir(temporary_directory));
     101                 :             : 
     102         [ +  - ]:           1 :         if(SUCCESS == status)
     103                 :             :         {
     104                 :           1 :                 directory_created = true;
     105                 :           1 :                 canonical_temporary_directory = realpath(m_text(temporary_directory),NULL);
     106                 :           1 :                 ASSERT(canonical_temporary_directory != NULL);
     107                 :             :         }
     108                 :             : 
     109                 :           1 :         ASSERT(SUCCESS == m_copy(program_path,temporary_directory));
     110                 :           1 :         ASSERT(SUCCESS == m_concat_literal(program_path,"/precizer"));
     111                 :           1 :         ASSERT(setenv("BINDIR",m_text(temporary_directory),1) == 0);
     112                 :           1 :         ASSERT(setenv("TMPDIR",m_text(temporary_directory),1) == 0);
     113                 :           1 :         ASSERT(setenv("TESTITALL_SPAWN_TEST_VALUE",inherited_value,1) == 0);
     114                 :             : 
     115         [ +  - ]:           1 :         if(SUCCESS == status)
     116                 :             :         {
     117                 :           1 :                 testitall_runit_mode = EXTERNAL_CALL;
     118                 :             :         }
     119                 :             : 
     120                 :           1 :         ASSERT(SUCCESS == check_external_spawn_error(ENOENT));
     121                 :             : 
     122         [ +  - ]:           1 :         if(SUCCESS == status)
     123                 :             :         {
     124                 :           1 :                 program_file = fopen(m_text(program_path),"wb");
     125                 :           1 :                 ASSERT(program_file != NULL);
     126                 :             :         }
     127                 :             : 
     128         [ +  - ]:           1 :         if(SUCCESS == status)
     129                 :             :         {
     130                 :           1 :                 program_created = true;
     131                 :           1 :                 ASSERT(fputs("#!/bin/sh\nexec /bin/sh \"$@\"\n",program_file) != EOF);
     132                 :             :         }
     133                 :             : 
     134         [ +  - ]:           1 :         if(program_file != NULL)
     135                 :             :         {
     136                 :           1 :                 const int close_status = fclose(program_file);
     137                 :           1 :                 program_file = NULL;
     138                 :           1 :                 ASSERT(close_status == 0);
     139                 :             :         }
     140                 :             : 
     141                 :           1 :         ASSERT(chmod(m_text(program_path),0700) == 0);
     142                 :             : 
     143                 :           1 :         ASSERT(asprintf(&expected_stdout,
     144                 :             :                 "%s\nenv=<%s>\nargc=<3>\narg1=<two words>\narg2=<>\narg3=<literal ; | & > $dollar *>\n",
     145                 :             :                 canonical_temporary_directory,inherited_value) >= 0);
     146                 :           1 :         ASSERT(SUCCESS == runit(arguments,captured_stdout,captured_stderr,0,STDERR_ALLOW));
     147                 :           1 :         ASSERT(strcmp(m_text(captured_stdout),expected_stdout) == 0);
     148                 :           1 :         ASSERT(strcmp(m_text(captured_stderr),"captured stderr\n") == 0);
     149                 :             : 
     150                 :           1 :         ASSERT(SUCCESS == runit("-c 'exit 127'",captured_stdout,captured_stderr,127,STDERR_ALLOW));
     151                 :           1 :         ASSERT(captured_stdout->string_length == 0U);
     152                 :           1 :         ASSERT(captured_stderr->string_length == 0U);
     153                 :           1 :         ASSERT(unlink(m_text(program_path)) == 0);
     154                 :             : 
     155         [ +  - ]:           1 :         if(SUCCESS == status)
     156                 :             :         {
     157                 :           1 :                 program_created = false;
     158                 :           1 :                 program_file = fopen(m_text(program_path),"wb");
     159                 :           1 :                 ASSERT(program_file != NULL);
     160                 :             :         }
     161                 :             : 
     162         [ +  - ]:           1 :         if(SUCCESS == status)
     163                 :             :         {
     164                 :           1 :                 program_created = true;
     165                 :           1 :                 ASSERT(fputs("printf unexpected-shell-fallback\n",program_file) != EOF);
     166                 :             :         }
     167                 :             : 
     168         [ +  - ]:           1 :         if(program_file != NULL)
     169                 :             :         {
     170                 :           1 :                 const int close_status = fclose(program_file);
     171                 :           1 :                 program_file = NULL;
     172                 :           1 :                 ASSERT(close_status == 0);
     173                 :             :         }
     174                 :             : 
     175                 :           1 :         ASSERT(chmod(m_text(program_path),0600) == 0);
     176                 :           1 :         ASSERT(SUCCESS == check_external_spawn_error(EACCES));
     177                 :           1 :         ASSERT(chmod(m_text(program_path),0700) == 0);
     178                 :           1 :         ASSERT(SUCCESS == check_external_spawn_error(ENOEXEC));
     179                 :             : 
     180         [ +  - ]:           1 :         if(SUCCESS == status)
     181                 :             :         {
     182                 :           1 :                 current_directory = getcwd(NULL,0);
     183                 :           1 :                 ASSERT(current_directory != NULL);
     184                 :           1 :                 ASSERT(strcmp(current_directory,original_directory) == 0);
     185                 :             :         }
     186                 :             : 
     187                 :             :         /* Restore shared state even when an assertion stopped the test */
     188                 :           1 :         testitall_runit_mode = saved_run_mode;
     189                 :             : 
     190         [ +  - ]:           1 :         if(environment_snapshot_ready == true)
     191                 :             :         {
     192         [ +  + ]:           4 :                 for(size_t index = 0U; index < environment_count; ++index)
     193                 :             :                 {
     194                 :           3 :                         int restore_status = 0;
     195                 :             : 
     196         [ +  + ]:           3 :                         if(saved_environment[index] != NULL)
     197                 :             :                         {
     198                 :           2 :                                 restore_status = setenv(environment_names[index],saved_environment[index],1);
     199                 :             :                         } else {
     200                 :           1 :                                 restore_status = unsetenv(environment_names[index]);
     201                 :             :                         }
     202                 :             : 
     203                 :           3 :                         ASSERT(restore_status == 0);
     204                 :             :                 }
     205                 :             :         }
     206                 :             : 
     207         [ +  - ]:           1 :         if(original_directory != NULL)
     208                 :             :         {
     209                 :           1 :                 const int restore_status = chdir(original_directory);
     210                 :           1 :                 ASSERT(restore_status == 0);
     211                 :             :         }
     212                 :             : 
     213         [ +  - ]:           1 :         if(program_created == true)
     214                 :             :         {
     215                 :           1 :                 const int remove_status = unlink(m_text(program_path));
     216                 :           1 :                 ASSERT(remove_status == 0);
     217                 :             :         }
     218                 :             : 
     219         [ +  - ]:           1 :         if(directory_created == true)
     220                 :             :         {
     221                 :           1 :                 const int remove_status = rmdir(m_text(temporary_directory));
     222                 :           1 :                 ASSERT(remove_status == 0);
     223                 :             :         }
     224                 :             : 
     225         [ +  + ]:           4 :         for(size_t index = 0U; index < environment_count; ++index)
     226                 :             :         {
     227                 :           3 :                 free(saved_environment[index]);
     228                 :             :         }
     229                 :             : 
     230                 :           1 :         free(expected_stdout);
     231                 :           1 :         free(canonical_temporary_directory);
     232                 :           1 :         free(current_directory);
     233                 :           1 :         free(original_directory);
     234                 :           1 :         call(m_del(captured_stderr));
     235                 :           1 :         call(m_del(captured_stdout));
     236                 :           1 :         call(m_del(program_path));
     237                 :           1 :         call(m_del(temporary_directory));
     238                 :             : 
     239                 :           1 :         RETURN_STATUS;
     240                 :             : }
        

Generated by: LCOV version 2.0-1