Path: blob/main/lib/libc/tests/gen/fts_children_test.c
289379 views
/*1* Copyright (c) 2026 Jitendra Bhati2*3* SPDX-License-Identifier: BSD-2-Clause4*/56/*7* Tests for fts_children().8*/910#include <sys/stat.h>1112#include <errno.h>13#include <fcntl.h>14#include <fts.h>15#include <stdbool.h>16#include <stdio.h>17#include <stdlib.h>18#include <string.h>19#include <unistd.h>2021#include <atf-c.h>2223static int24fts_lexical_compar(const FTSENT * const *a, const FTSENT * const *b)25{26return (strcmp((*a)->fts_name, (*b)->fts_name));27}2829/*30* fts_children() before fts_read() returns the list of root entries.31*/32ATF_TC(before_read);33ATF_TC_HEAD(before_read, tc)34{35atf_tc_set_md_var(tc, "descr",36"fts_children before fts_read returns root entry list");37}38ATF_TC_BODY(before_read, tc)39{40char *paths[] = { "dir", NULL };41FTS *fts;42FTSENT *children, *p;43int count;4445ATF_REQUIRE_EQ(0, mkdir("dir", 0755));46ATF_REQUIRE_EQ(0, close(creat("dir/a", 0644)));4748ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);4950errno = 0;51children = fts_children(fts, 0);52ATF_REQUIRE_MSG(children != NULL,53"fts_children before fts_read must return the root list");54ATF_CHECK_EQ(0, errno);5556count = 0;57for (p = children; p != NULL; p = p->fts_link) {58ATF_CHECK_EQ_MSG(FTS_D, p->fts_info,59"root entry should be FTS_D, got %d", p->fts_info);60count++;61}62ATF_CHECK_EQ_MSG(1, count,63"expected 1 root entry, found %d", count);6465ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");66}6768/*69* fts_children() on an empty directory returns NULL with errno == 0.70* errno=0 distinguishes "empty" from an actual error.71*/72ATF_TC(empty_dir);73ATF_TC_HEAD(empty_dir, tc)74{75atf_tc_set_md_var(tc, "descr",76"fts_children on empty directory returns NULL with errno 0");77}78ATF_TC_BODY(empty_dir, tc)79{80char *paths[] = { "dir", NULL };81FTS *fts;82FTSENT *ent, *children;8384ATF_REQUIRE_EQ(0, mkdir("dir", 0755));8586ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);8788ent = fts_read(fts);89ATF_REQUIRE(ent != NULL);90ATF_REQUIRE_EQ_MSG(FTS_D, ent->fts_info,91"expected FTS_D, got %d", ent->fts_info);9293errno = 1; /* sentinel — fts_children must clear this */94children = fts_children(fts, 0);95ATF_CHECK_MSG(children == NULL,96"fts_children on empty dir must return NULL");97ATF_CHECK_EQ_MSG(0, errno,98"fts_children on empty dir must set errno=0, got %d", errno);99100ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");101}102103/*104* fts_children() on a non-empty directory returns a linked list of all105* children in comparator order.106*/107ATF_TC(nonempty_dir);108ATF_TC_HEAD(nonempty_dir, tc)109{110atf_tc_set_md_var(tc, "descr",111"fts_children on non-empty directory returns all children");112}113ATF_TC_BODY(nonempty_dir, tc)114{115static const char *expected[] = { "a", "b", "c", NULL };116char *paths[] = { "dir", NULL };117FTS *fts;118FTSENT *ent, *children, *p;119int i;120121ATF_REQUIRE_EQ(0, mkdir("dir", 0755));122ATF_REQUIRE_EQ(0, close(creat("dir/a", 0644)));123ATF_REQUIRE_EQ(0, close(creat("dir/b", 0644)));124ATF_REQUIRE_EQ(0, close(creat("dir/c", 0644)));125126ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL,127fts_lexical_compar)) != NULL);128129ent = fts_read(fts);130ATF_REQUIRE(ent != NULL);131ATF_REQUIRE_EQ(FTS_D, ent->fts_info);132133children = fts_children(fts, 0);134ATF_REQUIRE_MSG(children != NULL, "fts_children(): %m");135136i = 0;137for (p = children; p != NULL; p = p->fts_link, i++) {138ATF_REQUIRE_MSG(expected[i] != NULL,139"more children returned than expected");140ATF_CHECK_STREQ(expected[i], p->fts_name);141ATF_CHECK_EQ(FTS_F, p->fts_info);142}143ATF_CHECK_MSG(expected[i] == NULL,144"fewer children returned than expected");145146ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");147}148149/*150* fts_children() called twice on the same FTS_D node must return an151* equivalent list both times.152*/153ATF_TC(called_twice);154ATF_TC_HEAD(called_twice, tc)155{156atf_tc_set_md_var(tc, "descr",157"fts_children called twice returns equivalent results");158}159ATF_TC_BODY(called_twice, tc)160{161char *paths[] = { "dir", NULL };162FTS *fts;163FTSENT *ent, *first, *second, *p;164int count1, count2;165166ATF_REQUIRE_EQ(0, mkdir("dir", 0755));167ATF_REQUIRE_EQ(0, close(creat("dir/x", 0644)));168ATF_REQUIRE_EQ(0, close(creat("dir/y", 0644)));169170ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);171172ent = fts_read(fts);173ATF_REQUIRE(ent != NULL);174ATF_REQUIRE_EQ(FTS_D, ent->fts_info);175176first = fts_children(fts, 0);177ATF_REQUIRE_MSG(first != NULL, "first fts_children call: %m");178179count1 = 0;180for (p = first; p != NULL; p = p->fts_link)181count1++;182183/*184* The second call frees the first list and rebuilds. Do not185* dereference 'first' after this point — it has been freed.186*/187second = fts_children(fts, 0);188ATF_REQUIRE_MSG(second != NULL, "second fts_children call: %m");189190count2 = 0;191for (p = second; p != NULL; p = p->fts_link)192count2++;193194ATF_CHECK_EQ_MSG(count1, count2,195"first call returned %d children, second returned %d",196count1, count2);197ATF_CHECK_EQ(2, count2);198199ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");200}201202/*203* fts_children(FTS_NAMEONLY): only fts_name and fts_namelen are filled.204* fts_info is FTS_NSOK for every entry.205*/206ATF_TC(nameonly);207ATF_TC_HEAD(nameonly, tc)208{209atf_tc_set_md_var(tc, "descr",210"FTS_NAMEONLY fills only fts_name, fts_info is FTS_NSOK");211}212ATF_TC_BODY(nameonly, tc)213{214char *paths[] = { "dir", NULL };215FTS *fts;216FTSENT *ent, *children, *p;217int count;218219ATF_REQUIRE_EQ(0, mkdir("dir", 0755));220ATF_REQUIRE_EQ(0, close(creat("dir/f1", 0644)));221ATF_REQUIRE_EQ(0, close(creat("dir/f2", 0644)));222223ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);224225ent = fts_read(fts);226ATF_REQUIRE(ent != NULL);227ATF_REQUIRE_EQ(FTS_D, ent->fts_info);228229children = fts_children(fts, FTS_NAMEONLY);230ATF_REQUIRE_MSG(children != NULL, "fts_children(FTS_NAMEONLY): %m");231232count = 0;233for (p = children; p != NULL; p = p->fts_link) {234ATF_CHECK_MSG(p->fts_name[0] != '\0',235"FTS_NAMEONLY: fts_name is empty");236ATF_CHECK_EQ(strlen(p->fts_name), p->fts_namelen);237ATF_CHECK_EQ_MSG(FTS_NSOK, p->fts_info,238"FTS_NAMEONLY: expected FTS_NSOK, got %d", p->fts_info);239count++;240}241ATF_CHECK_EQ(2, count);242243/* Normal traversal must still work after FTS_NAMEONLY. */244while (fts_read(fts) != NULL)245;246ATF_CHECK_EQ_MSG(0, errno,247"traversal after FTS_NAMEONLY ended with errno %d", errno);248249ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");250}251252/*253* fts_children() on a non-directory node must return NULL with errno == 0.254*/255ATF_TC(nondirectory);256ATF_TC_HEAD(nondirectory, tc)257{258atf_tc_set_md_var(tc, "descr",259"fts_children on a non-directory node returns NULL with errno 0");260}261ATF_TC_BODY(nondirectory, tc)262{263char *paths[] = { "dir", NULL };264FTS *fts;265FTSENT *ent;266267ATF_REQUIRE_EQ(0, mkdir("dir", 0755));268ATF_REQUIRE_EQ(0, close(creat("dir/file", 0644)));269270ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);271272ent = fts_read(fts); /* FTS_D dir */273ATF_REQUIRE(ent != NULL);274ATF_REQUIRE_EQ(FTS_D, ent->fts_info);275276ent = fts_read(fts); /* FTS_F file */277ATF_REQUIRE(ent != NULL);278ATF_REQUIRE_EQ(FTS_F, ent->fts_info);279280errno = 1;281ATF_CHECK_MSG(fts_children(fts, 0) == NULL,282"fts_children on FTS_F must return NULL");283ATF_CHECK_EQ_MSG(0, errno,284"fts_children on FTS_F must set errno=0, got %d", errno);285286ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");287}288289/*290* fts_children() with an invalid options value must return NULL with291* errno == EINVAL.292*/293ATF_TC(invalid_options);294ATF_TC_HEAD(invalid_options, tc)295{296atf_tc_set_md_var(tc, "descr",297"fts_children with invalid options returns NULL with EINVAL");298}299ATF_TC_BODY(invalid_options, tc)300{301char *paths[] = { ".", NULL };302FTS *fts;303304ATF_REQUIRE((fts = fts_open(paths, FTS_PHYSICAL, NULL)) != NULL);305306ATF_REQUIRE_ERRNO(EINVAL, fts_children(fts, 99) == NULL);307308ATF_REQUIRE_EQ_MSG(0, fts_close(fts), "fts_close(): %m");309}310311ATF_TP_ADD_TCS(tp)312{313ATF_TP_ADD_TC(tp, before_read);314ATF_TP_ADD_TC(tp, empty_dir);315ATF_TP_ADD_TC(tp, nonempty_dir);316ATF_TP_ADD_TC(tp, called_twice);317ATF_TP_ADD_TC(tp, nameonly);318ATF_TP_ADD_TC(tp, nondirectory);319ATF_TP_ADD_TC(tp, invalid_options);320321return (atf_no_error());322}323324325