diff --git a/lib/libc/tests/gen/Makefile b/lib/libc/tests/gen/Makefile index 2aff14f078bc..006512d30dc4 100644 --- a/lib/libc/tests/gen/Makefile +++ b/lib/libc/tests/gen/Makefile @@ -1,118 +1,121 @@ .include ATF_TESTS_C+= arc4random_test ATF_TESTS_C+= dir2_test ATF_TESTS_C+= dlopen_empty_test ATF_TESTS_C+= fmtcheck2_test ATF_TESTS_C+= fmtmsg_test ATF_TESTS_C+= fnmatch2_test ATF_TESTS_C+= fpclassify2_test .if ${COMPILER_FEATURES:Mblocks} ATF_TESTS_C+= fts_blocks_test .endif ATF_TESTS_C+= fts_options_test ATF_TESTS_C+= ftw_test ATF_TESTS_C+= getentropy_test ATF_TESTS_C+= getmntinfo_test ATF_TESTS_C+= glob2_test +.if ${COMPILER_FEATURES:Mblocks} +ATF_TESTS_C+= glob_blocks_test +.endif ATF_TESTS_C+= makecontext_test ATF_TESTS_C+= popen_test ATF_TESTS_C+= posix_spawn_test ATF_TESTS_C+= realpath2_test ATF_TESTS_C+= sigsetops_test ATF_TESTS_C+= wordexp_test # TODO: t_closefrom, t_fmtcheck, t_randomid, # TODO: t_siginfo (fixes require further inspection) # TODO: t_sethostname_test (consistently screws up the hostname) FILESGROUPS+= posix_spawn_test_FILES posix_spawn_test_FILES= spawnp_enoexec.sh posix_spawn_test_FILESDIR= ${TESTSDIR} posix_spawn_test_FILESMODE= 0755 posix_spawn_test_FILESOWN= root posix_spawn_test_FILESGRP= wheel posix_spawn_test_FILESPACKAGE= ${PACKAGE} CFLAGS+= -DTEST_LONG_DOUBLE # Define __HAVE_LONG_DOUBLE for architectures whose long double has greater # precision than their double. .if ${MACHINE_CPUARCH} == "aarch64" || \ ${MACHINE_CPUARCH} == "amd64" || \ ${MACHINE_CPUARCH} == "i386" || \ ${MACHINE_CPUARCH} == "riscv" CFLAGS+= -D__HAVE_LONG_DOUBLE .endif NETBSD_ATF_TESTS_C= alarm_test NETBSD_ATF_TESTS_C+= assert_test NETBSD_ATF_TESTS_C+= basedirname_test NETBSD_ATF_TESTS_C+= cpuset_test NETBSD_ATF_TESTS_C+= dir_test NETBSD_ATF_TESTS_C+= floatunditf_test NETBSD_ATF_TESTS_C+= fnmatch_test NETBSD_ATF_TESTS_C+= fpclassify_test NETBSD_ATF_TESTS_C+= fpsetmask_test NETBSD_ATF_TESTS_C+= fpsetround_test NETBSD_ATF_TESTS_C+= ftok_test NETBSD_ATF_TESTS_C+= getcwd_test NETBSD_ATF_TESTS_C+= getgrent_test NETBSD_ATF_TESTS_C+= glob_test NETBSD_ATF_TESTS_C+= humanize_number_test NETBSD_ATF_TESTS_C+= isnan_test NETBSD_ATF_TESTS_C+= nice_test NETBSD_ATF_TESTS_C+= pause_test NETBSD_ATF_TESTS_C+= raise_test NETBSD_ATF_TESTS_C+= realpath_test NETBSD_ATF_TESTS_C+= setdomainname_test NETBSD_ATF_TESTS_C+= sethostname_test NETBSD_ATF_TESTS_C+= sleep_test NETBSD_ATF_TESTS_C+= syslog_test NETBSD_ATF_TESTS_C+= time_test NETBSD_ATF_TESTS_C+= ttyname_test NETBSD_ATF_TESTS_C+= vis_test .include "../Makefile.netbsd-tests" CFLAGS.getentropy_test+= -I${SRCTOP}/include LIBADD.getentropy_test+= c LIBADD.humanize_number_test+= util LIBADD.fpclassify_test+=m LIBADD.fpsetround_test+=m LIBADD.siginfo_test+= m LIBADD.nice_test+= pthread LIBADD.syslog_test+= pthread CFLAGS+= -I${.CURDIR} SRCS.fmtcheck2_test= fmtcheck_test.c SRCS.fnmatch2_test= fnmatch_test.c TEST_METADATA.setdomainname_test+= is_exclusive=true TESTS_SUBDIRS= execve TESTS_SUBDIRS+= posix_spawn # Tests that require blocks support -.for t in fts_blocks_test +.for t in fts_blocks_test glob_blocks_test CFLAGS.${t}.c+= -fblocks LIBADD.${t}+= BlocksRuntime .endfor # The old testcase name TEST_FNMATCH= test-fnmatch CLEANFILES+= ${GEN_SH_CASE_TESTCASES} sh-tests: .PHONY .for target in clean obj depend all @cd ${.CURDIR} && ${MAKE} PROG=${TEST_FNMATCH} \ -DNO_SUBDIR ${target} .endfor @cd ${.OBJDIR} && ./${TEST_FNMATCH} -s 1 > \ ${SRCTOP}/bin/sh/tests/builtins/case2.0 @cd ${.OBJDIR} && ./${TEST_FNMATCH} -s 2 > \ ${SRCTOP}/bin/sh/tests/builtins/case3.0 .include diff --git a/lib/libc/tests/gen/glob2_test.c b/lib/libc/tests/gen/glob2_test.c index 8f69c36fece7..45d17b063593 100644 --- a/lib/libc/tests/gen/glob2_test.c +++ b/lib/libc/tests/gen/glob2_test.c @@ -1,111 +1,169 @@ /* * Copyright (c) 2017 Dell EMC Isilon * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include +#include + #include #include #include #include #include #include #include #include #include +static int glob_callback_invoked; + /* * Derived from Russ Cox' pathological case test program used for the * https://research.swtch.com/glob article. */ ATF_TC_WITHOUT_HEAD(glob_pathological_test); ATF_TC_BODY(glob_pathological_test, tc) { struct timespec t, t2; glob_t g; const char *longname = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; char pattern[1000], *p; double dt; unsigned i, j, k, mul; int fd, rc; fd = open(longname, O_CREAT | O_RDWR, 0666); ATF_REQUIRE(fd >= 0); /* * Test up to 100 a* groups. Exponential implementations typically go * bang at i=7 or 8. */ for (i = 0; i < 100; i++) { /* * Create a*...b pattern with i 'a*' groups. */ p = pattern; for (k = 0; k < i; k++) { *p++ = 'a'; *p++ = '*'; } *p++ = 'b'; *p = '\0'; clock_gettime(CLOCK_REALTIME, &t); for (j = 0; j < mul; j++) { memset(&g, 0, sizeof g); rc = glob(pattern, 0, 0, &g); if (rc == GLOB_NOSPACE || rc == GLOB_ABORTED) { ATF_REQUIRE_MSG(rc == GLOB_NOMATCH, "an unexpected error occurred: " "rc=%d errno=%d", rc, errno); /* NORETURN */ } ATF_CHECK_MSG(rc == GLOB_NOMATCH, "A bogus match occurred: '%s' ~ '%s'", pattern, g.gl_pathv[0]); globfree(&g); } clock_gettime(CLOCK_REALTIME, &t2); t2.tv_sec -= t.tv_sec; t2.tv_nsec -= t.tv_nsec; dt = t2.tv_sec + (double)t2.tv_nsec/1e9; dt /= mul; ATF_CHECK_MSG(dt < 1, "glob(3) took far too long: %d %.9f", i, dt); if (dt >= 0.0001) mul = 1; } } +ATF_TC(glob_period); +ATF_TC_HEAD(glob_period, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Test behaviour when matching files that start with a period" + "(documented in the glob(3) CAVEATS section)."); +} +ATF_TC_BODY(glob_period, tc) +{ + int i; + glob_t g; + + atf_utils_create_file(".test", ""); + glob(".", 0, NULL, &g); + ATF_REQUIRE_MSG(g.gl_matchc == 1, + "glob(3) shouldn't match files starting with a period when using '.'"); + for (i = 0; i < g.gl_matchc; i++) + printf("%s\n", g.gl_pathv[i]); + glob(".*", 0, NULL, &g); + ATF_REQUIRE_MSG(g.gl_matchc == 3 && strcmp(g.gl_pathv[2], ".test") == 0, + "glob(3) should match files starting with a period when using '.*'"); +} + +static int +errfunc(const char *path, int err) +{ + ATF_REQUIRE_STREQ(path, "test/"); + ATF_REQUIRE(err == EACCES); + glob_callback_invoked = 1; + /* Suppress EACCES errors. */ + return (0); +} + +ATF_TC_WITHOUT_HEAD(glob_callback_test); +ATF_TC_BODY(glob_callback_test, tc) +{ + int rv; + glob_t g; + + glob_callback_invoked = 0; + ATF_REQUIRE_EQ(0, mkdir("test", 0007)); + rv = glob("test/*", 0, errfunc, &g); + ATF_REQUIRE_MSG(glob_callback_invoked == 1, + "glob(3) failed to invoke callback function"); + ATF_REQUIRE_MSG(rv == GLOB_NOMATCH, + "error callback function failed to suppress EACCES"); + + /* GLOB_ERR should ignore the suppressed error. */ + rv = glob("test/*", GLOB_ERR, errfunc, &g); + ATF_REQUIRE_MSG(rv == GLOB_ABORTED, + "GLOB_ERR didn't override error callback function"); +} + ATF_TP_ADD_TCS(tp) { ATF_TP_ADD_TC(tp, glob_pathological_test); - + ATF_TP_ADD_TC(tp, glob_period); + ATF_TP_ADD_TC(tp, glob_callback_test); return (atf_no_error()); } diff --git a/lib/libc/tests/gen/glob_blocks_test.c b/lib/libc/tests/gen/glob_blocks_test.c new file mode 100644 index 000000000000..a20aad17ce31 --- /dev/null +++ b/lib/libc/tests/gen/glob_blocks_test.c @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +#include +#include + +#include + +static int glob_callback_invoked; + +ATF_TC_WITHOUT_HEAD(glob_b_callback_test); +ATF_TC_BODY(glob_b_callback_test, tc) +{ + int rv; + glob_t g; + + glob_callback_invoked = 0; + ATF_REQUIRE_EQ(0, mkdir("test", 0007)); + int (^errblk)(const char *, int) = + ^(const char *path, int err) { + ATF_REQUIRE_STREQ(path, "test/"); + ATF_REQUIRE(err == EACCES); + glob_callback_invoked = 1; + /* Suppress EACCES errors. */ + return (0); + }; + + rv = glob_b("test/*", 0, errblk, &g); + ATF_REQUIRE_MSG(glob_callback_invoked == 1, + "glob(3) failed to invoke callback block"); + ATF_REQUIRE_MSG(rv == GLOB_NOMATCH, + "error callback function failed to suppress EACCES"); + + /* GLOB_ERR should ignore the suppressed error. */ + rv = glob_b("test/*", GLOB_ERR, errblk, &g); + ATF_REQUIRE_MSG(rv == GLOB_ABORTED, + "GLOB_ERR didn't override error callback block"); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, glob_b_callback_test); + return (atf_no_error()); +}