]> git.saurik.com Git - apple/libc.git/blame - tests/fts_simple.c
Libc-1439.100.3.tar.gz
[apple/libc.git] / tests / fts_simple.c
CommitLineData
a9aaacca
A
1#include <err.h>
2#include <stdbool.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <sysexits.h>
7#include <unistd.h>
8#include <fts.h>
9#include <sys/types.h>
10#include <sys/stat.h>
11#include <struct.h>
12#include <darwintest.h>
13#include <darwintest_utils.h>
14
15static void
16_create_random_file(int root_fd, char *path)
17{
18 int fd = openat(root_fd, path, O_WRONLY | O_CREAT);
19 T_ASSERT_POSIX_SUCCESS(fd, NULL);
20 T_ASSERT_POSIX_SUCCESS(dprintf(fd, "Random File at: %s", path), NULL);
21 T_ASSERT_POSIX_SUCCESS(close(fd), NULL);
22}
23
24static void
25_create_symlink(char *root, char *destination, char *source)
26{
27 char *absolute_destination = NULL;
28 T_ASSERT_POSIX_SUCCESS(asprintf(&absolute_destination, "%s/%s", root, destination), NULL);
29 char *absolute_source = NULL;
30 T_ASSERT_POSIX_SUCCESS(asprintf(&absolute_source, "%s/%s", root, source), NULL);
31 T_ASSERT_POSIX_SUCCESS(symlink(absolute_destination, absolute_source), NULL);
32 free(absolute_destination);
33 free(absolute_source);
34}
35
36static char *
37_remove_prefix(char *prefix, char *input) {
38 char *start = strstr(input, prefix);
39 T_QUIET;
40 T_ASSERT_NOTNULL(start, "prefix: %s input: %s", prefix, input);
41 char *end = start + strlen(prefix) + 1;
42 return end;
43}
44
45T_DECL(fts_simple, "Simple fts_read test")
46{
47 T_LOG("prog: %s", getprogname());
48 char *tmp_path = NULL;
49 T_ASSERT_POSIX_SUCCESS(asprintf(&tmp_path, "%s/%s-XXXXXX", dt_tmpdir(), T_NAME), NULL);
50 T_ASSERT_NOTNULL(mktemp(tmp_path), NULL);
51 T_ASSERT_POSIX_SUCCESS(mkdir(tmp_path, 0777), NULL);
52 int tmp_fd = open(tmp_path, O_RDONLY | O_DIRECTORY);
53 T_LOG("tmp: %s", tmp_path);
54 T_ASSERT_POSIX_SUCCESS(tmp_fd, NULL);
55
56 T_ASSERT_POSIX_SUCCESS(mkdirat(tmp_fd, "A", 0777), NULL);
57 T_ASSERT_POSIX_SUCCESS(mkdirat(tmp_fd, "A/B", 0777), NULL);
58 T_ASSERT_POSIX_SUCCESS(mkdirat(tmp_fd, "A/C", 0777), NULL);
59 T_ASSERT_POSIX_SUCCESS(mkdirat(tmp_fd, "A/B/D", 0777), NULL);
60 T_ASSERT_POSIX_SUCCESS(mkdirat(tmp_fd, "A/C/empty", 0777), NULL);
61 _create_random_file(tmp_fd, "root");
62 _create_random_file(tmp_fd, "A/fileA1");
63 _create_random_file(tmp_fd, "A/fileA2");
64 _create_random_file(tmp_fd, "A/B/fileB1");
65 _create_random_file(tmp_fd, "A/C/fileC1");
66 _create_random_file(tmp_fd, "A/B/D/fileD1");
67 T_ASSERT_POSIX_SUCCESS(mkdirat(tmp_fd, "LINK", 0777), NULL);
68 T_ASSERT_POSIX_SUCCESS(mkdirat(tmp_fd, "LINK/Z", 0777), NULL);
69 _create_random_file(tmp_fd, "LINK/fileL1");
70 _create_random_file(tmp_fd, "LINK/Z/fileZ1");
71 _create_symlink(tmp_path, "LINK", "A/link");
72
73 char original_cwd[MAXPATHLEN];
74 T_ASSERT_NOTNULL(getcwd(original_cwd, sizeof(original_cwd)), NULL);
75
76 struct {
77 const char *cwd;
78 const char *path;
79 int info;
80 bool found;
81 } expected[] = {
82 {
83 .cwd = original_cwd,
84 .path = "A",
85 .info = FTS_D,
86 },
87 {
88 .cwd = "A",
89 .path = "A/fileA2",
90 .info = FTS_F,
91 },
92 {
93 .cwd = "A",
94 .path = "A/B",
95 .info = FTS_D,
96 },
97 {
98 .cwd = "A/B",
99 .path = "A/B/D",
100 .info = FTS_D,
101 },
102 {
103 .cwd = "A/B/D",
104 .path = "A/B/D/fileD1",
105 .info = FTS_F,
106 },
107 {
108 .cwd = "A/B",
109 .path = "A/B/D",
110 .info = FTS_DP,
111 },
112 {
113 .cwd = "A/B",
114 .path = "A/B/fileB1",
115 .info = FTS_F,
116 },
117 {
118 .cwd = "A",
119 .path = "A/B",
120 .info = FTS_DP,
121 },
122 {
123 .cwd = "A",
124 .path = "A/C",
125 .info = FTS_D,
126 },
127 {
128 .cwd = "A/C",
129 .path = "A/C/empty",
130 .info = FTS_D,
131 },
132 {
133 .cwd = "A/C",
134 .path = "A/C/empty",
135 .info = FTS_DP,
136 },
137 {
138 .cwd = "A/C",
139 .path = "A/C/fileC1",
140 .info = FTS_F,
141 },
142 {
143 .cwd = "A",
144 .path = "A/C",
145 .info = FTS_DP,
146 },
147 {
148 .cwd = "A",
149 .path = "A/link",
150 .info = FTS_SL,
151 },
152 {
153 .cwd = "A",
154 .path = "A/link",
155 .info = FTS_D,
156 },
157 {
158 .cwd = "LINK",
159 .path = "A/link/fileL1",
160 .info = FTS_F,
161 },
162 {
163 .cwd = "LINK",
164 .path = "A/link/Z",
165 .info = FTS_D,
166 },
167 {
168 .cwd = "LINK/Z",
169 .path = "A/link/Z/fileZ1",
170 .info = FTS_F,
171 },
172 {
173 .cwd = "LINK",
174 .path = "A/link/Z",
175 .info = FTS_DP,
176 },
177 {
178 .cwd = "A",
179 .path = "A/link",
180 .info = FTS_DP,
181 },
182 {
183 .cwd = "A",
184 .path = "A/fileA1",
185 .info = FTS_F,
186 },
187 {
188 .cwd = original_cwd,
189 .path = "A",
190 .info = FTS_DP,
191 },
192 };
193
194 const char *LABELS[] = {
195 [0] = "None",
196 [FTS_D] = "FTS_D", /* preorder directory */
197 [FTS_DC] = "FTS_DC", /* directory that causes cycles */
198 [FTS_DEFAULT] = "FTS_DEFAULT", /* none of the above */
199 [FTS_DNR] = "FTS_DNR", /* unreadable directory */
200 [FTS_DOT] = "FTS_DOT", /* dot or dot-dot */
201 [FTS_DP] = "FTS_DP", /* postorder directory */
202 [FTS_ERR] = "FTS_ERR", /* error; errno is set */
203 [FTS_F] = "FTS_F", /* regular file */
204 [FTS_INIT] = "FTS_INIT", /* initialized only */
205 [FTS_NS] = "FTS_NS", /* stat(2) failed */
206 [FTS_NSOK] = "FTS_NSOK", /* no stat(2) requested */
207 [FTS_SL] = "FTS_SL", /* symbolic link */
208 [FTS_SLNONE] = "FTS_SLNONE", /* symbolic link without target */
209 };
210
211 char *root_path = NULL;
212 T_ASSERT_POSIX_SUCCESS(asprintf(&root_path, "%s/A", tmp_path), NULL);
213 const char *paths[] = {
214 root_path,
215 NULL,
216 };
217 FTS *tree = fts_open(paths, FTS_PHYSICAL, NULL);
218 T_ASSERT_NOTNULL(tree, NULL);
219 FTSENT *node;
220 int found_count = 0;
221 while ((node = fts_read(tree))) {
222 char cwd[MAXPATHLEN];
223 T_QUIET;
224 T_ASSERT_NOTNULL(getcwd(cwd, sizeof(cwd)), NULL);
225
226 switch (node->fts_info) {
227 case FTS_ERR:
228 T_FAIL("FTS_ERR(%d)", node->fts_errno);
229 break;
230 case FTS_SL:
231 fts_set(tree, node, FTS_FOLLOW);
232 /* fall through */
233 default: {
234 bool found = false;
235 for (size_t index = 0; index < countof(expected) && !found; index++) {
236 if (expected[index].found) {
237 continue;
238 }
239 if (expected[index].info != node->fts_info) {
240 // Wrong type, skip
241 continue;
242 }
243
244 char *expected_path = expected[index].path;
245 char *actual_path = node->fts_path;
246 if (expected_path[0] != '/') {
247 actual_path = _remove_prefix(tmp_path, actual_path);
248 }
249
250 if (strcmp(actual_path, expected_path) == 0) {
251 char *expected_cwd = expected[index].cwd;
252 char *actual_cwd = cwd;
253 if (expected_cwd[0] != '/') {
254 // Relative path
255 actual_cwd = _remove_prefix(tmp_path, actual_cwd);
256 }
257 T_QUIET;
258 T_EXPECT_EQ_STR(actual_cwd, expected_cwd, NULL);
259 found = true;
260 expected[index].found = true;
261 found_count++;
262 }
263 }
264 T_EXPECT_TRUE(found, "path: %s info: %d [%s] cwd: %s", node->fts_path, node->fts_info, LABELS[node->fts_info], cwd);
265 }
266 }
267 }
268 T_QUIET;
269 T_EXPECT_EQ(found_count, countof(expected), NULL);
270 for (size_t index = 0; index < countof(expected); index++) {
271 T_QUIET;
272 T_EXPECT_TRUE(expected[index].found, "missing: path: %s info %d [%s]", expected[index].path, expected[index].info, LABELS[expected[index].info]);
273 }
274 fts_close(tree);
275 free(tmp_path);
276 free(root_path);
277}