LCOV - code coverage report
Current view: top level - libs/testitall/tests/src - test_libtestitall_0010.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 96.1 % 76 73
Test Date: 2026-10-20 14:27:43 Functions: 100.0 % 1 1
Branches: 58.3 % 24 14

             Branch data     Line data    Source code
       1                 :             : #include "test_libtestitall_all.h"
       2                 :             : #include <wordexp.h>
       3                 :             : 
       4                 :             : /**
       5                 :             :  * @brief Replace repeated file placeholders while preserving the remaining text
       6                 :             :  *
       7                 :             :  * Longer, shorter, and empty replacements exercise file growth and truncation.
       8                 :             :  * Unrelated placeholders, comments, and the missing final newline stay intact.
       9                 :             :  * Internal quoting preserves spaces, quotes, and backslashes in filenames and
      10                 :             :  * replacement text while allowing environment expansion. Missing files and
      11                 :             :  * command substitutions must fail without writing a file
      12                 :             :  *
      13                 :             :  * @return Return status code
      14                 :             :  */
      15                 :           1 : Return test_libtestitall_0010(void)
      16                 :             : {
      17                 :           1 :         INITTEST;
      18                 :             : 
      19                 :           1 :         const char original_text[] =
      20                 :             :                 "# Keep this comment\npath = %ROOT_PATH%\npath = %ROOT_PATH%\nother = %OTHER%";
      21                 :           1 :         const char *filename_expression = "$TESTITALL_REPLACEMENT_FILE";
      22                 :           1 :         const char *previous_value = getenv("TESTITALL_REPLACEMENT_FILE");
      23                 :           1 :         char *saved_value = NULL;
      24                 :           1 :         FILE *file = NULL;
      25                 :           1 :         bool file_created = false;
      26                 :           1 :         m_create(char,temporary_directory,MEMORY_STRING);
      27                 :           1 :         m_create(char,file_path,MEMORY_STRING);
      28                 :           1 :         m_create(char,actual_text,MEMORY_STRING);
      29                 :           1 :         m_create(char,expected_error,MEMORY_STRING);
      30                 :           1 :         m_create(char,expected_expanded_text,MEMORY_STRING);
      31                 :             : 
      32         [ -  + ]:           1 :         if(previous_value != NULL)
      33                 :             :         {
      34                 :           0 :                 saved_value = strdup(previous_value);
      35                 :           0 :                 ASSERT(saved_value != NULL);
      36                 :             :         }
      37                 :           1 :         const bool environment_saved = SUCCESS == status;
      38                 :           1 :         ASSERT(SUCCESS == create_tmpdir(temporary_directory));
      39                 :           1 :         const bool directory_created = SUCCESS == status;
      40                 :           1 :         ASSERT(SUCCESS == m_formatted_string(file_path,"%s/settings \"quoted\"\\file.conf",m_text(temporary_directory)));
      41                 :           1 :         ASSERT(setenv("TESTITALL_REPLACEMENT_FILE",m_text(file_path),1) == 0);
      42                 :           1 :         ASSERT(SUCCESS == m_formatted_string(expected_expanded_text,
      43                 :             :                 "# Keep this comment\npath = %s\npath = %s\nother = %%OTHER%%",
      44                 :             :                 m_text(file_path),m_text(file_path)));
      45                 :             : 
      46                 :             :         /* Reading a missing file must fail before any output file is opened */
      47                 :           1 :         call(m_del(STDERR));
      48                 :           1 :         ASSERT(FAILURE & replace_placeholder_in_file(filename_expression,"%ROOT_PATH%","x"));
      49                 :           1 :         ASSERT(SUCCESS == m_formatted_string(expected_error,
      50                 :             :                 "Failed to open pattern file %s\n",m_text(file_path)));
      51                 :           1 :         ASSERT(strcmp(m_text(STDERR),m_text(expected_error)) == 0);
      52                 :           1 :         ASSERT(access(m_text(file_path),F_OK) == -1);
      53         [ +  - ]:           1 :         if(SUCCESS == status)
      54                 :             :         {
      55                 :           1 :                 call(m_del(STDERR));
      56                 :             :         }
      57                 :             : 
      58                 :             :         const struct {
      59                 :             :                 const char *filename;
      60                 :             :                 const char *replacement;
      61                 :             :                 const char *expected_text;
      62                 :           6 :         } cases[] = {
      63                 :           1 :                 {m_text(file_path),"/tmp/storage with spaces/данные",
      64                 :             :                  "# Keep this comment\npath = /tmp/storage with spaces/данные\n"
      65                 :             :                  "path = /tmp/storage with spaces/данные\nother = %OTHER%"},
      66                 :           1 :                 {m_text(file_path),"x","# Keep this comment\npath = x\npath = x\nother = %OTHER%"},
      67                 :           1 :                 {m_text(file_path),"","# Keep this comment\npath = \npath = \nother = %OTHER%"},
      68                 :           1 :                 {m_text(file_path),"\"double\" 'single' \\tail\\",
      69                 :             :                  "# Keep this comment\npath = \"double\" 'single' \\tail\\\n"
      70                 :             :                  "path = \"double\" 'single' \\tail\\\nother = %OTHER%"},
      71                 :           1 :                 {m_text(file_path),"*?[ab] ; | < > &",
      72                 :             :                  "# Keep this comment\npath = *?[ab] ; | < > &\n"
      73                 :             :                  "path = *?[ab] ; | < > &\nother = %OTHER%"},
      74                 :           1 :                 {filename_expression,"$TESTITALL_REPLACEMENT_FILE",m_text(expected_expanded_text)}
      75                 :             :         };
      76                 :             : 
      77   [ +  +  +  - ]:           7 :         for(size_t i = 0U; i < sizeof(cases) / sizeof(cases[0]) && SUCCESS == status; ++i)
      78                 :             :         {
      79                 :           6 :                 file = fopen(m_text(file_path),"wb");
      80                 :           6 :                 ASSERT(file != NULL);
      81         [ +  - ]:           6 :                 if(file != NULL)
      82                 :             :                 {
      83                 :           6 :                         file_created = true;
      84                 :           6 :                         ASSERT(fputs(original_text,file) != EOF);
      85                 :           6 :                         const int close_status = fclose(file);
      86                 :           6 :                         file = NULL;
      87                 :           6 :                         ASSERT(close_status == 0);
      88                 :             :                 }
      89                 :             : 
      90                 :           6 :                 ASSERT(SUCCESS == replace_placeholder_in_file(cases[i].filename,"%ROOT_PATH%",cases[i].replacement));
      91                 :           6 :                 ASSERT(SUCCESS == get_file_content(m_text(file_path),actual_text));
      92                 :           6 :                 ASSERT(strcmp(m_text(actual_text),cases[i].expected_text) == 0);
      93                 :             :         }
      94                 :             : 
      95                 :             :         /* Rejected expressions must leave the existing file unchanged */
      96                 :           1 :         const char *invalid_cases[] = {
      97                 :             :                 "$(printf forbidden)",
      98                 :             :                 "`printf forbidden`"
      99                 :             :         };
     100   [ +  +  +  - ]:           3 :         for(size_t i = 0U; i < sizeof(invalid_cases) / sizeof(invalid_cases[0]) && SUCCESS == status; ++i)
     101                 :             :         {
     102                 :           2 :                 const char *expression = invalid_cases[i];
     103                 :           2 :                 ASSERT(FAILURE & replace_placeholder_in_file(filename_expression,"%ROOT_PATH%",expression));
     104                 :           2 :                 ASSERT(SUCCESS == m_formatted_string(expected_error,
     105                 :             :                         "Failed to expand expression \"%s\" (wordexp code %d)\n",expression,WRDE_CMDSUB));
     106                 :           2 :                 ASSERT(strcmp(m_text(STDERR),m_text(expected_error)) == 0);
     107         [ +  - ]:           2 :                 if(SUCCESS == status)
     108                 :             :                 {
     109                 :           2 :                         call(m_del(STDERR));
     110                 :             :                 }
     111                 :           2 :                 ASSERT(SUCCESS == get_file_content(m_text(file_path),actual_text));
     112                 :           2 :                 ASSERT(strcmp(m_text(actual_text),m_text(expected_expanded_text)) == 0);
     113                 :             :         }
     114                 :             : 
     115         [ +  - ]:           1 :         if(file_created == true)
     116                 :             :         {
     117                 :           1 :                 const int remove_status = unlink(m_text(file_path));
     118                 :           1 :                 ASSERT(remove_status == 0);
     119                 :             :         }
     120         [ +  - ]:           1 :         if(directory_created == true)
     121                 :             :         {
     122                 :           1 :                 const int remove_status = rmdir(m_text(temporary_directory));
     123                 :           1 :                 ASSERT(remove_status == 0);
     124                 :             :         }
     125                 :             : 
     126         [ +  - ]:           1 :         if(environment_saved == true)
     127                 :             :         {
     128                 :             :                 int restore_status;
     129         [ -  + ]:           1 :                 if(saved_value != NULL)
     130                 :             :                 {
     131                 :           0 :                         restore_status = setenv("TESTITALL_REPLACEMENT_FILE",saved_value,1);
     132                 :             :                 } else {
     133                 :           1 :                         restore_status = unsetenv("TESTITALL_REPLACEMENT_FILE");
     134                 :             :                 }
     135                 :           1 :                 ASSERT(restore_status == 0);
     136                 :             :         }
     137                 :           1 :         free(saved_value);
     138                 :           1 :         call(m_del(expected_expanded_text));
     139                 :           1 :         call(m_del(expected_error));
     140                 :           1 :         call(m_del(actual_text));
     141                 :           1 :         call(m_del(file_path));
     142                 :           1 :         call(m_del(temporary_directory));
     143                 :           1 :         RETURN_STATUS;
     144                 :             : }
        

Generated by: LCOV version 2.0-1