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 : : }
|