Branch data Line data Source code
1 : : #include "sute.h"
2 : :
3 : : enum
4 : : {
5 : : TEST0023_EXPECTED_PATH_COUNT = 8
6 : : };
7 : :
8 : : static const char *const test0023_expected_paths[TEST0023_EXPECTED_PATH_COUNT] = {
9 : : ".",
10 : : "AAA",
11 : : "AAA/BBB",
12 : : "AAA/BBB/CCC",
13 : : "AAA/BBB/CCC/a.txt",
14 : : "AAA/BBB/uuu.txt",
15 : : "AAA/tttt.txt",
16 : : "sss.txt"
17 : : };
18 : :
19 : : /**
20 : : * @brief Compare two FTS entries by filename
21 : : * @param first Pointer to first FTSENT structure
22 : : * @param second Pointer to second FTSENT structure
23 : : * @return Integer less than, equal to, or greater than zero when the first
24 : : * name is sorted before, matches, or is sorted after the second name
25 : : */
26 : 21 : static int test0023_compare_by_name(
27 : : #ifdef __CYGWIN__
28 : : const FTSENT * const *first,
29 : : const FTSENT * const *second)
30 : : #else
31 : : const FTSENT **first,
32 : : const FTSENT **second)
33 : : #endif
34 : : {
35 : 21 : return strcmp((*first)->fts_name,(*second)->fts_name);
36 : : }
37 : :
38 : : /**
39 : : * @brief Check path_build_relative() for one textual form of the same fixture root
40 : : *
41 : : * @details
42 : : * The helper opens the supplied root path with FTS and verifies that every
43 : : * directory or regular file in the fixture is represented by the same
44 : : * root-relative path set. The root spelling itself may be plain relative,
45 : : * dot-relative, contain `..`, or be absolute; the built child paths must stay
46 : : * relative to that opened root
47 : : *
48 : : * @param[in] root_path_text Root path spelling passed to FTS
49 : : * @return SUCCESS when all expected root-relative paths are produced
50 : : */
51 : 7 : static Return test0023_expect_paths_for_root(const char *root_path_text)
52 : : {
53 : : /* Status returned by this function through provide()
54 : : Default value assumes successful completion */
55 : 7 : Return status = SUCCESS;
56 : :
57 : 7 : bool seen[TEST0023_EXPECTED_PATH_COUNT] = {false};
58 : 7 : FTS *file_systems = NULL;
59 : :
60 : 7 : m_create(char,root_path,MEMORY_STRING);
61 : 7 : m_create(char,relative_path,MEMORY_STRING);
62 : :
63 : 7 : ASSERT(root_path_text != NULL);
64 : :
65 [ + - ]: 7 : if(SUCCESS == status)
66 : : {
67 : 7 : run(m_copy_string(root_path,root_path_text));
68 : : }
69 : :
70 [ + - ]: 7 : if(SUCCESS == status)
71 : : {
72 : 7 : char *runtime_root_path = m_data(char,root_path);
73 : 7 : ASSERT(runtime_root_path != NULL);
74 : :
75 [ + - ]: 7 : if(SUCCESS == status)
76 : : {
77 : 7 : char *root_argv[] = {
78 : : runtime_root_path,
79 : : NULL
80 : : };
81 : 7 : int fts_options = FTS_PHYSICAL;
82 : :
83 : : #ifdef FTS_NOCHDIR
84 : 7 : fts_options |= FTS_NOCHDIR;
85 : : #endif
86 : :
87 : 7 : file_systems = fts_open(root_argv,fts_options,test0023_compare_by_name);
88 : 7 : ASSERT(file_systems != NULL);
89 : : }
90 : : }
91 : :
92 [ + - ]: 7 : if(SUCCESS == status)
93 : : {
94 : 7 : FTSENT *entry = NULL;
95 : :
96 [ + + ]: 91 : while((entry = fts_read(file_systems)) != NULL)
97 : : {
98 [ + + + + ]: 84 : if(entry->fts_info != FTS_D && entry->fts_info != FTS_F)
99 : : {
100 : 28 : continue;
101 : : }
102 : :
103 : 56 : run(path_build_relative(relative_path,entry));
104 : :
105 [ - + ]: 56 : if(SUCCESS != status)
106 : : {
107 : 0 : break;
108 : : }
109 : :
110 : 56 : const char *relative_path_text = m_text(relative_path);
111 : 56 : bool expected_path_seen = false;
112 : :
113 : 56 : ASSERT(relative_path_text != NULL);
114 : 56 : ASSERT(relative_path_text[0] != '\0');
115 : 56 : ASSERT(relative_path_text[0] != '/');
116 : :
117 [ + - ]: 252 : for(size_t i = 0; i < TEST0023_EXPECTED_PATH_COUNT; i++)
118 : : {
119 [ + + ]: 252 : if(strcmp(relative_path_text,test0023_expected_paths[i]) == COMPLETED)
120 : : {
121 : 56 : seen[i] = true;
122 : 56 : expected_path_seen = true;
123 : :
124 : 56 : break;
125 : : }
126 : : }
127 : :
128 : 56 : ASSERT(expected_path_seen == true);
129 : : }
130 : : }
131 : :
132 [ + - ]: 7 : if(file_systems != NULL)
133 : : {
134 [ - + ]: 7 : if(fts_close(file_systems) != COMPLETED)
135 : : {
136 : 0 : status = FAILURE;
137 : : }
138 : : }
139 : :
140 [ + - ]: 7 : if(SUCCESS == status)
141 : : {
142 [ + + ]: 63 : for(size_t i = 0; i < TEST0023_EXPECTED_PATH_COUNT; i++)
143 : : {
144 : 56 : ASSERT(seen[i] == true);
145 : : }
146 : : }
147 : :
148 : 7 : call(m_del(relative_path));
149 : 7 : call(m_del(root_path));
150 : :
151 : 7 : provide(status);
152 : : }
153 : :
154 : : /**
155 : : * @brief Test path_build_relative() with several root path spellings
156 : : *
157 : : * @details
158 : : * The test uses one real fixture directory and opens it through different
159 : : * root spellings. Relative spellings are checked from the isolated test
160 : : * workspace, because application tests also run against fixtures copied under
161 : : * `$TMPDIR`. This checks the behavior that matters to the application: the
162 : : * root path is chosen by the user and stored separately, while each file path
163 : : * produced by traversal is built relative to that opened root
164 : : *
165 : : * @return Test status indicating success or failure
166 : : */
167 : 1 : Return test0023(void)
168 : : {
169 : 1 : INITTEST;
170 : :
171 : 1 : bool cwd_changed = false;
172 : 1 : char *saved_cwd = NULL;
173 : 1 : const char *tmpdir = getenv("TMPDIR");
174 : 1 : const char *tmpdir_basename = NULL;
175 : 1 : const char *tmpdir_last_slash = NULL;
176 : 1 : const char *parent_basename = NULL;
177 : 1 : const char *parent_end = NULL;
178 : 1 : size_t parent_basename_length = 0U;
179 : :
180 : 1 : m_create(char,parent_relative_root_path,MEMORY_STRING);
181 : 1 : m_create(char,grandparent_relative_root_path,MEMORY_STRING);
182 : 1 : m_create(char,absolute_root_path,MEMORY_STRING);
183 : :
184 : : /*
185 : : * Save the current directory because the relative-root checks below run
186 : : * from TMPDIR, just like application runs launched through testitall helpers
187 : : */
188 : : #if defined(__GLIBC__)
189 : 1 : saved_cwd = get_current_dir_name();
190 : : #else
191 : : // Portable fallback for platforms without get_current_dir_name, such as macOS
192 : : saved_cwd = getcwd(NULL,0);
193 : : #endif
194 : :
195 : 1 : ASSERT(tmpdir != NULL);
196 : 1 : ASSERT(saved_cwd != NULL);
197 : :
198 : : /*
199 : : * Split TMPDIR into its final directory name and its parent directory name.
200 : : * Those pieces let the test spell the same fixture root through ../ and
201 : : * ../../ without hard-coding the generated temporary directory path
202 : : */
203 [ + - ]: 1 : if(SUCCESS == status)
204 : : {
205 : 1 : tmpdir_last_slash = strrchr(tmpdir,'/');
206 : :
207 : 1 : ASSERT(tmpdir_last_slash != NULL);
208 : 1 : ASSERT(tmpdir_last_slash[1] != '\0');
209 : : }
210 : :
211 [ + - ]: 1 : if(SUCCESS == status)
212 : : {
213 : 1 : tmpdir_basename = tmpdir_last_slash + 1;
214 : 1 : parent_end = tmpdir_last_slash;
215 : :
216 [ + - - + ]: 1 : while(parent_end > tmpdir && parent_end[-1] == '/')
217 : : {
218 : 0 : parent_end--;
219 : : }
220 : :
221 : 1 : parent_basename = parent_end;
222 : :
223 [ + - + + ]: 4 : while(parent_basename > tmpdir && parent_basename[-1] != '/')
224 : : {
225 : 3 : parent_basename--;
226 : : }
227 : :
228 : 1 : parent_basename_length = (size_t)(parent_end - parent_basename);
229 : :
230 : 1 : ASSERT(parent_basename_length > 0U);
231 : 1 : ASSERT(parent_basename_length <= INT_MAX);
232 : : }
233 : :
234 : : /*
235 : : * Build two parent-relative root spellings that still resolve to the same
236 : : * fixture directory after the test changes into TMPDIR
237 : : */
238 [ + - ]: 1 : if(SUCCESS == status)
239 : : {
240 : 1 : run(m_formatted_string(parent_relative_root_path,
241 : : "../%s/tests/fixtures/4",
242 : : tmpdir_basename));
243 : 1 : run(m_formatted_string(grandparent_relative_root_path,
244 : : "../../%.*s/%s/tests/fixtures/4",
245 : : (int)parent_basename_length,
246 : : parent_basename,
247 : : tmpdir_basename));
248 : : }
249 : :
250 : : /*
251 : : * Run the relative spelling checks from TMPDIR. The main application tests
252 : : * also run commands from this isolated workspace, so this mirrors the
253 : : * command-line environment used by system-style tests
254 : : */
255 [ + - ]: 1 : if(SUCCESS == status)
256 : : {
257 : 1 : ASSERT(chdir(tmpdir) == COMPLETED);
258 : :
259 [ + - ]: 1 : if(SUCCESS == status)
260 : : {
261 : 1 : cwd_changed = true;
262 : : }
263 : : }
264 : :
265 : : /*
266 : : * Open the same fixture root through several user-facing spellings and
267 : : * require path_build_relative() to produce the same root-relative path set
268 : : * for each spelling
269 : : */
270 [ + - ]: 1 : if(SUCCESS == status)
271 : : {
272 : 3 : const char *const root_path_spellings[] = {
273 : : "tests/fixtures/4",
274 : : "tests/fixtures/4/",
275 : : "./tests/fixtures/4",
276 : : "tests/fixtures/diffs/../4",
277 : 1 : m_text(parent_relative_root_path),
278 : 1 : m_text(grandparent_relative_root_path),
279 : : NULL
280 : : };
281 : :
282 [ + + ]: 7 : for(size_t i = 0; root_path_spellings[i] != NULL; i++)
283 : : {
284 : 6 : run(test0023_expect_paths_for_root(root_path_spellings[i]));
285 : : }
286 : : }
287 : :
288 : : /*
289 : : * Restore the original working directory before checking the absolute root
290 : : * spelling. This keeps the rest of the test suite independent from this test
291 : : */
292 [ + - + - ]: 1 : if(cwd_changed == true && saved_cwd != NULL)
293 : : {
294 [ - + ]: 1 : if(chdir(saved_cwd) != COMPLETED)
295 : : {
296 : 0 : status = FAILURE;
297 : : }
298 : :
299 : 1 : cwd_changed = false;
300 : : }
301 : :
302 : : /*
303 : : * Check the absolute spelling separately after restoring cwd. The expected
304 : : * root-relative paths must still be identical to the relative spelling cases
305 : : */
306 [ + - ]: 1 : if(SUCCESS == status)
307 : : {
308 : 1 : run(construct_path("tests/fixtures/4",absolute_root_path));
309 : 1 : run(test0023_expect_paths_for_root(m_text(absolute_root_path)));
310 : : }
311 : :
312 : 1 : call(m_del(absolute_root_path));
313 : 1 : call(m_del(grandparent_relative_root_path));
314 : 1 : call(m_del(parent_relative_root_path));
315 : :
316 [ + - ]: 1 : if(saved_cwd != NULL)
317 : : {
318 : 1 : free(saved_cwd);
319 : : }
320 : :
321 : 1 : RETURN_STATUS;
322 : : }
|