Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libc/tests/gen/fts_children_test.c
289379 views
1
/*
2
* Copyright (c) 2026 Jitendra Bhati
3
*
4
* SPDX-License-Identifier: BSD-2-Clause
5
*/
6
7
/*
8
* Tests for fts_children().
9
*/
10
11
#include <sys/stat.h>
12
13
#include <errno.h>
14
#include <fcntl.h>
15
#include <fts.h>
16
#include <stdbool.h>
17
#include <stdio.h>
18
#include <stdlib.h>
19
#include <string.h>
20
#include <unistd.h>
21
22
#include <atf-c.h>
23
24
static int
25
fts_lexical_compar(const FTSENT * const *a, const FTSENT * const *b)
26
{
27
return (strcmp((*a)->fts_name, (*b)->fts_name));
28
}
29
30
/*
31
* fts_children() before fts_read() returns the list of root entries.
32
*/
33
ATF_TC(before_read);
34
ATF_TC_HEAD(before_read, tc)
35
{
36
atf_tc_set_md_var(tc, "descr",
37
"fts_children before fts_read returns root entry list");
38
}
39
ATF_TC_BODY(before_read, tc)
40
{
41
char *paths[] = { "dir", NULL };
42
FTS *fts;
43
FTSENT *children, *p;
44
int count;
45
46
ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
47
ATF_REQUIRE_EQ(0, close(creat("dir/a", 0644)));
48
49
ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
50
51
errno = 0;
52
children = fts_children(fts, 0);
53
ATF_REQUIRE_MSG(children != NULL,
54
"fts_children before fts_read must return the root list");
55
ATF_CHECK_EQ(0, errno);
56
57
count = 0;
58
for (p = children; p != NULL; p = p->fts_link) {
59
ATF_CHECK_EQ_MSG(FTS_D, p->fts_info,
60
"root entry should be FTS_D, got %d", p->fts_info);
61
count++;
62
}
63
ATF_CHECK_EQ_MSG(1, count,
64
"expected 1 root entry, found %d", count);
65
66
ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
67
}
68
69
/*
70
* fts_children() on an empty directory returns NULL with errno == 0.
71
* errno=0 distinguishes "empty" from an actual error.
72
*/
73
ATF_TC(empty_dir);
74
ATF_TC_HEAD(empty_dir, tc)
75
{
76
atf_tc_set_md_var(tc, "descr",
77
"fts_children on empty directory returns NULL with errno 0");
78
}
79
ATF_TC_BODY(empty_dir, tc)
80
{
81
char *paths[] = { "dir", NULL };
82
FTS *fts;
83
FTSENT *ent, *children;
84
85
ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
86
87
ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
88
89
ent = fts_read(fts);
90
ATF_REQUIRE(ent != NULL);
91
ATF_REQUIRE_EQ_MSG(FTS_D, ent->fts_info,
92
"expected FTS_D, got %d", ent->fts_info);
93
94
errno = 1; /* sentinel — fts_children must clear this */
95
children = fts_children(fts, 0);
96
ATF_CHECK_MSG(children == NULL,
97
"fts_children on empty dir must return NULL");
98
ATF_CHECK_EQ_MSG(0, errno,
99
"fts_children on empty dir must set errno=0, got %d", errno);
100
101
ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
102
}
103
104
/*
105
* fts_children() on a non-empty directory returns a linked list of all
106
* children in comparator order.
107
*/
108
ATF_TC(nonempty_dir);
109
ATF_TC_HEAD(nonempty_dir, tc)
110
{
111
atf_tc_set_md_var(tc, "descr",
112
"fts_children on non-empty directory returns all children");
113
}
114
ATF_TC_BODY(nonempty_dir, tc)
115
{
116
static const char *expected[] = { "a", "b", "c", NULL };
117
char *paths[] = { "dir", NULL };
118
FTS *fts;
119
FTSENT *ent, *children, *p;
120
int i;
121
122
ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
123
ATF_REQUIRE_EQ(0, close(creat("dir/a", 0644)));
124
ATF_REQUIRE_EQ(0, close(creat("dir/b", 0644)));
125
ATF_REQUIRE_EQ(0, close(creat("dir/c", 0644)));
126
127
ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL,
128
fts_lexical_compar)) != NULL);
129
130
ent = fts_read(fts);
131
ATF_REQUIRE(ent != NULL);
132
ATF_REQUIRE_EQ(FTS_D, ent->fts_info);
133
134
children = fts_children(fts, 0);
135
ATF_REQUIRE_MSG(children != NULL, "fts_children(): %m");
136
137
i = 0;
138
for (p = children; p != NULL; p = p->fts_link, i++) {
139
ATF_REQUIRE_MSG(expected[i] != NULL,
140
"more children returned than expected");
141
ATF_CHECK_STREQ(expected[i], p->fts_name);
142
ATF_CHECK_EQ(FTS_F, p->fts_info);
143
}
144
ATF_CHECK_MSG(expected[i] == NULL,
145
"fewer children returned than expected");
146
147
ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
148
}
149
150
/*
151
* fts_children() called twice on the same FTS_D node must return an
152
* equivalent list both times.
153
*/
154
ATF_TC(called_twice);
155
ATF_TC_HEAD(called_twice, tc)
156
{
157
atf_tc_set_md_var(tc, "descr",
158
"fts_children called twice returns equivalent results");
159
}
160
ATF_TC_BODY(called_twice, tc)
161
{
162
char *paths[] = { "dir", NULL };
163
FTS *fts;
164
FTSENT *ent, *first, *second, *p;
165
int count1, count2;
166
167
ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
168
ATF_REQUIRE_EQ(0, close(creat("dir/x", 0644)));
169
ATF_REQUIRE_EQ(0, close(creat("dir/y", 0644)));
170
171
ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
172
173
ent = fts_read(fts);
174
ATF_REQUIRE(ent != NULL);
175
ATF_REQUIRE_EQ(FTS_D, ent->fts_info);
176
177
first = fts_children(fts, 0);
178
ATF_REQUIRE_MSG(first != NULL, "first fts_children call: %m");
179
180
count1 = 0;
181
for (p = first; p != NULL; p = p->fts_link)
182
count1++;
183
184
/*
185
* The second call frees the first list and rebuilds. Do not
186
* dereference 'first' after this point — it has been freed.
187
*/
188
second = fts_children(fts, 0);
189
ATF_REQUIRE_MSG(second != NULL, "second fts_children call: %m");
190
191
count2 = 0;
192
for (p = second; p != NULL; p = p->fts_link)
193
count2++;
194
195
ATF_CHECK_EQ_MSG(count1, count2,
196
"first call returned %d children, second returned %d",
197
count1, count2);
198
ATF_CHECK_EQ(2, count2);
199
200
ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
201
}
202
203
/*
204
* fts_children(FTS_NAMEONLY): only fts_name and fts_namelen are filled.
205
* fts_info is FTS_NSOK for every entry.
206
*/
207
ATF_TC(nameonly);
208
ATF_TC_HEAD(nameonly, tc)
209
{
210
atf_tc_set_md_var(tc, "descr",
211
"FTS_NAMEONLY fills only fts_name, fts_info is FTS_NSOK");
212
}
213
ATF_TC_BODY(nameonly, tc)
214
{
215
char *paths[] = { "dir", NULL };
216
FTS *fts;
217
FTSENT *ent, *children, *p;
218
int count;
219
220
ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
221
ATF_REQUIRE_EQ(0, close(creat("dir/f1", 0644)));
222
ATF_REQUIRE_EQ(0, close(creat("dir/f2", 0644)));
223
224
ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
225
226
ent = fts_read(fts);
227
ATF_REQUIRE(ent != NULL);
228
ATF_REQUIRE_EQ(FTS_D, ent->fts_info);
229
230
children = fts_children(fts, FTS_NAMEONLY);
231
ATF_REQUIRE_MSG(children != NULL, "fts_children(FTS_NAMEONLY): %m");
232
233
count = 0;
234
for (p = children; p != NULL; p = p->fts_link) {
235
ATF_CHECK_MSG(p->fts_name[0] != '\0',
236
"FTS_NAMEONLY: fts_name is empty");
237
ATF_CHECK_EQ(strlen(p->fts_name), p->fts_namelen);
238
ATF_CHECK_EQ_MSG(FTS_NSOK, p->fts_info,
239
"FTS_NAMEONLY: expected FTS_NSOK, got %d", p->fts_info);
240
count++;
241
}
242
ATF_CHECK_EQ(2, count);
243
244
/* Normal traversal must still work after FTS_NAMEONLY. */
245
while (fts_read(fts) != NULL)
246
;
247
ATF_CHECK_EQ_MSG(0, errno,
248
"traversal after FTS_NAMEONLY ended with errno %d", errno);
249
250
ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
251
}
252
253
/*
254
* fts_children() on a non-directory node must return NULL with errno == 0.
255
*/
256
ATF_TC(nondirectory);
257
ATF_TC_HEAD(nondirectory, tc)
258
{
259
atf_tc_set_md_var(tc, "descr",
260
"fts_children on a non-directory node returns NULL with errno 0");
261
}
262
ATF_TC_BODY(nondirectory, tc)
263
{
264
char *paths[] = { "dir", NULL };
265
FTS *fts;
266
FTSENT *ent;
267
268
ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
269
ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
270
271
ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
272
273
ent = fts_read(fts); /* FTS_D dir */
274
ATF_REQUIRE(ent != NULL);
275
ATF_REQUIRE_EQ(FTS_D, ent->fts_info);
276
277
ent = fts_read(fts); /* FTS_F file */
278
ATF_REQUIRE(ent != NULL);
279
ATF_REQUIRE_EQ(FTS_F, ent->fts_info);
280
281
errno = 1;
282
ATF_CHECK_MSG(fts_children(fts, 0) == NULL,
283
"fts_children on FTS_F must return NULL");
284
ATF_CHECK_EQ_MSG(0, errno,
285
"fts_children on FTS_F must set errno=0, got %d", errno);
286
287
ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
288
}
289
290
/*
291
* fts_children() with an invalid options value must return NULL with
292
* errno == EINVAL.
293
*/
294
ATF_TC(invalid_options);
295
ATF_TC_HEAD(invalid_options, tc)
296
{
297
atf_tc_set_md_var(tc, "descr",
298
"fts_children with invalid options returns NULL with EINVAL");
299
}
300
ATF_TC_BODY(invalid_options, tc)
301
{
302
char *paths[] = { ".", NULL };
303
FTS *fts;
304
305
ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);
306
307
ATF_REQUIRE_ERRNO(EINVAL, fts_children(fts, 99) == NULL);
308
309
ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");
310
}
311
312
ATF_TP_ADD_TCS(tp)
313
{
314
ATF_TP_ADD_TC(tp, before_read);
315
ATF_TP_ADD_TC(tp, empty_dir);
316
ATF_TP_ADD_TC(tp, nonempty_dir);
317
ATF_TP_ADD_TC(tp, called_twice);
318
ATF_TP_ADD_TC(tp, nameonly);
319
ATF_TP_ADD_TC(tp, nondirectory);
320
ATF_TP_ADD_TC(tp, invalid_options);
321
322
return (atf_no_error());
323
}
324
325