xref: /freebsd/lib/libc/tests/gen/scandir_test.c (revision b670c9bafc0e31c7609969bf374b2e80bdc00211)
1 /*-
2  * Copyright (c) 2025 Klara, Inc.
3  *
4  * SPDX-License-Identifier: BSD-2-Clause
5  */
6 
7 #include <sys/stat.h>
8 
9 #include <dirent.h>
10 #include <fcntl.h>
11 #include <stdlib.h>
12 
13 #include <atf-c.h>
14 
15 static void
16 scandir_prepare(const struct atf_tc *tc)
17 {
18 	ATF_REQUIRE_EQ(0, mkdir("dir", 0755));
19 	ATF_REQUIRE_EQ(0, mkdir("dir/dir", 0755));
20 	ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));
21 	ATF_REQUIRE_EQ(0, symlink("file", "dir/link"));
22 	ATF_REQUIRE_EQ(0, mkdir("dir/skip", 0755));
23 }
24 
25 static void
26 scandir_verify(const struct atf_tc *tc, int n, struct dirent **namelist)
27 {
28 	ATF_REQUIRE_EQ_MSG(5, n, "return value is %d", n);
29 	ATF_CHECK_STREQ("link", namelist[0]->d_name);
30 	ATF_CHECK_STREQ("file", namelist[1]->d_name);
31 	ATF_CHECK_STREQ("dir", namelist[2]->d_name);
32 	ATF_CHECK_STREQ("..", namelist[3]->d_name);
33 	ATF_CHECK_STREQ(".", namelist[4]->d_name);
34 }
35 
36 static int
37 scandir_select(const struct dirent *ent)
38 {
39 	return (strcmp(ent->d_name, "skip") != 0);
40 }
41 
42 static int
43 scandir_compare(const struct dirent **a, const struct dirent **b)
44 {
45 	return (strcmp((*b)->d_name, (*a)->d_name));
46 }
47 
48 ATF_TC(scandir_test);
49 ATF_TC_HEAD(scandir_test, tc)
50 {
51 	atf_tc_set_md_var(tc, "descr", "Test scandir()");
52 }
53 ATF_TC_BODY(scandir_test, tc)
54 {
55 	struct dirent **namelist = NULL;
56 	int i, ret;
57 
58 	scandir_prepare(tc);
59 	ret = scandir("dir", &namelist, scandir_select, scandir_compare);
60 	scandir_verify(tc, ret, namelist);
61 	for (i = 0; i < ret; i++)
62 		free(namelist[i]);
63 	free(namelist);
64 }
65 
66 ATF_TC(fscandir_test);
67 ATF_TC_HEAD(fscandir_test, tc)
68 {
69 	atf_tc_set_md_var(tc, "descr", "Test fscandir()");
70 }
71 ATF_TC_BODY(fscandir_test, tc)
72 {
73 	struct dirent **namelist = NULL;
74 	int fd, i, ret;
75 
76 	scandir_prepare(tc);
77 	ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0);
78 	ret = fscandir(fd, &namelist, scandir_select, scandir_compare);
79 	scandir_verify(tc, ret, namelist);
80 	for (i = 0; i < ret; i++)
81 		free(namelist[i]);
82 	free(namelist);
83 	ATF_REQUIRE_EQ(0, close(fd));
84 }
85 
86 ATF_TC(scandirat_test);
87 ATF_TC_HEAD(scandirat_test, tc)
88 {
89 	atf_tc_set_md_var(tc, "descr", "Test scandirat()");
90 }
91 ATF_TC_BODY(scandirat_test, tc)
92 {
93 	struct dirent **namelist = NULL;
94 	int fd, i, ret;
95 
96 	scandir_prepare(tc);
97 	ATF_REQUIRE((fd = open("dir", O_DIRECTORY | O_SEARCH)) >= 0);
98 	ret = scandirat(fd, ".", &namelist, scandir_select, scandir_compare);
99 	scandir_verify(tc, ret, namelist);
100 	for (i = 0; i < ret; i++)
101 		free(namelist[i]);
102 	free(namelist);
103 	ATF_REQUIRE_EQ(0, close(fd));
104 }
105 
106 static int
107 scandir_none(const struct dirent *ent __unused)
108 {
109 	return (0);
110 }
111 
112 ATF_TC(scandir_none);
113 ATF_TC_HEAD(scandir_none, tc)
114 {
115 	atf_tc_set_md_var(tc, "descr",
116 	    "Test scandir() when no entries are selected");
117 }
118 ATF_TC_BODY(scandir_none, tc)
119 {
120 	struct dirent **namelist = NULL;
121 
122 	ATF_REQUIRE_EQ(0, scandir(".", &namelist, scandir_none, alphasort));
123 	ATF_REQUIRE(namelist);
124 	free(namelist);
125 }
126 
127 ATF_TP_ADD_TCS(tp)
128 {
129 	ATF_TP_ADD_TC(tp, scandir_test);
130 	ATF_TP_ADD_TC(tp, fscandir_test);
131 	ATF_TP_ADD_TC(tp, scandirat_test);
132 	ATF_TP_ADD_TC(tp, scandir_none);
133 	return (atf_no_error());
134 }
135