summaryrefslogtreecommitdiffstats
path: root/unit/atf-src/atf-c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:37:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-05 18:37:14 +0000
commitea648e70a989cca190cd7403fe892fd2dcc290b4 (patch)
treee2b6b1c647da68b0d4d66082835e256eb30970e8 /unit/atf-src/atf-c
parentInitial commit. (diff)
downloadbind9-ea648e70a989cca190cd7403fe892fd2dcc290b4.tar.xz
bind9-ea648e70a989cca190cd7403fe892fd2dcc290b4.zip
Adding upstream version 1:9.11.5.P4+dfsg.upstream/1%9.11.5.P4+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'unit/atf-src/atf-c')
-rw-r--r--unit/atf-src/atf-c/Atffile15
-rw-r--r--unit/atf-src/atf-c/Kyuafile15
-rw-r--r--unit/atf-src/atf-c/Makefile.am.inc145
-rw-r--r--unit/atf-src/atf-c/atf-c.3793
-rw-r--r--unit/atf-src/atf-c/atf-c.m444
-rw-r--r--unit/atf-src/atf-c/atf-c.pc.in11
-rw-r--r--unit/atf-src/atf-c/atf-common.m488
-rw-r--r--unit/atf-src/atf-c/atf_c_test.c46
-rw-r--r--unit/atf-src/atf-c/build.c282
-rw-r--r--unit/atf-src/atf-c/build.h38
-rw-r--r--unit/atf-src/atf-c/build_test.c249
-rw-r--r--unit/atf-src/atf-c/check.c484
-rw-r--r--unit/atf-src/atf-c/check.h69
-rw-r--r--unit/atf-src/atf-c/check_test.c521
-rw-r--r--unit/atf-src/atf-c/defs.h.in33
-rw-r--r--unit/atf-src/atf-c/detail/Atffile13
-rw-r--r--unit/atf-src/atf-c/detail/Kyuafile13
-rw-r--r--unit/atf-src/atf-c/detail/Makefile.am.inc99
-rw-r--r--unit/atf-src/atf-c/detail/dynstr.c394
-rw-r--r--unit/atf-src/atf-c/detail/dynstr.h77
-rw-r--r--unit/atf-src/atf-c/detail/dynstr_test.c634
-rw-r--r--unit/atf-src/atf-c/detail/env.c114
-rw-r--r--unit/atf-src/atf-c/detail/env.h39
-rw-r--r--unit/atf-src/atf-c/detail/env_test.c133
-rw-r--r--unit/atf-src/atf-c/detail/fs.c884
-rw-r--r--unit/atf-src/atf-c/detail/fs.h128
-rw-r--r--unit/atf-src/atf-c/detail/fs_test.c1079
-rw-r--r--unit/atf-src/atf-c/detail/list.c388
-rw-r--r--unit/atf-src/atf-c/detail/list.h111
-rw-r--r--unit/atf-src/atf-c/detail/list_test.c365
-rw-r--r--unit/atf-src/atf-c/detail/map.c380
-rw-r--r--unit/atf-src/atf-c/detail/map.h114
-rw-r--r--unit/atf-src/atf-c/detail/map_test.c421
-rw-r--r--unit/atf-src/atf-c/detail/process.c670
-rw-r--r--unit/atf-src/atf-c/detail/process.h131
-rw-r--r--unit/atf-src/atf-c/detail/process_helpers.c113
-rw-r--r--unit/atf-src/atf-c/detail/process_test.c1159
-rw-r--r--unit/atf-src/atf-c/detail/sanity.c74
-rw-r--r--unit/atf-src/atf-c/detail/sanity.h72
-rw-r--r--unit/atf-src/atf-c/detail/sanity_test.c230
-rw-r--r--unit/atf-src/atf-c/detail/test_helpers.c147
-rw-r--r--unit/atf-src/atf-c/detail/test_helpers.h75
-rw-r--r--unit/atf-src/atf-c/detail/text.c180
-rw-r--r--unit/atf-src/atf-c/detail/text.h44
-rw-r--r--unit/atf-src/atf-c/detail/text_test.c421
-rw-r--r--unit/atf-src/atf-c/detail/tp_main.c615
-rw-r--r--unit/atf-src/atf-c/detail/user.c75
-rw-r--r--unit/atf-src/atf-c/detail/user.h45
-rw-r--r--unit/atf-src/atf-c/detail/user_test.c146
-rw-r--r--unit/atf-src/atf-c/detail/version_helper.c43
-rw-r--r--unit/atf-src/atf-c/error.c263
-rw-r--r--unit/atf-src/atf-c/error.h67
-rw-r--r--unit/atf-src/atf-c/error_fwd.h36
-rw-r--r--unit/atf-src/atf-c/error_test.c297
-rw-r--r--unit/atf-src/atf-c/h_build.h410
-rw-r--r--unit/atf-src/atf-c/macros.h212
-rw-r--r--unit/atf-src/atf-c/macros_h_test.c102
-rw-r--r--unit/atf-src/atf-c/macros_test.c893
-rw-r--r--unit/atf-src/atf-c/pkg_config_test.sh143
-rw-r--r--unit/atf-src/atf-c/tc.c1219
-rw-r--r--unit/atf-src/atf-c/tc.h136
-rw-r--r--unit/atf-src/atf-c/tc_test.c183
-rw-r--r--unit/atf-src/atf-c/tp.c213
-rw-r--r--unit/atf-src/atf-c/tp.h65
-rw-r--r--unit/atf-src/atf-c/tp_test.c88
-rw-r--r--unit/atf-src/atf-c/unused_test.c52
-rw-r--r--unit/atf-src/atf-c/utils.c456
-rw-r--r--unit/atf-src/atf-c/utils.h50
-rw-r--r--unit/atf-src/atf-c/utils_test.c570
69 files changed, 17914 insertions, 0 deletions
diff --git a/unit/atf-src/atf-c/Atffile b/unit/atf-src/atf-c/Atffile
new file mode 100644
index 0000000..d6fd6b9
--- /dev/null
+++ b/unit/atf-src/atf-c/Atffile
@@ -0,0 +1,15 @@
+Content-Type: application/X-atf-atffile; version="1"
+
+prop: test-suite = atf
+
+tp: detail
+
+tp: atf_c_test
+tp: build_test
+tp: check_test
+tp: error_test
+tp: macros_test
+tp: pkg_config_test
+tp: tc_test
+tp: tp_test
+tp: utils_test
diff --git a/unit/atf-src/atf-c/Kyuafile b/unit/atf-src/atf-c/Kyuafile
new file mode 100644
index 0000000..40fdb92
--- /dev/null
+++ b/unit/atf-src/atf-c/Kyuafile
@@ -0,0 +1,15 @@
+syntax("kyuafile", 1)
+
+test_suite("atf")
+
+atf_test_program{name="atf_c_test"}
+atf_test_program{name="build_test"}
+atf_test_program{name="check_test"}
+atf_test_program{name="error_test"}
+atf_test_program{name="macros_test"}
+atf_test_program{name="pkg_config_test"}
+atf_test_program{name="tc_test"}
+atf_test_program{name="tp_test"}
+atf_test_program{name="utils_test"}
+
+include("detail/Kyuafile")
diff --git a/unit/atf-src/atf-c/Makefile.am.inc b/unit/atf-src/atf-c/Makefile.am.inc
new file mode 100644
index 0000000..fb89bf0
--- /dev/null
+++ b/unit/atf-src/atf-c/Makefile.am.inc
@@ -0,0 +1,145 @@
+# Copyright (c) 2007 The NetBSD Foundation, Inc.
+# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+
+lib_LTLIBRARIES += libatf-c.la
+libatf_c_la_SOURCES = atf-c/build.c \
+ atf-c/build.h \
+ atf-c/check.c \
+ atf-c/check.h \
+ atf-c/error.c \
+ atf-c/error.h \
+ atf-c/error_fwd.h \
+ atf-c/macros.h \
+ atf-c/tc.c \
+ atf-c/tc.h \
+ atf-c/tp.c \
+ atf-c/tp.h \
+ atf-c/utils.c \
+ atf-c/utils.h
+nodist_libatf_c_la_SOURCES = atf-c/defs.h
+#libatf_c_la_CPPFLAGS = "-DATF_BUILD_CC=\"$(ATF_BUILD_CC)\"" \
+# "-DATF_BUILD_CFLAGS=\"$(ATF_BUILD_CFLAGS)\"" \
+# "-DATF_BUILD_CPP=\"$(ATF_BUILD_CPP)\"" \
+# "-DATF_BUILD_CPPFLAGS=\"$(ATF_BUILD_CPPFLAGS)\"" \
+# "-DATF_BUILD_CXX=\"$(ATF_BUILD_CXX)\"" \
+# "-DATF_BUILD_CXXFLAGS=\"$(ATF_BUILD_CXXFLAGS)\"" \
+# "-DATF_INCLUDEDIR=\"$(includedir)\"" \
+# "-DATF_LIBEXECDIR=\"$(libexecdir)\"" \
+# "-DATF_PKGDATADIR=\"$(pkgdatadir)\"" \
+# "-DATF_SHELL=\"$(ATF_SHELL)\"" \
+# "-DATF_WORKDIR=\"$(ATF_WORKDIR)\""
+libatf_c_la_CPPFLAGS = "-DATF_BUILD_CC=\"$(ATF_BUILD_CC)\"" \
+ "-DATF_BUILD_CFLAGS=\"$(ATF_BUILD_CFLAGS)\"" \
+ "-DATF_BUILD_CPP=\"$(ATF_BUILD_CPP)\"" \
+ "-DATF_BUILD_CPPFLAGS=\"$(ATF_BUILD_CPPFLAGS)\"" \
+ "-DATF_BUILD_CXX=\"$(ATF_BUILD_CXX)\"" \
+ "-DATF_BUILD_CXXFLAGS=\"$(ATF_BUILD_CXXFLAGS)\""
+libatf_c_la_LDFLAGS = -version-info 1:0:0
+
+# XXX For some reason, the nodist line above does not work as expected.
+# Work this problem around.
+DIST_HOOKS += kill-defs-h
+kill-defs-h:
+ rm -f $(distdir)/atf-c/defs.h
+
+include_HEADERS += atf-c.h
+atf_c_HEADERS = atf-c/build.h \
+ atf-c/check.h \
+ atf-c/error.h \
+ atf-c/error_fwd.h \
+ atf-c/macros.h \
+ atf-c/tc.h \
+ atf-c/tp.h \
+ atf-c/utils.h
+nodist_atf_c_HEADERS = atf-c/defs.h
+atf_cdir = $(includedir)/atf-c
+
+dist_man_MANS += atf-c/atf-c.3
+
+atf_aclocal_DATA += atf-c/atf-common.m4 atf-c/atf-c.m4
+EXTRA_DIST += atf-c/atf-common.m4 atf-c/atf-c.m4
+
+atf_cpkgconfigdir = $(atf_pkgconfigdir)
+atf_cpkgconfig_DATA = atf-c/atf-c.pc
+CLEANFILES += atf-c/atf-c.pc
+EXTRA_DIST += atf-c/atf-c.pc.in
+atf-c/atf-c.pc: $(srcdir)/atf-c/atf-c.pc.in Makefile
+ $(AM_V_GEN)test -d atf-c || mkdir -p atf-c; \
+ sed -e 's#__ATF_VERSION__#$(PACKAGE_VERSION)#g' \
+ -e 's#__CC__#$(CC)#g' \
+ -e 's#__INCLUDEDIR__#$(includedir)#g' \
+ -e 's#__LIBDIR__#$(libdir)#g' \
+ <$(srcdir)/atf-c/atf-c.pc.in >atf-c/atf-c.pc.tmp; \
+ mv atf-c/atf-c.pc.tmp atf-c/atf-c.pc
+
+tests_atf_c_DATA = atf-c/Atffile \
+ atf-c/Kyuafile \
+ atf-c/macros_h_test.c \
+ atf-c/unused_test.c
+tests_atf_cdir = $(pkgtestsdir)/atf-c
+EXTRA_DIST += $(tests_atf_c_DATA)
+
+tests_atf_c_PROGRAMS = atf-c/atf_c_test
+atf_c_atf_c_test_SOURCES = atf-c/atf_c_test.c
+atf_c_atf_c_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_PROGRAMS += atf-c/build_test
+atf_c_build_test_SOURCES = atf-c/build_test.c atf-c/h_build.h
+atf_c_build_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_PROGRAMS += atf-c/check_test
+atf_c_check_test_SOURCES = atf-c/check_test.c
+atf_c_check_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_PROGRAMS += atf-c/error_test
+atf_c_error_test_SOURCES = atf-c/error_test.c
+atf_c_error_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_PROGRAMS += atf-c/macros_test
+atf_c_macros_test_SOURCES = atf-c/macros_test.c
+atf_c_macros_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_SCRIPTS = atf-c/pkg_config_test
+CLEANFILES += atf-c/pkg_config_test
+EXTRA_DIST += atf-c/pkg_config_test.sh
+atf-c/pkg_config_test: $(srcdir)/atf-c/pkg_config_test.sh
+ $(AM_V_GEN)src="$(srcdir)/atf-c/pkg_config_test.sh"; \
+ dst="atf-c/pkg_config_test"; $(BUILD_SH_TP)
+
+tests_atf_c_PROGRAMS += atf-c/tc_test
+atf_c_tc_test_SOURCES = atf-c/tc_test.c
+atf_c_tc_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_PROGRAMS += atf-c/tp_test
+atf_c_tp_test_SOURCES = atf-c/tp_test.c
+atf_c_tp_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_PROGRAMS += atf-c/utils_test
+atf_c_utils_test_SOURCES = atf-c/utils_test.c atf-c/h_build.h
+atf_c_utils_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+include atf-c/detail/Makefile.am.inc
+
+# vim: syntax=make:noexpandtab:shiftwidth=8:softtabstop=8
diff --git a/unit/atf-src/atf-c/atf-c.3 b/unit/atf-src/atf-c/atf-c.3
new file mode 100644
index 0000000..edb7207
--- /dev/null
+++ b/unit/atf-src/atf-c/atf-c.3
@@ -0,0 +1,793 @@
+.\" Copyright (c) 2008 The NetBSD Foundation, Inc.
+.\" 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+.Dd October 13, 2014
+.Dt ATF-C 3
+.Os
+.Sh NAME
+.Nm atf-c ,
+.Nm ATF_CHECK ,
+.Nm ATF_CHECK_MSG ,
+.Nm ATF_CHECK_EQ ,
+.Nm ATF_CHECK_EQ_MSG ,
+.Nm ATF_CHECK_MATCH ,
+.Nm ATF_CHECK_MATCH_MSG ,
+.Nm ATF_CHECK_STREQ ,
+.Nm ATF_CHECK_STREQ_MSG ,
+.Nm ATF_CHECK_ERRNO ,
+.Nm ATF_REQUIRE ,
+.Nm ATF_REQUIRE_MSG ,
+.Nm ATF_REQUIRE_EQ ,
+.Nm ATF_REQUIRE_EQ_MSG ,
+.Nm ATF_REQUIRE_MATCH ,
+.Nm ATF_REQUIRE_MATCH_MSG ,
+.Nm ATF_REQUIRE_STREQ ,
+.Nm ATF_REQUIRE_STREQ_MSG ,
+.Nm ATF_REQUIRE_ERRNO ,
+.Nm ATF_TC ,
+.Nm ATF_TC_BODY ,
+.Nm ATF_TC_BODY_NAME ,
+.Nm ATF_TC_CLEANUP ,
+.Nm ATF_TC_CLEANUP_NAME ,
+.Nm ATF_TC_HEAD ,
+.Nm ATF_TC_HEAD_NAME ,
+.Nm ATF_TC_NAME ,
+.Nm ATF_TC_WITH_CLEANUP ,
+.Nm ATF_TC_WITHOUT_HEAD ,
+.Nm ATF_TP_ADD_TC ,
+.Nm ATF_TP_ADD_TCS ,
+.Nm atf_tc_get_config_var ,
+.Nm atf_tc_get_config_var_wd ,
+.Nm atf_tc_get_config_var_as_bool ,
+.Nm atf_tc_get_config_var_as_bool_wd ,
+.Nm atf_tc_get_config_var_as_long ,
+.Nm atf_tc_get_config_var_as_long_wd ,
+.Nm atf_no_error ,
+.Nm atf_tc_expect_death ,
+.Nm atf_tc_expect_exit ,
+.Nm atf_tc_expect_fail ,
+.Nm atf_tc_expect_pass ,
+.Nm atf_tc_expect_signal ,
+.Nm atf_tc_expect_timeout ,
+.Nm atf_tc_fail ,
+.Nm atf_tc_fail_nonfatal ,
+.Nm atf_tc_pass ,
+.Nm atf_tc_skip ,
+.Nm atf_utils_cat_file ,
+.Nm atf_utils_compare_file ,
+.Nm atf_utils_copy_file ,
+.Nm atf_utils_create_file ,
+.Nm atf_utils_file_exists ,
+.Nm atf_utils_fork ,
+.Nm atf_utils_free_charpp ,
+.Nm atf_utils_grep_file ,
+.Nm atf_utils_grep_string ,
+.Nm atf_utils_readline ,
+.Nm atf_utils_redirect ,
+.Nm atf_utils_wait
+.Nd C API to write ATF-based test programs
+.Sh SYNOPSIS
+.In atf-c.h
+.\" NO_CHECK_STYLE_BEGIN
+.Fn ATF_CHECK "expression"
+.Fn ATF_CHECK_MSG "expression" "fail_msg_fmt" ...
+.Fn ATF_CHECK_EQ "expected_expression" "actual_expression"
+.Fn ATF_CHECK_EQ_MSG "expected_expression" "actual_expression" "fail_msg_fmt" ...
+.Fn ATF_CHECK_MATCH "regexp" "string"
+.Fn ATF_CHECK_MATCH_MSG "regexp" "string" "fail_msg_fmt" ...
+.Fn ATF_CHECK_STREQ "string_1" "string_2"
+.Fn ATF_CHECK_STREQ_MSG "string_1" "string_2" "fail_msg_fmt" ...
+.Fn ATF_CHECK_ERRNO "expected_errno" "bool_expression"
+.Fn ATF_REQUIRE "expression"
+.Fn ATF_REQUIRE_MSG "expression" "fail_msg_fmt" ...
+.Fn ATF_REQUIRE_EQ "expected_expression" "actual_expression"
+.Fn ATF_REQUIRE_EQ_MSG "expected_expression" "actual_expression" "fail_msg_fmt" ...
+.Fn ATF_REQUIRE_MATCH "regexp" "string"
+.Fn ATF_REQUIRE_MATCH_MSG "regexp" "string" "fail_msg_fmt" ...
+.Fn ATF_REQUIRE_STREQ "expected_string" "actual_string"
+.Fn ATF_REQUIRE_STREQ_MSG "expected_string" "actual_string" "fail_msg_fmt" ...
+.Fn ATF_REQUIRE_ERRNO "expected_errno" "bool_expression"
+.\" NO_CHECK_STYLE_END
+.Fn ATF_TC "name"
+.Fn ATF_TC_BODY "name" "tc"
+.Fn ATF_TC_BODY_NAME "name"
+.Fn ATF_TC_CLEANUP "name" "tc"
+.Fn ATF_TC_CLEANUP_NAME "name"
+.Fn ATF_TC_HEAD "name" "tc"
+.Fn ATF_TC_HEAD_NAME "name"
+.Fn ATF_TC_NAME "name"
+.Fn ATF_TC_WITH_CLEANUP "name"
+.Fn ATF_TC_WITHOUT_HEAD "name"
+.Fn ATF_TP_ADD_TC "tp_name" "tc_name"
+.Fn ATF_TP_ADD_TCS "tp_name"
+.Fn atf_tc_get_config_var "tc" "varname"
+.Fn atf_tc_get_config_var_wd "tc" "variable_name" "default_value"
+.Fn atf_tc_get_config_var_as_bool "tc" "variable_name"
+.Fn atf_tc_get_config_var_as_bool_wd "tc" "variable_name" "default_value"
+.Fn atf_tc_get_config_var_as_long "tc" "variable_name"
+.Fn atf_tc_get_config_var_as_long_wd "tc" "variable_name" "default_value"
+.Fn atf_no_error
+.Fn atf_tc_expect_death "reason" "..."
+.Fn atf_tc_expect_exit "exitcode" "reason" "..."
+.Fn atf_tc_expect_fail "reason" "..."
+.Fn atf_tc_expect_pass
+.Fn atf_tc_expect_signal "signo" "reason" "..."
+.Fn atf_tc_expect_timeout "reason" "..."
+.Fn atf_tc_fail "reason"
+.Fn atf_tc_fail_nonfatal "reason"
+.Fn atf_tc_pass
+.Fn atf_tc_skip "reason"
+.Ft void
+.Fo atf_utils_cat_file
+.Fa "const char *file"
+.Fa "const char *prefix"
+.Fc
+.Ft bool
+.Fo atf_utils_compare_file
+.Fa "const char *file"
+.Fa "const char *contents"
+.Fc
+.Ft void
+.Fo atf_utils_copy_file
+.Fa "const char *source"
+.Fa "const char *destination"
+.Fc
+.Ft void
+.Fo atf_utils_create_file
+.Fa "const char *file"
+.Fa "const char *contents"
+.Fa "..."
+.Fc
+.Ft void
+.Fo atf_utils_file_exists
+.Fa "const char *file"
+.Fc
+.Ft pid_t
+.Fo atf_utils_fork
+.Fa "void"
+.Fc
+.Ft void
+.Fo atf_utils_free_charpp
+.Fa "char **argv"
+.Fc
+.Ft bool
+.Fo atf_utils_grep_file
+.Fa "const char *regexp"
+.Fa "const char *file"
+.Fa "..."
+.Fc
+.Ft bool
+.Fo atf_utils_grep_string
+.Fa "const char *regexp"
+.Fa "const char *str"
+.Fa "..."
+.Fc
+.Ft char *
+.Fo atf_utils_readline
+.Fa "int fd"
+.Fc
+.Ft void
+.Fo atf_utils_redirect
+.Fa "const int fd"
+.Fa "const char *file"
+.Fc
+.Ft void
+.Fo atf_utils_wait
+.Fa "const pid_t pid"
+.Fa "const int expected_exit_status"
+.Fa "const char *expected_stdout"
+.Fa "const char *expected_stderr"
+.Fc
+.Sh DESCRIPTION
+ATF provides a C programming interface to implement test programs.
+C-based test programs follow this template:
+.Bd -literal -offset indent
+.Ns ... C-specific includes go here ...
+
+#include <atf-c.h>
+
+ATF_TC(tc1);
+ATF_TC_HEAD(tc1, tc)
+{
+ ... first test case's header ...
+}
+ATF_TC_BODY(tc1, tc)
+{
+ ... first test case's body ...
+}
+
+ATF_TC_WITH_CLEANUP(tc2);
+ATF_TC_HEAD(tc2, tc)
+{
+ ... second test case's header ...
+}
+ATF_TC_BODY(tc2, tc)
+{
+ ... second test case's body ...
+}
+ATF_TC_CLEANUP(tc2, tc)
+{
+ ... second test case's cleanup ...
+}
+
+ATF_TC_WITHOUT_HEAD(tc3);
+ATF_TC_BODY(tc3, tc)
+{
+ ... third test case's body ...
+}
+
+.Ns ... additional test cases ...
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tcs, tc1);
+ ATF_TP_ADD_TC(tcs, tc2);
+ ATF_TP_ADD_TC(tcs, tc3);
+ ... add additional test cases ...
+
+ return atf_no_error();
+}
+.Ed
+.Ss Definition of test cases
+Test cases have an identifier and are composed of three different parts:
+the header, the body and an optional cleanup routine, all of which are
+described in
+.Xr atf-test-case 4 .
+To define test cases, one can use the
+.Fn ATF_TC ,
+.Fn ATF_TC_WITH_CLEANUP
+or the
+.Fn ATF_TC_WITHOUT_HEAD
+macros, which take a single parameter specifiying the test case's name.
+.Fn ATF_TC ,
+requires to define a head and a body for the test case,
+.Fn ATF_TC_WITH_CLEANUP
+requires to define a head, a body and a cleanup for the test case and
+.Fn ATF_TC_WITHOUT_HEAD
+requires only a body for the test case.
+It is important to note that these
+.Em do not
+set the test case up for execution when the program is run.
+In order to do so, a later registration is needed with the
+.Fn ATF_TP_ADD_TC
+macro detailed in
+.Sx Program initialization .
+.Pp
+Later on, one must define the three parts of the body by means of three
+functions.
+Their headers are given by the
+.Fn ATF_TC_HEAD ,
+.Fn ATF_TC_BODY
+and
+.Fn ATF_TC_CLEANUP
+macros, all of which take the test case name provided to the
+.Fn ATF_TC
+.Fn ATF_TC_WITH_CLEANUP ,
+or
+.Fn ATF_TC_WITHOUT_HEAD
+macros and the name of the variable that will hold a pointer to the
+test case data.
+Following each of these, a block of code is expected, surrounded by the
+opening and closing brackets.
+.Ss Program initialization
+The library provides a way to easily define the test program's
+.Fn main
+function.
+You should never define one on your own, but rely on the
+library to do it for you.
+This is done by using the
+.Fn ATF_TP_ADD_TCS
+macro, which is passed the name of the object that will hold the test
+cases; i.e. the test program instance.
+This name can be whatever you want as long as it is a valid variable
+identifier.
+.Pp
+After the macro, you are supposed to provide the body of a function, which
+should only use the
+.Fn ATF_TP_ADD_TC
+macro to register the test cases the test program will execute and return
+a success error code.
+The first parameter of this macro matches the name you provided in the
+former call.
+The success status can be returned using the
+.Fn atf_no_error
+function.
+.Ss Header definitions
+The test case's header can define the meta-data by using the
+.Fn atf_tc_set_md_var
+method, which takes three parameters: the first one points to the test
+case data, the second one specifies the meta-data variable to be set
+and the third one specifies its value.
+Both of them are strings.
+.Ss Configuration variables
+The test case has read-only access to the current configuration variables
+by means of the
+.Ft bool
+.Fn atf_tc_has_config_var ,
+.Ft const char *
+.Fn atf_tc_get_config_var ,
+.Ft const char *
+.Fn atf_tc_get_config_var_wd ,
+.Ft bool
+.Fn atf_tc_get_config_var_as_bool ,
+.Ft bool
+.Fn atf_tc_get_config_var_as_bool_wd ,
+.Ft long
+.Fn atf_tc_get_config_var_as_long ,
+and the
+.Ft long
+.Fn atf_tc_get_config_var_as_long_wd
+functions, which can be called in any of the three parts of a test case.
+.Pp
+The
+.Sq _wd
+variants take a default value for the variable which is returned if the
+variable is not defined.
+The other functions without the
+.Sq _wd
+suffix
+.Em require
+the variable to be defined.
+.Ss Access to the source directory
+It is possible to get the path to the test case's source directory from any
+of its three components by querying the
+.Sq srcdir
+configuration variable.
+.Ss Requiring programs
+Aside from the
+.Va require.progs
+meta-data variable available in the header only, one can also check for
+additional programs in the test case's body by using the
+.Fn atf_tc_require_prog
+function, which takes the base name or full path of a single binary.
+Relative paths are forbidden.
+If it is not found, the test case will be automatically skipped.
+.Ss Test case finalization
+The test case finalizes either when the body reaches its end, at which
+point the test is assumed to have
+.Em passed ,
+unless any non-fatal errors were raised using
+.Fn atf_tc_fail_nonfatal ,
+or at any explicit call to
+.Fn atf_tc_pass ,
+.Fn atf_tc_fail
+or
+.Fn atf_tc_skip .
+These three functions terminate the execution of the test case immediately.
+The cleanup routine will be processed afterwards in a completely automated
+way, regardless of the test case's termination reason.
+.Pp
+.Fn atf_tc_pass
+does not take any parameters.
+.Fn atf_tc_fail ,
+.Fn atf_tc_fail_nonfatal
+and
+.Fn atf_tc_skip
+take a format string and a variable list of parameters, which describe, in
+a user-friendly manner, why the test case failed or was skipped,
+respectively.
+It is very important to provide a clear error message in both cases so that
+the user can quickly know why the test did not pass.
+.Ss Expectations
+Everything explained in the previous section changes when the test case
+expectations are redefined by the programmer.
+.Pp
+Each test case has an internal state called
+.Sq expect
+that describes what the test case expectations are at any point in time.
+The value of this property can change during execution by any of:
+.Bl -tag -width indent
+.It Fn atf_tc_expect_death "reason" "..."
+Expects the test case to exit prematurely regardless of the nature of the
+exit.
+.It Fn atf_tc_expect_exit "exitcode" "reason" "..."
+Expects the test case to exit cleanly.
+If
+.Va exitcode
+is not
+.Sq -1 ,
+the runtime engine will validate that the exit code of the test case
+matches the one provided in this call.
+Otherwise, the exact value will be ignored.
+.It Fn atf_tc_expect_fail "reason" "..."
+Any failure (be it fatal or non-fatal) raised in this mode is recorded.
+However, such failures do not report the test case as failed; instead, the
+test case finalizes cleanly and is reported as
+.Sq expected failure ;
+this report includes the provided
+.Fa reason
+as part of it.
+If no error is raised while running in this mode, then the test case is
+reported as
+.Sq failed .
+.Pp
+This mode is useful to reproduce actual known bugs in tests.
+Whenever the developer fixes the bug later on, the test case will start
+reporting a failure, signaling the developer that the test case must be
+adjusted to the new conditions.
+In this situation, it is useful, for example, to set
+.Fa reason
+as the bug number for tracking purposes.
+.It Fn atf_tc_expect_pass
+This is the normal mode of execution.
+In this mode, any failure is reported as such to the user and the test case
+is marked as
+.Sq failed .
+.It Fn atf_tc_expect_signal "signo" "reason" "..."
+Expects the test case to terminate due to the reception of a signal.
+If
+.Va signo
+is not
+.Sq -1 ,
+the runtime engine will validate that the signal that terminated the test
+case matches the one provided in this call.
+Otherwise, the exact value will be ignored.
+.It Fn atf_tc_expect_timeout "reason" "..."
+Expects the test case to execute for longer than its timeout.
+.El
+.Ss Helper macros for common checks
+The library provides several macros that are very handy in multiple
+situations.
+These basically check some condition after executing a given statement or
+processing a given expression and, if the condition is not met, they
+report the test case as failed.
+.Pp
+The
+.Sq REQUIRE
+variant of the macros immediately abort the test case as soon as an error
+condition is detected by calling the
+.Fn atf_tc_fail
+function.
+Use this variant whenever it makes no sense to continue the execution of a
+test case when the checked condition is not met.
+The
+.Sq CHECK
+variant, on the other hand, reports a failure as soon as it is encountered
+using the
+.Fn atf_tc_fail_nonfatal
+function, but the execution of the test case continues as if nothing had
+happened.
+Use this variant whenever the checked condition is important as a result of
+the test case, but there are other conditions that can be subsequently
+checked on the same run without aborting.
+.Pp
+Additionally, the
+.Sq MSG
+variants take an extra set of parameters to explicitly specify the failure
+message.
+This failure message is formatted according to the
+.Xr printf 3
+formatters.
+.Pp
+.Fn ATF_CHECK ,
+.Fn ATF_CHECK_MSG ,
+.Fn ATF_REQUIRE
+and
+.Fn ATF_REQUIRE_MSG
+take an expression and fail if the expression evaluates to false.
+.Pp
+.Fn ATF_CHECK_EQ ,
+.Fn ATF_CHECK_EQ_MSG ,
+.Fn ATF_REQUIRE_EQ
+and
+.Fn ATF_REQUIRE_EQ_MSG
+take two expressions and fail if the two evaluated values are not equal.
+The common style is to put the expected value in the first parameter and the
+actual value in the second parameter.
+.Pp
+.Fn ATF_CHECK_MATCH ,
+.Fn ATF_CHECK_MATCH_MSG ,
+.Fn ATF_REQUIRE_MATCH
+and
+.Fn ATF_REQUIRE_MATCH_MSG
+take a regular expression and a string and fail if the regular expression does
+not match the given string.
+Note that the regular expression is not anchored, so it will match anywhere in
+the string.
+.Pp
+.Fn ATF_CHECK_STREQ ,
+.Fn ATF_CHECK_STREQ_MSG ,
+.Fn ATF_REQUIRE_STREQ
+and
+.Fn ATF_REQUIRE_STREQ_MSG
+take two strings and fail if the two are not equal character by character.
+The common style is to put the expected string in the first parameter and the
+actual string in the second parameter.
+.Pp
+.Fn ATF_CHECK_ERRNO
+and
+.Fn ATF_REQUIRE_ERRNO
+take, first, the error code that the check is expecting to find in the
+.Va errno
+variable and, second, a boolean expression that, if evaluates to true,
+means that a call failed and
+.Va errno
+has to be checked against the first value.
+.Ss Utility functions
+The following functions are provided as part of the
+.Nm
+API to simplify the creation of a variety of tests.
+In particular, these are useful to write tests for command-line interfaces.
+.Pp
+.Ft void
+.Fo atf_utils_cat_file
+.Fa "const char *file"
+.Fa "const char *prefix"
+.Fc
+.Bd -ragged -offset indent
+Prints the contents of
+.Fa file
+to the standard output, prefixing every line with the string in
+.Fa prefix .
+.Ed
+.Pp
+.Ft bool
+.Fo atf_utils_compare_file
+.Fa "const char *file"
+.Fa "const char *contents"
+.Fc
+.Bd -ragged -offset indent
+Returns true if the given
+.Fa file
+matches exactly the expected inlined
+.Fa contents .
+.Ed
+.Pp
+.Ft void
+.Fo atf_utils_copy_file
+.Fa "const char *source"
+.Fa "const char *destination"
+.Fc
+.Bd -ragged -offset indent
+Copies the file
+.Fa source
+to
+.Fa destination .
+The permissions of the file are preserved during the code.
+.Ed
+.Pp
+.Ft void
+.Fo atf_utils_create_file
+.Fa "const char *file"
+.Fa "const char *contents"
+.Fa "..."
+.Fc
+.Bd -ragged -offset indent
+Creates
+.Fa file
+with the text given in
+.Fa contents ,
+which is a formatting string that uses the rest of the variable arguments.
+.Ed
+.Pp
+.Ft void
+.Fo atf_utils_file_exists
+.Fa "const char *file"
+.Fc
+.Bd -ragged -offset indent
+Checks if
+.Fa file
+exists.
+.Ed
+.Pp
+.Ft pid_t
+.Fo atf_utils_fork
+.Fa "void"
+.Fc
+.Bd -ragged -offset indent
+Forks a process and redirects the standard output and standard error of the
+child to files for later validation with
+.Fn atf_utils_wait .
+Fails the test case if the fork fails, so this does not return an error.
+.Ed
+.Pp
+.Ft void
+.Fo atf_utils_free_charpp
+.Fa "char **argv"
+.Fc
+.Bd -ragged -offset indent
+Frees a dynamically-allocated array of dynamically-allocated strings.
+.Ed
+.Pp
+.Ft bool
+.Fo atf_utils_grep_file
+.Fa "const char *regexp"
+.Fa "const char *file"
+.Fa "..."
+.Fc
+.Bd -ragged -offset indent
+Searches for the
+.Fa regexp ,
+which is a formatting string representing the regular expression,
+in the
+.Fa file .
+The variable arguments are used to construct the regular expression.
+.Ed
+.Pp
+.Ft bool
+.Fo atf_utils_grep_string
+.Fa "const char *regexp"
+.Fa "const char *str"
+.Fa "..."
+.Fc
+.Bd -ragged -offset indent
+Searches for the
+.Fa regexp ,
+which is a formatting string representing the regular expression,
+in the literal string
+.Fa str .
+The variable arguments are used to construct the regular expression.
+.Ed
+.Pp
+.Ft char *
+.Fo atf_utils_readline
+.Fa "int fd"
+.Fc
+.Bd -ragged -offset indent
+Reads a line from the file descriptor
+.Fa fd .
+The line, if any, is returned as a dynamically-allocated buffer that must be
+released with
+.Xr free 3 .
+If there was nothing to read, returns
+.Sq NULL .
+.Ed
+.Pp
+.Ft void
+.Fo atf_utils_redirect
+.Fa "const int fd"
+.Fa "const char *file"
+.Fc
+.Bd -ragged -offset indent
+Redirects the given file descriptor
+.Fa fd
+to
+.Fa file .
+This function exits the process in case of an error and does not properly mark
+the test case as failed.
+As a result, it should only be used in subprocesses of the test case; specially
+those spawned by
+.Fn atf_utils_fork .
+.Ed
+.Pp
+.Ft void
+.Fo atf_utils_wait
+.Fa "const pid_t pid"
+.Fa "const int expected_exit_status"
+.Fa "const char *expected_stdout"
+.Fa "const char *expected_stderr"
+.Fc
+.Bd -ragged -offset indent
+Waits and validates the result of a subprocess spawned with
+.Fn atf_utils_wait .
+The validation involves checking that the subprocess exited cleanly and returned
+the code specified in
+.Fa expected_exit_status
+and that its standard output and standard error match the strings given in
+.Fa expected_stdout
+and
+.Fa expected_stderr .
+.Pp
+If any of the
+.Fa expected_stdout
+or
+.Fa expected_stderr
+strings are prefixed with
+.Sq save: ,
+then they specify the name of the file into which to store the stdout or stderr
+of the subprocess, and no comparison is performed.
+.Ed
+.Sh ENVIRONMENT
+The following variables are recognized by
+.Nm
+but should not be overridden other than for testing purposes:
+.Pp
+.Bl -tag -width ATFXBUILDXCXXFLAGSXX -compact
+.It Va ATF_BUILD_CC
+Path to the C compiler.
+.It Va ATF_BUILD_CFLAGS
+C compiler flags.
+.It Va ATF_BUILD_CPP
+Path to the C/C++ preprocessor.
+.It Va ATF_BUILD_CPPFLAGS
+C/C++ preprocessor flags.
+.It Va ATF_BUILD_CXX
+Path to the C++ compiler.
+.It Va ATF_BUILD_CXXFLAGS
+C++ compiler flags.
+.El
+.Sh EXAMPLES
+The following shows a complete test program with a single test case that
+validates the addition operator:
+.Bd -literal -offset indent
+#include <atf-c.h>
+
+ATF_TC(addition);
+ATF_TC_HEAD(addition, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Sample tests for the addition operator");
+}
+ATF_TC_BODY(addition, tc)
+{
+ ATF_CHECK_EQ(0, 0 + 0);
+ ATF_CHECK_EQ(1, 0 + 1);
+ ATF_CHECK_EQ(1, 1 + 0);
+
+ ATF_CHECK_EQ(2, 1 + 1);
+
+ ATF_CHECK_EQ(300, 100 + 200);
+}
+
+ATF_TC(string_formatting);
+ATF_TC_HEAD(string_formatting, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Sample tests for the snprintf");
+}
+ATF_TC_BODY(string_formatting, tc)
+{
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "a %s", "string");
+ ATF_CHECK_STREQ_MSG("a string", buf, "%s is not working");
+}
+
+ATF_TC(open_failure);
+ATF_TC_HEAD(open_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Sample tests for the open function");
+}
+ATF_TC_BODY(open_failure, tc)
+{
+ ATF_CHECK_ERRNO(ENOENT, open("non-existent", O_RDONLY) == -1);
+}
+
+ATF_TC(known_bug);
+ATF_TC_HEAD(known_bug, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Reproduces a known bug");
+}
+ATF_TC_BODY(known_bug, tc)
+{
+ atf_tc_expect_fail("See bug number foo/bar");
+ ATF_CHECK_EQ(3, 1 + 1);
+ atf_tc_expect_pass();
+ ATF_CHECK_EQ(3, 1 + 2);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, addition);
+ ATF_TP_ADD_TC(tp, string_formatting);
+ ATF_TP_ADD_TC(tp, open_failure);
+ ATF_TP_ADD_TC(tp, known_bug);
+
+ return atf_no_error();
+}
+.Ed
+.Sh SEE ALSO
+.Xr atf-test-program 1 ,
+.Xr atf-test-case 4
diff --git a/unit/atf-src/atf-c/atf-c.m4 b/unit/atf-src/atf-c/atf-c.m4
new file mode 100644
index 0000000..94288f7
--- /dev/null
+++ b/unit/atf-src/atf-c/atf-c.m4
@@ -0,0 +1,44 @@
+dnl Copyright 2011 Google Inc.
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted provided that the following conditions are
+dnl met:
+dnl
+dnl * Redistributions of source code must retain the above copyright
+dnl notice, this list of conditions and the following disclaimer.
+dnl * Redistributions in binary form must reproduce the above copyright
+dnl notice, this list of conditions and the following disclaimer in the
+dnl documentation and/or other materials provided with the distribution.
+dnl * Neither the name of Google Inc. nor the names of its contributors
+dnl may be used to endorse or promote products derived from this software
+dnl without specific prior written permission.
+dnl
+dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+dnl ATF_CHECK_C([version-spec])
+dnl
+dnl Checks if atf-c is present. If version-spec is provided, ensures that
+dnl the installed version of atf-sh matches the required version. This
+dnl argument must be something like '>= 0.14' and accepts any version
+dnl specification supported by pkg-config.
+dnl
+dnl Defines and substitutes ATF_C_CFLAGS and ATF_C_LIBS with the compiler
+dnl and linker flags need to build against atf-c.
+AC_DEFUN([ATF_CHECK_C], [
+ spec="atf-c[]m4_default_nblank([ $1], [])"
+ _ATF_CHECK_ARG_WITH(
+ [PKG_CHECK_MODULES([ATF_C], [${spec}],
+ [found=yes found_atf_c=yes], [found=no])],
+ [required ${spec} not found])
+])
diff --git a/unit/atf-src/atf-c/atf-c.pc.in b/unit/atf-src/atf-c/atf-c.pc.in
new file mode 100644
index 0000000..6fd5274
--- /dev/null
+++ b/unit/atf-src/atf-c/atf-c.pc.in
@@ -0,0 +1,11 @@
+# ATF pkg-config file
+
+cc=__CC__
+includedir=__INCLUDEDIR__
+libdir=__LIBDIR__
+
+Name: atf-c
+Description: Automated Testing Framework (C binding)
+Version: __ATF_VERSION__
+Cflags: -I${includedir}
+Libs: -L${libdir} -latf-c
diff --git a/unit/atf-src/atf-c/atf-common.m4 b/unit/atf-src/atf-c/atf-common.m4
new file mode 100644
index 0000000..f8fc7e9
--- /dev/null
+++ b/unit/atf-src/atf-c/atf-common.m4
@@ -0,0 +1,88 @@
+dnl Copyright 2011 Google Inc.
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted provided that the following conditions are
+dnl met:
+dnl
+dnl * Redistributions of source code must retain the above copyright
+dnl notice, this list of conditions and the following disclaimer.
+dnl * Redistributions in binary form must reproduce the above copyright
+dnl notice, this list of conditions and the following disclaimer in the
+dnl documentation and/or other materials provided with the distribution.
+dnl * Neither the name of Google Inc. nor the names of its contributors
+dnl may be used to endorse or promote products derived from this software
+dnl without specific prior written permission.
+dnl
+dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+dnl ATF_ARG_WITH
+dnl
+dnl Adds a --with-atf flag to the configure script that allows the user to
+dnl enable or disable atf support.
+dnl
+dnl The ATF_CHECK_{C,CXX,SH} macros honor the flag defined herein if
+dnl instantiated. If not instantiated, they will request the presence of
+dnl the libraries unconditionally.
+dnl
+dnl Defines the WITH_ATF Automake conditional if ATF has been found by any
+dnl of the ATF_CHECK_{C,CXX,SH} macros.
+AC_DEFUN([ATF_ARG_WITH], [
+ m4_define([atf_arg_with_called], [yes])
+
+ m4_divert_text([DEFAULTS], [with_atf=auto])
+ AC_ARG_WITH([atf],
+ [AS_HELP_STRING([--with-atf=<yes|no|auto>],
+ [build atf-based test programs])],
+ [with_atf=${withval}], [with_atf=auto])
+
+ m4_divert_text([DEFAULTS], [
+ found_atf_c=no
+ found_atf_cxx=no
+ found_atf_sh=no
+ ])
+ AM_CONDITIONAL([WITH_ATF], [test x"${found_atf_c}" = x"yes" -o \
+ x"${found_atf_cxx}" = x"yes" -o \
+ x"${found_atf_sh}" = x"yes"])
+])
+
+dnl _ATF_CHECK_ARG_WITH(check, error_message)
+dnl
+dnl Internal macro to execute a check conditional on the --with-atf flag
+dnl and handle the result accordingly.
+dnl
+dnl 'check' specifies the piece of code to be run to detect the feature.
+dnl This code must set the 'found' shell variable to yes or no depending
+dnl on the raw result of the check.
+AC_DEFUN([_ATF_CHECK_ARG_WITH], [
+ m4_ifdef([atf_arg_with_called], [
+ m4_fatal([ATF_ARG_WITH must be called after the ATF_CHECK_* checks])
+ ])
+
+ m4_divert_text([DEFAULTS], [with_atf=yes])
+
+ if test x"${with_atf}" = x"no"; then
+ _found=no
+ else
+ $1
+ if test x"${with_atf}" = x"auto"; then
+ _found="${found}"
+ else
+ if test x"${found}" = x"yes"; then
+ _found=yes
+ else
+ AC_MSG_ERROR([$2])
+ fi
+ fi
+ fi
+])
diff --git a/unit/atf-src/atf-c/atf_c_test.c b/unit/atf-src/atf-c/atf_c_test.c
new file mode 100644
index 0000000..eda9596
--- /dev/null
+++ b/unit/atf-src/atf-c/atf_c_test.c
@@ -0,0 +1,46 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+
+/* ---------------------------------------------------------------------
+ * Tests cases for the header file.
+ * --------------------------------------------------------------------- */
+
+HEADER_TC(include, "atf-c.h");
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Add the test cases for the header file. */
+ ATF_TP_ADD_TC(tp, include);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/build.c b/unit/atf-src/atf-c/build.c
new file mode 100644
index 0000000..c119fc9
--- /dev/null
+++ b/unit/atf-src/atf-c/build.c
@@ -0,0 +1,282 @@
+/* Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/build.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "atf-c/detail/env.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/detail/text.h"
+#include "atf-c/error.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+atf_error_t
+append_config_var(const char *var, const char *default_value, atf_list_t *argv)
+{
+ atf_error_t err;
+ atf_list_t words;
+
+ err = atf_text_split(atf_env_get_with_default(var, default_value),
+ " ", &words);
+ if (atf_is_error(err))
+ goto out;
+
+ atf_list_append_list(argv, &words);
+
+out:
+ return err;
+}
+
+static
+atf_error_t
+append_arg1(const char *arg, atf_list_t *argv)
+{
+ return atf_list_append(argv, strdup(arg), true);
+}
+
+static
+atf_error_t
+append_arg2(const char *flag, const char *arg, atf_list_t *argv)
+{
+ atf_error_t err;
+
+ err = append_arg1(flag, argv);
+ if (!atf_is_error(err))
+ err = append_arg1(arg, argv);
+
+ return err;
+}
+
+static
+atf_error_t
+append_optargs(const char *const optargs[], atf_list_t *argv)
+{
+ atf_error_t err;
+
+ err = atf_no_error();
+ while (*optargs != NULL && !atf_is_error(err)) {
+ err = append_arg1(strdup(*optargs), argv);
+ optargs++;
+ }
+
+ return err;
+}
+
+static
+atf_error_t
+append_src_out(const char *src, const char *obj, atf_list_t *argv)
+{
+ atf_error_t err;
+
+ err = append_arg2("-o", obj, argv);
+ if (atf_is_error(err))
+ goto out;
+
+ err = append_arg1("-c", argv);
+ if (atf_is_error(err))
+ goto out;
+
+ err = append_arg1(src, argv);
+
+out:
+ return err;
+}
+
+static
+atf_error_t
+list_to_array(const atf_list_t *l, char ***ap)
+{
+ atf_error_t err;
+ char **a;
+
+ a = (char **)malloc((atf_list_size(l) + 1) * sizeof(char *));
+ if (a == NULL)
+ err = atf_no_memory_error();
+ else {
+ char **aiter;
+ atf_list_citer_t liter;
+
+ aiter = a;
+ atf_list_for_each_c(liter, l) {
+ *aiter = strdup((const char *)atf_list_citer_data(liter));
+ aiter++;
+ }
+ *aiter = NULL;
+
+ err = atf_no_error();
+ }
+ *ap = a; /* Shut up warnings in the caller about uninitialized *ap. */
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+atf_error_t
+atf_build_c_o(const char *sfile,
+ const char *ofile,
+ const char *const optargs[],
+ char ***argv)
+{
+ atf_error_t err;
+ atf_list_t argv_list;
+
+ err = atf_list_init(&argv_list);
+ if (atf_is_error(err))
+ goto out;
+
+ err = append_config_var("ATF_BUILD_CC", ATF_BUILD_CC, &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+
+ err = append_config_var("ATF_BUILD_CPPFLAGS", ATF_BUILD_CPPFLAGS,
+ &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+
+ err = append_config_var("ATF_BUILD_CFLAGS", ATF_BUILD_CFLAGS, &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+
+ if (optargs != NULL) {
+ err = append_optargs(optargs, &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+ }
+
+ err = append_src_out(sfile, ofile, &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+
+ err = list_to_array(&argv_list, argv);
+ if (atf_is_error(err))
+ goto out_list;
+
+out_list:
+ atf_list_fini(&argv_list);
+out:
+ return err;
+}
+
+atf_error_t
+atf_build_cpp(const char *sfile,
+ const char *ofile,
+ const char *const optargs[],
+ char ***argv)
+{
+ atf_error_t err;
+ atf_list_t argv_list;
+
+ err = atf_list_init(&argv_list);
+ if (atf_is_error(err))
+ goto out;
+
+ err = append_config_var("ATF_BUILD_CPP", ATF_BUILD_CPP, &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+
+ err = append_config_var("ATF_BUILD_CPPFLAGS", ATF_BUILD_CPPFLAGS,
+ &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+
+ if (optargs != NULL) {
+ err = append_optargs(optargs, &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+ }
+
+ err = append_arg2("-o", ofile, &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+
+ err = append_arg1(sfile, &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+
+ err = list_to_array(&argv_list, argv);
+ if (atf_is_error(err))
+ goto out_list;
+
+out_list:
+ atf_list_fini(&argv_list);
+out:
+ return err;
+}
+
+atf_error_t
+atf_build_cxx_o(const char *sfile,
+ const char *ofile,
+ const char *const optargs[],
+ char ***argv)
+{
+ atf_error_t err;
+ atf_list_t argv_list;
+
+ err = atf_list_init(&argv_list);
+ if (atf_is_error(err))
+ goto out;
+
+ err = append_config_var("ATF_BUILD_CXX", ATF_BUILD_CXX, &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+
+ err = append_config_var("ATF_BUILD_CPPFLAGS", ATF_BUILD_CPPFLAGS,
+ &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+
+ err = append_config_var("ATF_BUILD_CXXFLAGS", ATF_BUILD_CXXFLAGS,
+ &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+
+ if (optargs != NULL) {
+ err = append_optargs(optargs, &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+ }
+
+ err = append_src_out(sfile, ofile, &argv_list);
+ if (atf_is_error(err))
+ goto out_list;
+
+ err = list_to_array(&argv_list, argv);
+ if (atf_is_error(err))
+ goto out_list;
+
+out_list:
+ atf_list_fini(&argv_list);
+out:
+ return err;
+}
diff --git a/unit/atf-src/atf-c/build.h b/unit/atf-src/atf-c/build.h
new file mode 100644
index 0000000..a263f50
--- /dev/null
+++ b/unit/atf-src/atf-c/build.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_BUILD_H)
+#define ATF_C_BUILD_H
+
+#include <atf-c/error_fwd.h>
+
+atf_error_t atf_build_c_o(const char *, const char *, const char *const [],
+ char ***);
+atf_error_t atf_build_cpp(const char *, const char *, const char *const [],
+ char ***);
+atf_error_t atf_build_cxx_o(const char *, const char *, const char *const [],
+ char ***);
+
+#endif /* !defined(ATF_C_BUILD_H) */
diff --git a/unit/atf-src/atf-c/build_test.c b/unit/atf-src/atf-c/build_test.c
new file mode 100644
index 0000000..c24b573
--- /dev/null
+++ b/unit/atf-src/atf-c/build_test.c
@@ -0,0 +1,249 @@
+/* Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/build.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/env.h"
+#include "atf-c/detail/test_helpers.h"
+#include "atf-c/h_build.h"
+#include "atf-c/utils.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+bool
+equal_arrays(const char *const *exp_array, char **actual_array)
+{
+ bool equal;
+
+ if (*exp_array == NULL && *actual_array == NULL)
+ equal = true;
+ else if (*exp_array == NULL || *actual_array == NULL)
+ equal = false;
+ else {
+ equal = true;
+ while (*actual_array != NULL) {
+ if (*exp_array == NULL || strcmp(*exp_array, *actual_array) != 0) {
+ equal = false;
+ break;
+ }
+ exp_array++;
+ actual_array++;
+ }
+ }
+
+ return equal;
+}
+
+static
+void
+check_equal_array(const char *const *exp_array, char **actual_array)
+{
+ {
+ const char *const *exp_ptr;
+ printf("Expected arguments:");
+ for (exp_ptr = exp_array; *exp_ptr != NULL; exp_ptr++)
+ printf(" '%s'", *exp_ptr);
+ printf("\n");
+ }
+
+ {
+ char **actual_ptr;
+ printf("Returned arguments:");
+ for (actual_ptr = actual_array; *actual_ptr != NULL; actual_ptr++)
+ printf(" '%s'", *actual_ptr);
+ printf("\n");
+ }
+
+ if (!equal_arrays(exp_array, actual_array))
+ atf_tc_fail_nonfatal("The constructed argv differs from the "
+ "expected values");
+}
+
+static
+void
+verbose_set_env(const char *var, const char *val)
+{
+ printf("Setting %s to '%s'\n", var, val);
+ RE(atf_env_set(var, val));
+}
+
+/* ---------------------------------------------------------------------
+ * Internal test cases.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(equal_arrays);
+ATF_TC_HEAD(equal_arrays, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the test case internal "
+ "equal_arrays function");
+}
+ATF_TC_BODY(equal_arrays, tc)
+{
+ {
+ const char *const exp[] = { NULL };
+ char *actual[] = { NULL };
+
+ ATF_CHECK(equal_arrays(exp, actual));
+ }
+
+ {
+ const char *const exp[] = { NULL };
+ char *actual[2] = { strdup("foo"), NULL };
+
+ ATF_CHECK(!equal_arrays(exp, actual));
+ free(actual[0]);
+ }
+
+ {
+ const char *const exp[] = { "foo", NULL };
+ char *actual[] = { NULL };
+
+ ATF_CHECK(!equal_arrays(exp, actual));
+ }
+
+ {
+ const char *const exp[] = { "foo", NULL };
+ char *actual[2] = { strdup("foo"), NULL };
+
+ ATF_CHECK(equal_arrays(exp, actual));
+ free(actual[0]);
+ }
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(c_o);
+ATF_TC_HEAD(c_o, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_build_c_o function");
+}
+ATF_TC_BODY(c_o, tc)
+{
+ struct c_o_test *test;
+
+ for (test = c_o_tests; test->expargv[0] != NULL; test++) {
+ printf("> Test: %s\n", test->msg);
+
+ verbose_set_env("ATF_BUILD_CC", test->cc);
+ verbose_set_env("ATF_BUILD_CFLAGS", test->cflags);
+ verbose_set_env("ATF_BUILD_CPPFLAGS", test->cppflags);
+
+ {
+ char **argv;
+ if (test->hasoptargs)
+ RE(atf_build_c_o(test->sfile, test->ofile, test->optargs,
+ &argv));
+ else
+ RE(atf_build_c_o(test->sfile, test->ofile, NULL, &argv));
+ check_equal_array(test->expargv, argv);
+ atf_utils_free_charpp(argv);
+ }
+ }
+}
+
+ATF_TC(cpp);
+ATF_TC_HEAD(cpp, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_build_cpp function");
+}
+ATF_TC_BODY(cpp, tc)
+{
+ struct cpp_test *test;
+
+ for (test = cpp_tests; test->expargv[0] != NULL; test++) {
+ printf("> Test: %s\n", test->msg);
+
+ verbose_set_env("ATF_BUILD_CPP", test->cpp);
+ verbose_set_env("ATF_BUILD_CPPFLAGS", test->cppflags);
+
+ {
+ char **argv;
+ if (test->hasoptargs)
+ RE(atf_build_cpp(test->sfile, test->ofile, test->optargs,
+ &argv));
+ else
+ RE(atf_build_cpp(test->sfile, test->ofile, NULL, &argv));
+ check_equal_array(test->expargv, argv);
+ atf_utils_free_charpp(argv);
+ }
+ }
+}
+
+ATF_TC(cxx_o);
+ATF_TC_HEAD(cxx_o, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_build_cxx_o function");
+}
+ATF_TC_BODY(cxx_o, tc)
+{
+ struct cxx_o_test *test;
+
+ for (test = cxx_o_tests; test->expargv[0] != NULL; test++) {
+ printf("> Test: %s\n", test->msg);
+
+ verbose_set_env("ATF_BUILD_CXX", test->cxx);
+ verbose_set_env("ATF_BUILD_CXXFLAGS", test->cxxflags);
+ verbose_set_env("ATF_BUILD_CPPFLAGS", test->cppflags);
+
+ {
+ char **argv;
+ if (test->hasoptargs)
+ RE(atf_build_cxx_o(test->sfile, test->ofile, test->optargs,
+ &argv));
+ else
+ RE(atf_build_cxx_o(test->sfile, test->ofile, NULL, &argv));
+ check_equal_array(test->expargv, argv);
+ atf_utils_free_charpp(argv);
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Add the internal test cases. */
+ ATF_TP_ADD_TC(tp, equal_arrays);
+
+ /* Add the test cases for the free functions. */
+ ATF_TP_ADD_TC(tp, c_o);
+ ATF_TP_ADD_TC(tp, cpp);
+ ATF_TP_ADD_TC(tp, cxx_o);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/check.c b/unit/atf-src/atf-c/check.c
new file mode 100644
index 0000000..38afdf3
--- /dev/null
+++ b/unit/atf-src/atf-c/check.c
@@ -0,0 +1,484 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/check.h"
+
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atf-c/build.h"
+#include "atf-c/defs.h"
+#include "atf-c/detail/dynstr.h"
+#include "atf-c/detail/env.h"
+#include "atf-c/detail/fs.h"
+#include "atf-c/detail/list.h"
+#include "atf-c/detail/process.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/error.h"
+#include "atf-c/utils.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+atf_error_t
+create_tmpdir(atf_fs_path_t *dir)
+{
+ atf_error_t err;
+
+ err = atf_fs_path_init_fmt(dir, "%s/check.XXXXXX",
+ atf_env_get_with_default("TMPDIR", "/tmp"));
+ if (atf_is_error(err))
+ goto out;
+
+ err = atf_fs_mkdtemp(dir);
+ if (atf_is_error(err)) {
+ atf_fs_path_fini(dir);
+ goto out;
+ }
+
+ INV(!atf_is_error(err));
+out:
+ return err;
+}
+
+static
+void
+cleanup_tmpdir(const atf_fs_path_t *dir, const atf_fs_path_t *outfile,
+ const atf_fs_path_t *errfile)
+{
+ {
+ atf_error_t err = atf_fs_unlink(outfile);
+ if (atf_is_error(err)) {
+ INV(atf_error_is(err, "libc") &&
+ atf_libc_error_code(err) == ENOENT);
+ atf_error_free(err);
+ } else
+ INV(!atf_is_error(err));
+ }
+
+ {
+ atf_error_t err = atf_fs_unlink(errfile);
+ if (atf_is_error(err)) {
+ INV(atf_error_is(err, "libc") &&
+ atf_libc_error_code(err) == ENOENT);
+ atf_error_free(err);
+ } else
+ INV(!atf_is_error(err));
+ }
+
+ {
+ atf_error_t err = atf_fs_rmdir(dir);
+ INV(!atf_is_error(err));
+ }
+}
+
+static
+int
+const_execvp(const char *file, const char *const *argv)
+{
+#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+ return execvp(file, UNCONST(argv));
+#undef UNCONST
+}
+
+static
+atf_error_t
+init_sb(const atf_fs_path_t *path, atf_process_stream_t *sb)
+{
+ atf_error_t err;
+
+ if (path == NULL)
+ err = atf_process_stream_init_inherit(sb);
+ else
+ err = atf_process_stream_init_redirect_path(sb, path);
+
+ return err;
+}
+
+static
+atf_error_t
+init_sbs(const atf_fs_path_t *outfile, atf_process_stream_t *outsb,
+ const atf_fs_path_t *errfile, atf_process_stream_t *errsb)
+{
+ atf_error_t err;
+
+ err = init_sb(outfile, outsb);
+ if (atf_is_error(err))
+ goto out;
+
+ err = init_sb(errfile, errsb);
+ if (atf_is_error(err)) {
+ atf_process_stream_fini(outsb);
+ goto out;
+ }
+
+out:
+ return err;
+}
+
+struct exec_data {
+ const char *const *m_argv;
+};
+
+static void exec_child(void *) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+static
+void
+exec_child(void *v)
+{
+ struct exec_data *ea = v;
+
+ const_execvp(ea->m_argv[0], ea->m_argv);
+ fprintf(stderr, "execvp(%s) failed: %s\n", ea->m_argv[0], strerror(errno));
+ exit(127);
+}
+
+static
+atf_error_t
+fork_and_wait(const char *const *argv, const atf_fs_path_t *outfile,
+ const atf_fs_path_t *errfile, atf_process_status_t *status)
+{
+ atf_error_t err;
+ atf_process_child_t child;
+ atf_process_stream_t outsb, errsb;
+ struct exec_data ea = { argv };
+
+ err = init_sbs(outfile, &outsb, errfile, &errsb);
+ if (atf_is_error(err))
+ goto out;
+
+ err = atf_process_fork(&child, exec_child, &outsb, &errsb, &ea);
+ if (atf_is_error(err))
+ goto out_sbs;
+
+ err = atf_process_child_wait(&child, status);
+
+out_sbs:
+ atf_process_stream_fini(&errsb);
+ atf_process_stream_fini(&outsb);
+out:
+ return err;
+}
+
+static
+void
+update_success_from_status(const char *progname,
+ const atf_process_status_t *status, bool *success)
+{
+ bool s = atf_process_status_exited(status) &&
+ atf_process_status_exitstatus(status) == EXIT_SUCCESS;
+
+ if (atf_process_status_exited(status)) {
+ if (atf_process_status_exitstatus(status) == EXIT_SUCCESS)
+ INV(s);
+ else {
+ INV(!s);
+ fprintf(stderr, "%s failed with exit code %d\n", progname,
+ atf_process_status_exitstatus(status));
+ }
+ } else if (atf_process_status_signaled(status)) {
+ INV(!s);
+ fprintf(stderr, "%s failed due to signal %d%s\n", progname,
+ atf_process_status_termsig(status),
+ atf_process_status_coredump(status) ? " (core dumped)" : "");
+ } else {
+ INV(!s);
+ fprintf(stderr, "%s failed due to unknown reason\n", progname);
+ }
+
+ *success = s;
+}
+
+static
+atf_error_t
+array_to_list(const char *const *a, atf_list_t *l)
+{
+ atf_error_t err;
+
+ err = atf_list_init(l);
+ if (atf_is_error(err))
+ goto out;
+
+ while (*a != NULL) {
+ char *item = strdup(*a);
+ if (item == NULL) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ err = atf_list_append(l, item, true);
+ if (atf_is_error(err))
+ goto out;
+
+ a++;
+ }
+
+out:
+ return err;
+}
+
+static void
+print_array(const char *const *array, const char *pfx)
+{
+ const char *const *ptr;
+
+ printf("%s", pfx);
+ for (ptr = array; *ptr != NULL; ptr++)
+ printf(" %s", *ptr);
+ printf("\n");
+}
+
+static
+atf_error_t
+check_build_run(const char *const *argv, bool *success)
+{
+ atf_error_t err;
+ atf_process_status_t status;
+
+ print_array(argv, ">");
+
+ err = fork_and_wait(argv, NULL, NULL, &status);
+ if (atf_is_error(err))
+ goto out;
+
+ update_success_from_status(argv[0], &status, success);
+ atf_process_status_fini(&status);
+
+ INV(!atf_is_error(err));
+out:
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_check_result" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_check_result_impl {
+ atf_list_t m_argv;
+ atf_fs_path_t m_dir;
+ atf_fs_path_t m_stdout;
+ atf_fs_path_t m_stderr;
+ atf_process_status_t m_status;
+};
+
+static
+atf_error_t
+atf_check_result_init(atf_check_result_t *r, const char *const *argv,
+ const atf_fs_path_t *dir)
+{
+ atf_error_t err;
+
+ r->pimpl = malloc(sizeof(struct atf_check_result_impl));
+ if (r->pimpl == NULL)
+ return atf_no_memory_error();
+
+ err = array_to_list(argv, &r->pimpl->m_argv);
+ if (atf_is_error(err))
+ goto out;
+
+ err = atf_fs_path_copy(&r->pimpl->m_dir, dir);
+ if (atf_is_error(err))
+ goto err_argv;
+
+ err = atf_fs_path_init_fmt(&r->pimpl->m_stdout, "%s/stdout",
+ atf_fs_path_cstring(dir));
+ if (atf_is_error(err))
+ goto err_dir;
+
+ err = atf_fs_path_init_fmt(&r->pimpl->m_stderr, "%s/stderr",
+ atf_fs_path_cstring(dir));
+ if (atf_is_error(err))
+ goto err_stdout;
+
+ INV(!atf_is_error(err));
+ goto out;
+
+err_stdout:
+ atf_fs_path_fini(&r->pimpl->m_stdout);
+err_dir:
+ atf_fs_path_fini(&r->pimpl->m_dir);
+err_argv:
+ atf_list_fini(&r->pimpl->m_argv);
+out:
+ return err;
+}
+
+void
+atf_check_result_fini(atf_check_result_t *r)
+{
+ atf_process_status_fini(&r->pimpl->m_status);
+
+ cleanup_tmpdir(&r->pimpl->m_dir, &r->pimpl->m_stdout,
+ &r->pimpl->m_stderr);
+ atf_fs_path_fini(&r->pimpl->m_stdout);
+ atf_fs_path_fini(&r->pimpl->m_stderr);
+ atf_fs_path_fini(&r->pimpl->m_dir);
+
+ atf_list_fini(&r->pimpl->m_argv);
+
+ free(r->pimpl);
+}
+
+const char *
+atf_check_result_stdout(const atf_check_result_t *r)
+{
+ return atf_fs_path_cstring(&r->pimpl->m_stdout);
+}
+
+const char *
+atf_check_result_stderr(const atf_check_result_t *r)
+{
+ return atf_fs_path_cstring(&r->pimpl->m_stderr);
+}
+
+bool
+atf_check_result_exited(const atf_check_result_t *r)
+{
+ return atf_process_status_exited(&r->pimpl->m_status);
+}
+
+int
+atf_check_result_exitcode(const atf_check_result_t *r)
+{
+ return atf_process_status_exitstatus(&r->pimpl->m_status);
+}
+
+bool
+atf_check_result_signaled(const atf_check_result_t *r)
+{
+ return atf_process_status_signaled(&r->pimpl->m_status);
+}
+
+int
+atf_check_result_termsig(const atf_check_result_t *r)
+{
+ return atf_process_status_termsig(&r->pimpl->m_status);
+}
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+/* XXX: This function shouldn't be in this module. It messes with stdout
+ * and stderr, and it provides a very high-end interface. This belongs,
+ * probably, somewhere related to test cases (such as in the tc module). */
+atf_error_t
+atf_check_build_c_o(const char *sfile,
+ const char *ofile,
+ const char *const optargs[],
+ bool *success)
+{
+ atf_error_t err;
+ char **argv;
+
+ err = atf_build_c_o(sfile, ofile, optargs, &argv);
+ if (atf_is_error(err))
+ goto out;
+
+ err = check_build_run((const char *const *)argv, success);
+
+ atf_utils_free_charpp(argv);
+out:
+ return err;
+}
+
+atf_error_t
+atf_check_build_cpp(const char *sfile,
+ const char *ofile,
+ const char *const optargs[],
+ bool *success)
+{
+ atf_error_t err;
+ char **argv;
+
+ err = atf_build_cpp(sfile, ofile, optargs, &argv);
+ if (atf_is_error(err))
+ goto out;
+
+ err = check_build_run((const char *const *)argv, success);
+
+ atf_utils_free_charpp(argv);
+out:
+ return err;
+}
+
+atf_error_t
+atf_check_build_cxx_o(const char *sfile,
+ const char *ofile,
+ const char *const optargs[],
+ bool *success)
+{
+ atf_error_t err;
+ char **argv;
+
+ err = atf_build_cxx_o(sfile, ofile, optargs, &argv);
+ if (atf_is_error(err))
+ goto out;
+
+ err = check_build_run((const char *const *)argv, success);
+
+ atf_utils_free_charpp(argv);
+out:
+ return err;
+}
+
+atf_error_t
+atf_check_exec_array(const char *const *argv, atf_check_result_t *r)
+{
+ atf_error_t err;
+ atf_fs_path_t dir;
+
+ err = create_tmpdir(&dir);
+ if (atf_is_error(err))
+ goto out;
+
+ err = atf_check_result_init(r, argv, &dir);
+ if (atf_is_error(err)) {
+ atf_error_t err2 = atf_fs_rmdir(&dir);
+ INV(!atf_is_error(err2));
+ goto out;
+ }
+
+ err = fork_and_wait(argv, &r->pimpl->m_stdout, &r->pimpl->m_stderr,
+ &r->pimpl->m_status);
+ if (atf_is_error(err)) {
+ atf_check_result_fini(r);
+ goto out;
+ }
+
+ INV(!atf_is_error(err));
+
+ atf_fs_path_fini(&dir);
+out:
+ return err;
+}
diff --git a/unit/atf-src/atf-c/check.h b/unit/atf-src/atf-c/check.h
new file mode 100644
index 0000000..1eb2267
--- /dev/null
+++ b/unit/atf-src/atf-c/check.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_CHECK_H)
+#define ATF_C_CHECK_H
+
+#include <stdbool.h>
+
+#include <atf-c/error_fwd.h>
+
+/* ---------------------------------------------------------------------
+ * The "atf_check_result" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_check_result_impl;
+struct atf_check_result {
+ struct atf_check_result_impl *pimpl;
+};
+typedef struct atf_check_result atf_check_result_t;
+
+/* Construtors and destructors */
+void atf_check_result_fini(atf_check_result_t *);
+
+/* Getters */
+const char *atf_check_result_stdout(const atf_check_result_t *);
+const char *atf_check_result_stderr(const atf_check_result_t *);
+bool atf_check_result_exited(const atf_check_result_t *);
+int atf_check_result_exitcode(const atf_check_result_t *);
+bool atf_check_result_signaled(const atf_check_result_t *);
+int atf_check_result_termsig(const atf_check_result_t *);
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+atf_error_t atf_check_build_c_o(const char *, const char *,
+ const char *const [],
+ bool *);
+atf_error_t atf_check_build_cpp(const char *, const char *,
+ const char *const [],
+ bool *);
+atf_error_t atf_check_build_cxx_o(const char *, const char *,
+ const char *const [],
+ bool *);
+atf_error_t atf_check_exec_array(const char *const *, atf_check_result_t *);
+
+#endif /* !defined(ATF_C_CHECK_H) */
diff --git a/unit/atf-src/atf-c/check_test.c b/unit/atf-src/atf-c/check_test.c
new file mode 100644
index 0000000..adaca64
--- /dev/null
+++ b/unit/atf-src/atf-c/check_test.c
@@ -0,0 +1,521 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/check.h"
+
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/fs.h"
+#include "atf-c/detail/map.h"
+#include "atf-c/detail/process.h"
+#include "atf-c/detail/test_helpers.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+void
+do_exec(const atf_tc_t *tc, const char *helper_name, atf_check_result_t *r)
+{
+ atf_fs_path_t process_helpers;
+ const char *argv[3];
+
+ get_process_helpers_path(tc, false, &process_helpers);
+
+ argv[0] = atf_fs_path_cstring(&process_helpers);
+ argv[1] = helper_name;
+ argv[2] = NULL;
+ printf("Executing %s %s\n", argv[0], argv[1]);
+ RE(atf_check_exec_array(argv, r));
+
+ atf_fs_path_fini(&process_helpers);
+}
+
+static
+void
+do_exec_with_arg(const atf_tc_t *tc, const char *helper_name, const char *arg,
+ atf_check_result_t *r)
+{
+ atf_fs_path_t process_helpers;
+ const char *argv[4];
+
+ get_process_helpers_path(tc, false, &process_helpers);
+
+ argv[0] = atf_fs_path_cstring(&process_helpers);
+ argv[1] = helper_name;
+ argv[2] = arg;
+ argv[3] = NULL;
+ printf("Executing %s %s %s\n", argv[0], argv[1], argv[2]);
+ RE(atf_check_exec_array(argv, r));
+
+ atf_fs_path_fini(&process_helpers);
+}
+
+static
+void
+check_line(int fd, const char *exp)
+{
+ char *line = atf_utils_readline(fd);
+ ATF_CHECK(line != NULL);
+ ATF_CHECK_STREQ_MSG(exp, line, "read: '%s', expected: '%s'", line, exp);
+ free(line);
+}
+
+/* ---------------------------------------------------------------------
+ * Helper test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(h_build_c_o_ok);
+ATF_TC_HEAD(h_build_c_o_ok, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Helper test case for build_c_o");
+}
+ATF_TC_BODY(h_build_c_o_ok, tc)
+{
+ FILE *sfile;
+ bool success;
+
+ ATF_REQUIRE((sfile = fopen("test.c", "w")) != NULL);
+ fprintf(sfile, "#include <stdio.h>\n");
+ fclose(sfile);
+
+ RE(atf_check_build_c_o("test.c", "test.o", NULL, &success));
+ ATF_REQUIRE(success);
+}
+
+ATF_TC(h_build_c_o_fail);
+ATF_TC_HEAD(h_build_c_o_fail, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Helper test case for build_c_o");
+}
+ATF_TC_BODY(h_build_c_o_fail, tc)
+{
+ FILE *sfile;
+ bool success;
+
+ ATF_REQUIRE((sfile = fopen("test.c", "w")) != NULL);
+ fprintf(sfile, "void foo(void) { int a = UNDEFINED_SYMBOL; }\n");
+ fclose(sfile);
+
+ RE(atf_check_build_c_o("test.c", "test.o", NULL, &success));
+ ATF_REQUIRE(!success);
+}
+
+ATF_TC(h_build_cpp_ok);
+ATF_TC_HEAD(h_build_cpp_ok, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Helper test case for build_cpp");
+}
+ATF_TC_BODY(h_build_cpp_ok, tc)
+{
+ FILE *sfile;
+ bool success;
+ atf_fs_path_t test_p;
+
+ RE(atf_fs_path_init_fmt(&test_p, "test.p"));
+
+ ATF_REQUIRE((sfile = fopen("test.c", "w")) != NULL);
+ fprintf(sfile, "#define A foo\n");
+ fprintf(sfile, "#define B bar\n");
+ fprintf(sfile, "A B\n");
+ fclose(sfile);
+
+ RE(atf_check_build_cpp("test.c", atf_fs_path_cstring(&test_p), NULL,
+ &success));
+ ATF_REQUIRE(success);
+
+ atf_fs_path_fini(&test_p);
+}
+
+ATF_TC(h_build_cpp_fail);
+ATF_TC_HEAD(h_build_cpp_fail, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Helper test case for build_cpp");
+}
+ATF_TC_BODY(h_build_cpp_fail, tc)
+{
+ FILE *sfile;
+ bool success;
+
+ ATF_REQUIRE((sfile = fopen("test.c", "w")) != NULL);
+ fprintf(sfile, "#include \"./non-existent.h\"\n");
+ fclose(sfile);
+
+ RE(atf_check_build_cpp("test.c", "test.p", NULL, &success));
+ ATF_REQUIRE(!success);
+}
+
+ATF_TC(h_build_cxx_o_ok);
+ATF_TC_HEAD(h_build_cxx_o_ok, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Helper test case for build_cxx_o");
+}
+ATF_TC_BODY(h_build_cxx_o_ok, tc)
+{
+ FILE *sfile;
+ bool success;
+
+ ATF_REQUIRE((sfile = fopen("test.cpp", "w")) != NULL);
+ fprintf(sfile, "#include <iostream>\n");
+ fclose(sfile);
+
+ RE(atf_check_build_cxx_o("test.cpp", "test.o", NULL, &success));
+ ATF_REQUIRE(success);
+}
+
+ATF_TC(h_build_cxx_o_fail);
+ATF_TC_HEAD(h_build_cxx_o_fail, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Helper test case for build_cxx_o");
+}
+ATF_TC_BODY(h_build_cxx_o_fail, tc)
+{
+ FILE *sfile;
+ bool success;
+
+ ATF_REQUIRE((sfile = fopen("test.cpp", "w")) != NULL);
+ fprintf(sfile, "void foo(void) { int a = UNDEFINED_SYMBOL; }\n");
+ fclose(sfile);
+
+ RE(atf_check_build_cxx_o("test.cpp", "test.o", NULL, &success));
+ ATF_REQUIRE(!success);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+static
+void
+init_and_run_h_tc(atf_tc_t *tc, const atf_tc_pack_t *tcpack,
+ const char *outname, const char *errname)
+{
+ const char *const config[] = { NULL };
+
+ RE(atf_tc_init_pack(tc, tcpack, config));
+ run_h_tc(tc, outname, errname, "result");
+ atf_tc_fini(tc);
+}
+
+ATF_TC(build_c_o);
+ATF_TC_HEAD(build_c_o, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_check_build_c_o "
+ "function");
+}
+ATF_TC_BODY(build_c_o, tc)
+{
+ init_and_run_h_tc(&ATF_TC_NAME(h_build_c_o_ok),
+ &ATF_TC_PACK_NAME(h_build_c_o_ok), "stdout", "stderr");
+ ATF_CHECK(atf_utils_grep_file("-o test.o", "stdout"));
+ ATF_CHECK(atf_utils_grep_file("-c test.c", "stdout"));
+
+ init_and_run_h_tc(&ATF_TC_NAME(h_build_c_o_fail),
+ &ATF_TC_PACK_NAME(h_build_c_o_fail), "stdout", "stderr");
+ ATF_CHECK(atf_utils_grep_file("-o test.o", "stdout"));
+ ATF_CHECK(atf_utils_grep_file("-c test.c", "stdout"));
+ ATF_CHECK(atf_utils_grep_file("test.c", "stderr"));
+ ATF_CHECK(atf_utils_grep_file("UNDEFINED_SYMBOL", "stderr"));
+}
+
+ATF_TC(build_cpp);
+ATF_TC_HEAD(build_cpp, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_check_build_cpp "
+ "function");
+}
+ATF_TC_BODY(build_cpp, tc)
+{
+ init_and_run_h_tc(&ATF_TC_NAME(h_build_cpp_ok),
+ &ATF_TC_PACK_NAME(h_build_cpp_ok), "stdout", "stderr");
+ ATF_CHECK(atf_utils_grep_file("-o.*test.p", "stdout"));
+ ATF_CHECK(atf_utils_grep_file("test.c", "stdout"));
+ ATF_CHECK(atf_utils_grep_file("foo bar", "test.p"));
+
+ init_and_run_h_tc(&ATF_TC_NAME(h_build_cpp_fail),
+ &ATF_TC_PACK_NAME(h_build_cpp_fail), "stdout", "stderr");
+ ATF_CHECK(atf_utils_grep_file("-o test.p", "stdout"));
+ ATF_CHECK(atf_utils_grep_file("test.c", "stdout"));
+ ATF_CHECK(atf_utils_grep_file("test.c", "stderr"));
+ ATF_CHECK(atf_utils_grep_file("non-existent.h", "stderr"));
+}
+
+ATF_TC(build_cxx_o);
+ATF_TC_HEAD(build_cxx_o, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_check_build_cxx_o "
+ "function");
+}
+ATF_TC_BODY(build_cxx_o, tc)
+{
+ init_and_run_h_tc(&ATF_TC_NAME(h_build_cxx_o_ok),
+ &ATF_TC_PACK_NAME(h_build_cxx_o_ok), "stdout", "stderr");
+ ATF_CHECK(atf_utils_grep_file("-o test.o", "stdout"));
+ ATF_CHECK(atf_utils_grep_file("-c test.cpp", "stdout"));
+
+ init_and_run_h_tc(&ATF_TC_NAME(h_build_cxx_o_fail),
+ &ATF_TC_PACK_NAME(h_build_cxx_o_fail), "stdout", "stderr");
+ ATF_CHECK(atf_utils_grep_file("-o test.o", "stdout"));
+ ATF_CHECK(atf_utils_grep_file("-c test.cpp", "stdout"));
+ ATF_CHECK(atf_utils_grep_file("test.cpp", "stderr"));
+ ATF_CHECK(atf_utils_grep_file("UNDEFINED_SYMBOL", "stderr"));
+}
+
+ATF_TC(exec_array);
+ATF_TC_HEAD(exec_array, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that atf_check_exec_array "
+ "works properly");
+}
+ATF_TC_BODY(exec_array, tc)
+{
+ atf_fs_path_t process_helpers;
+ atf_check_result_t result;
+
+ get_process_helpers_path(tc, false, &process_helpers);
+
+ const char *argv[4];
+ argv[0] = atf_fs_path_cstring(&process_helpers);
+ argv[1] = "echo";
+ argv[2] = "test-message";
+ argv[3] = NULL;
+
+ RE(atf_check_exec_array(argv, &result));
+
+ ATF_CHECK(atf_check_result_exited(&result));
+ ATF_CHECK(atf_check_result_exitcode(&result) == EXIT_SUCCESS);
+
+ {
+ const char *path = atf_check_result_stdout(&result);
+ int fd = open(path, O_RDONLY);
+ ATF_CHECK(fd != -1);
+ check_line(fd, "test-message");
+ close(fd);
+ }
+
+ atf_check_result_fini(&result);
+ atf_fs_path_fini(&process_helpers);
+}
+
+ATF_TC(exec_cleanup);
+ATF_TC_HEAD(exec_cleanup, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that atf_check_exec_array "
+ "properly cleans up the temporary files it creates");
+}
+ATF_TC_BODY(exec_cleanup, tc)
+{
+ atf_fs_path_t out, err;
+ atf_check_result_t result;
+ bool exists;
+
+ do_exec(tc, "exit-success", &result);
+ RE(atf_fs_path_init_fmt(&out, "%s", atf_check_result_stdout(&result)));
+ RE(atf_fs_path_init_fmt(&err, "%s", atf_check_result_stderr(&result)));
+
+ RE(atf_fs_exists(&out, &exists)); ATF_CHECK(exists);
+ RE(atf_fs_exists(&err, &exists)); ATF_CHECK(exists);
+ atf_check_result_fini(&result);
+ RE(atf_fs_exists(&out, &exists)); ATF_CHECK(!exists);
+ RE(atf_fs_exists(&err, &exists)); ATF_CHECK(!exists);
+
+ atf_fs_path_fini(&err);
+ atf_fs_path_fini(&out);
+}
+
+ATF_TC(exec_exitstatus);
+ATF_TC_HEAD(exec_exitstatus, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that atf_check_exec_array "
+ "properly captures the exit status of the executed "
+ "command");
+}
+ATF_TC_BODY(exec_exitstatus, tc)
+{
+ {
+ atf_check_result_t result;
+ do_exec(tc, "exit-success", &result);
+ ATF_CHECK(atf_check_result_exited(&result));
+ ATF_CHECK(!atf_check_result_signaled(&result));
+ ATF_CHECK(atf_check_result_exitcode(&result) == EXIT_SUCCESS);
+ atf_check_result_fini(&result);
+ }
+
+ {
+ atf_check_result_t result;
+ do_exec(tc, "exit-failure", &result);
+ ATF_CHECK(atf_check_result_exited(&result));
+ ATF_CHECK(!atf_check_result_signaled(&result));
+ ATF_CHECK(atf_check_result_exitcode(&result) == EXIT_FAILURE);
+ atf_check_result_fini(&result);
+ }
+
+ {
+ atf_check_result_t result;
+ do_exec(tc, "exit-signal", &result);
+ ATF_CHECK(!atf_check_result_exited(&result));
+ ATF_CHECK(atf_check_result_signaled(&result));
+ ATF_CHECK(atf_check_result_termsig(&result) == SIGKILL);
+ atf_check_result_fini(&result);
+ }
+}
+
+ATF_TC(exec_stdout_stderr);
+ATF_TC_HEAD(exec_stdout_stderr, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that atf_check_exec_array "
+ "properly captures the stdout and stderr streams "
+ "of the child process");
+}
+ATF_TC_BODY(exec_stdout_stderr, tc)
+{
+ atf_check_result_t result1, result2;
+ const char *out1, *out2;
+ const char *err1, *err2;
+
+ do_exec_with_arg(tc, "stdout-stderr", "result1", &result1);
+ ATF_CHECK(atf_check_result_exited(&result1));
+ ATF_CHECK(atf_check_result_exitcode(&result1) == EXIT_SUCCESS);
+
+ do_exec_with_arg(tc, "stdout-stderr", "result2", &result2);
+ ATF_CHECK(atf_check_result_exited(&result2));
+ ATF_CHECK(atf_check_result_exitcode(&result2) == EXIT_SUCCESS);
+
+ out1 = atf_check_result_stdout(&result1);
+ out2 = atf_check_result_stdout(&result2);
+ err1 = atf_check_result_stderr(&result1);
+ err2 = atf_check_result_stderr(&result2);
+
+ ATF_CHECK(strstr(out1, "check.XXXXXX") == NULL);
+ ATF_CHECK(strstr(out2, "check.XXXXXX") == NULL);
+ ATF_CHECK(strstr(err1, "check.XXXXXX") == NULL);
+ ATF_CHECK(strstr(err2, "check.XXXXXX") == NULL);
+
+ ATF_CHECK(strstr(out1, "/check") != NULL);
+ ATF_CHECK(strstr(out2, "/check") != NULL);
+ ATF_CHECK(strstr(err1, "/check") != NULL);
+ ATF_CHECK(strstr(err2, "/check") != NULL);
+
+ ATF_CHECK(strstr(out1, "/stdout") != NULL);
+ ATF_CHECK(strstr(out2, "/stdout") != NULL);
+ ATF_CHECK(strstr(err1, "/stderr") != NULL);
+ ATF_CHECK(strstr(err2, "/stderr") != NULL);
+
+ ATF_CHECK(strcmp(out1, out2) != 0);
+ ATF_CHECK(strcmp(err1, err2) != 0);
+
+#define CHECK_LINES(path, outname, resname) \
+ do { \
+ int fd = open(path, O_RDONLY); \
+ ATF_CHECK(fd != -1); \
+ check_line(fd, "Line 1 to " outname " for " resname); \
+ check_line(fd, "Line 2 to " outname " for " resname); \
+ close(fd); \
+ } while (false)
+
+ CHECK_LINES(out1, "stdout", "result1");
+ CHECK_LINES(out2, "stdout", "result2");
+ CHECK_LINES(err1, "stderr", "result1");
+ CHECK_LINES(err2, "stderr", "result2");
+
+#undef CHECK_LINES
+
+ atf_check_result_fini(&result2);
+ atf_check_result_fini(&result1);
+}
+
+ATF_TC(exec_umask);
+ATF_TC_HEAD(exec_umask, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that atf_check_exec_array "
+ "correctly reports an error if the umask is too "
+ "restrictive to create temporary files");
+}
+ATF_TC_BODY(exec_umask, tc)
+{
+ atf_check_result_t result;
+ atf_fs_path_t process_helpers;
+ const char *argv[3];
+
+ get_process_helpers_path(tc, false, &process_helpers);
+ argv[0] = atf_fs_path_cstring(&process_helpers);
+ argv[1] = "exit-success";
+ argv[2] = NULL;
+
+ umask(0222);
+ atf_error_t err = atf_check_exec_array(argv, &result);
+ ATF_CHECK(atf_is_error(err));
+ ATF_CHECK(atf_error_is(err, "invalid_umask"));
+ atf_error_free(err);
+
+ atf_fs_path_fini(&process_helpers);
+}
+
+ATF_TC(exec_unknown);
+ATF_TC_HEAD(exec_unknown, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that running a non-existing "
+ "binary is handled correctly");
+}
+ATF_TC_BODY(exec_unknown, tc)
+{
+ const char *argv[2];
+ argv[0] = "/foo/bar/non-existent";
+ argv[1] = NULL;
+
+ atf_check_result_t result;
+ RE(atf_check_exec_array(argv, &result));
+ ATF_CHECK(atf_check_result_exited(&result));
+ ATF_CHECK(atf_check_result_exitcode(&result) == 127);
+ atf_check_result_fini(&result);
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Add the test cases for the free functions. */
+ ATF_TP_ADD_TC(tp, build_c_o);
+ ATF_TP_ADD_TC(tp, build_cpp);
+ ATF_TP_ADD_TC(tp, build_cxx_o);
+ ATF_TP_ADD_TC(tp, exec_array);
+ ATF_TP_ADD_TC(tp, exec_cleanup);
+ ATF_TP_ADD_TC(tp, exec_exitstatus);
+ ATF_TP_ADD_TC(tp, exec_stdout_stderr);
+ ATF_TP_ADD_TC(tp, exec_umask);
+ ATF_TP_ADD_TC(tp, exec_unknown);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/defs.h.in b/unit/atf-src/atf-c/defs.h.in
new file mode 100644
index 0000000..6059e7f
--- /dev/null
+++ b/unit/atf-src/atf-c/defs.h.in
@@ -0,0 +1,33 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_DEFS_H)
+#define ATF_C_DEFS_H
+
+#define ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(a, b) @ATTRIBUTE_FORMAT_PRINTF@
+#define ATF_DEFS_ATTRIBUTE_NORETURN @ATTRIBUTE_NORETURN@
+#define ATF_DEFS_ATTRIBUTE_UNUSED @ATTRIBUTE_UNUSED@
+
+#endif /* !defined(ATF_C_DEFS_H) */
diff --git a/unit/atf-src/atf-c/detail/Atffile b/unit/atf-src/atf-c/detail/Atffile
new file mode 100644
index 0000000..5fd8593
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/Atffile
@@ -0,0 +1,13 @@
+Content-Type: application/X-atf-atffile; version="1"
+
+prop: test-suite = atf
+
+tp: dynstr_test
+tp: env_test
+tp: fs_test
+tp: list_test
+tp: map_test
+tp: process_test
+tp: sanity_test
+tp: text_test
+tp: user_test
diff --git a/unit/atf-src/atf-c/detail/Kyuafile b/unit/atf-src/atf-c/detail/Kyuafile
new file mode 100644
index 0000000..bb741da
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/Kyuafile
@@ -0,0 +1,13 @@
+syntax("kyuafile", 1)
+
+test_suite("atf")
+
+atf_test_program{name="dynstr_test"}
+atf_test_program{name="env_test"}
+atf_test_program{name="fs_test"}
+atf_test_program{name="list_test"}
+atf_test_program{name="map_test"}
+atf_test_program{name="process_test"}
+atf_test_program{name="sanity_test"}
+atf_test_program{name="text_test"}
+atf_test_program{name="user_test"}
diff --git a/unit/atf-src/atf-c/detail/Makefile.am.inc b/unit/atf-src/atf-c/detail/Makefile.am.inc
new file mode 100644
index 0000000..ad45602
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/Makefile.am.inc
@@ -0,0 +1,99 @@
+# Copyright (c) 2007 The NetBSD Foundation, Inc.
+# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+
+libatf_c_la_SOURCES += atf-c/detail/dynstr.c \
+ atf-c/detail/dynstr.h \
+ atf-c/detail/env.c \
+ atf-c/detail/env.h \
+ atf-c/detail/fs.c \
+ atf-c/detail/fs.h \
+ atf-c/detail/list.c \
+ atf-c/detail/list.h \
+ atf-c/detail/map.c \
+ atf-c/detail/map.h \
+ atf-c/detail/process.c \
+ atf-c/detail/process.h \
+ atf-c/detail/sanity.c \
+ atf-c/detail/sanity.h \
+ atf-c/detail/text.c \
+ atf-c/detail/text.h \
+ atf-c/detail/tp_main.c \
+ atf-c/detail/user.c \
+ atf-c/detail/user.h
+
+tests_atf_c_detail_DATA = atf-c/detail/Atffile \
+ atf-c/detail/Kyuafile
+tests_atf_c_detaildir = $(pkgtestsdir)/atf-c/detail
+EXTRA_DIST += $(tests_atf_c_detail_DATA)
+
+noinst_LTLIBRARIES += atf-c/detail/libtest_helpers.la
+atf_c_detail_libtest_helpers_la_SOURCES = atf-c/detail/test_helpers.c \
+ atf-c/detail/test_helpers.h
+atf_c_detail_libtest_helpers_la_CPPFLAGS = -I$(srcdir)/atf-c \
+ -DATF_INCLUDEDIR=\"$(includedir)\"
+
+tests_atf_c_detail_PROGRAMS = atf-c/detail/dynstr_test
+atf_c_detail_dynstr_test_SOURCES = atf-c/detail/dynstr_test.c
+atf_c_detail_dynstr_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/env_test
+atf_c_detail_env_test_SOURCES = atf-c/detail/env_test.c
+atf_c_detail_env_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/fs_test
+atf_c_detail_fs_test_SOURCES = atf-c/detail/fs_test.c
+atf_c_detail_fs_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/list_test
+atf_c_detail_list_test_SOURCES = atf-c/detail/list_test.c
+atf_c_detail_list_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/map_test
+atf_c_detail_map_test_SOURCES = atf-c/detail/map_test.c
+atf_c_detail_map_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/process_helpers
+atf_c_detail_process_helpers_SOURCES = atf-c/detail/process_helpers.c
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/process_test
+atf_c_detail_process_test_SOURCES = atf-c/detail/process_test.c
+atf_c_detail_process_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/sanity_test
+atf_c_detail_sanity_test_SOURCES = atf-c/detail/sanity_test.c
+atf_c_detail_sanity_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/text_test
+atf_c_detail_text_test_SOURCES = atf-c/detail/text_test.c
+atf_c_detail_text_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/user_test
+atf_c_detail_user_test_SOURCES = atf-c/detail/user_test.c
+atf_c_detail_user_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la
+
+tests_atf_c_detail_PROGRAMS += atf-c/detail/version_helper
+atf_c_detail_version_helper_SOURCES = atf-c/detail/version_helper.c
+
+# vim: syntax=make:noexpandtab:shiftwidth=8:softtabstop=8
diff --git a/unit/atf-src/atf-c/detail/dynstr.c b/unit/atf-src/atf-c/detail/dynstr.c
new file mode 100644
index 0000000..424a1c9
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/dynstr.c
@@ -0,0 +1,394 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/dynstr.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "atf-c/detail/sanity.h"
+#include "atf-c/detail/text.h"
+#include "atf-c/error.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+atf_error_t
+resize(atf_dynstr_t *ad, size_t newsize)
+{
+ char *newdata;
+ atf_error_t err;
+
+ PRE(newsize > ad->m_datasize);
+
+ newdata = (char *)malloc(newsize);
+ if (newdata == NULL) {
+ err = atf_no_memory_error();
+ } else {
+ strcpy(newdata, ad->m_data);
+ free(ad->m_data);
+ ad->m_data = newdata;
+ ad->m_datasize = newsize;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+static
+atf_error_t
+prepend_or_append(atf_dynstr_t *ad, const char *fmt, va_list ap,
+ bool prepend)
+{
+ char *aux;
+ atf_error_t err;
+ size_t newlen;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = atf_text_format_ap(&aux, fmt, ap2);
+ va_end(ap2);
+ if (atf_is_error(err))
+ goto out;
+ newlen = ad->m_length + strlen(aux);
+
+ if (newlen + sizeof(char) > ad->m_datasize) {
+ err = resize(ad, newlen + sizeof(char));
+ if (atf_is_error(err))
+ goto out_free;
+ }
+
+ if (prepend) {
+ memmove(ad->m_data + strlen(aux), ad->m_data, ad->m_length + 1);
+ memcpy(ad->m_data, aux, strlen(aux));
+ } else
+ strcpy(ad->m_data + ad->m_length, aux);
+ ad->m_length = newlen;
+ err = atf_no_error();
+
+out_free:
+ free(aux);
+out:
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_dynstr" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constants.
+ */
+
+const size_t atf_dynstr_npos = SIZE_MAX;
+
+/*
+ * Constructors and destructors.
+ */
+
+atf_error_t
+atf_dynstr_init(atf_dynstr_t *ad)
+{
+ atf_error_t err;
+
+ ad->m_data = (char *)malloc(sizeof(char));
+ if (ad->m_data == NULL) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ ad->m_data[0] = '\0';
+ ad->m_datasize = 1;
+ ad->m_length = 0;
+ err = atf_no_error();
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_dynstr_init_ap(atf_dynstr_t *ad, const char *fmt, va_list ap)
+{
+ atf_error_t err;
+
+ ad->m_datasize = strlen(fmt) + 1;
+ ad->m_length = 0;
+
+ do {
+ va_list ap2;
+ int ret;
+
+ ad->m_datasize *= 2;
+ ad->m_data = (char *)malloc(ad->m_datasize);
+ if (ad->m_data == NULL) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ va_copy(ap2, ap);
+ ret = vsnprintf(ad->m_data, ad->m_datasize, fmt, ap2);
+ va_end(ap2);
+ if (ret < 0) {
+ free(ad->m_data);
+ err = atf_libc_error(errno, "Cannot format string");
+ goto out;
+ }
+
+ INV(ret >= 0);
+ if ((size_t)ret >= ad->m_datasize) {
+ free(ad->m_data);
+ ad->m_data = NULL;
+ }
+ ad->m_length = ret;
+ } while (ad->m_length >= ad->m_datasize);
+
+ err = atf_no_error();
+out:
+ POST(atf_is_error(err) || ad->m_data != NULL);
+ return err;
+}
+
+atf_error_t
+atf_dynstr_init_fmt(atf_dynstr_t *ad, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_dynstr_init_ap(ad, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+atf_error_t
+atf_dynstr_init_raw(atf_dynstr_t *ad, const void *mem, size_t memlen)
+{
+ atf_error_t err;
+
+ if (memlen >= SIZE_MAX - 1) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ ad->m_data = (char *)malloc(memlen + 1);
+ if (ad->m_data == NULL) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ ad->m_datasize = memlen + 1;
+ memcpy(ad->m_data, mem, memlen);
+ ad->m_data[memlen] = '\0';
+ ad->m_length = strlen(ad->m_data);
+ INV(ad->m_length <= memlen);
+ err = atf_no_error();
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_dynstr_init_rep(atf_dynstr_t *ad, size_t len, char ch)
+{
+ atf_error_t err;
+
+ if (len == SIZE_MAX) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ ad->m_datasize = (len + 1) * sizeof(char);
+ ad->m_data = (char *)malloc(ad->m_datasize);
+ if (ad->m_data == NULL) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ memset(ad->m_data, ch, len);
+ ad->m_data[len] = '\0';
+ ad->m_length = len;
+ err = atf_no_error();
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_dynstr_init_substr(atf_dynstr_t *ad, const atf_dynstr_t *src,
+ size_t beg, size_t end)
+{
+ if (beg > src->m_length)
+ beg = src->m_length;
+
+ if (end == atf_dynstr_npos || end > src->m_length)
+ end = src->m_length;
+
+ return atf_dynstr_init_raw(ad, src->m_data + beg, end - beg);
+}
+
+atf_error_t
+atf_dynstr_copy(atf_dynstr_t *dest, const atf_dynstr_t *src)
+{
+ atf_error_t err;
+
+ dest->m_data = (char *)malloc(src->m_datasize);
+ if (dest->m_data == NULL)
+ err = atf_no_memory_error();
+ else {
+ memcpy(dest->m_data, src->m_data, src->m_datasize);
+ dest->m_datasize = src->m_datasize;
+ dest->m_length = src->m_length;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+void
+atf_dynstr_fini(atf_dynstr_t *ad)
+{
+ INV(ad->m_data != NULL);
+ free(ad->m_data);
+}
+
+char *
+atf_dynstr_fini_disown(atf_dynstr_t *ad)
+{
+ INV(ad->m_data != NULL);
+ return ad->m_data;
+}
+
+/*
+ * Getters.
+ */
+
+const char *
+atf_dynstr_cstring(const atf_dynstr_t *ad)
+{
+ return ad->m_data;
+}
+
+size_t
+atf_dynstr_length(const atf_dynstr_t *ad)
+{
+ return ad->m_length;
+}
+
+size_t
+atf_dynstr_rfind_ch(const atf_dynstr_t *ad, char ch)
+{
+ size_t pos;
+
+ for (pos = ad->m_length; pos > 0 && ad->m_data[pos - 1] != ch; pos--)
+ ;
+
+ return pos == 0 ? atf_dynstr_npos : pos - 1;
+}
+
+/*
+ * Modifiers.
+ */
+
+atf_error_t
+atf_dynstr_append_ap(atf_dynstr_t *ad, const char *fmt, va_list ap)
+{
+ atf_error_t err;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = prepend_or_append(ad, fmt, ap2, false);
+ va_end(ap2);
+
+ return err;
+}
+
+atf_error_t
+atf_dynstr_append_fmt(atf_dynstr_t *ad, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = prepend_or_append(ad, fmt, ap, false);
+ va_end(ap);
+
+ return err;
+}
+
+void
+atf_dynstr_clear(atf_dynstr_t *ad)
+{
+ ad->m_data[0] = '\0';
+ ad->m_length = 0;
+}
+
+atf_error_t
+atf_dynstr_prepend_ap(atf_dynstr_t *ad, const char *fmt, va_list ap)
+{
+ atf_error_t err;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = prepend_or_append(ad, fmt, ap2, true);
+ va_end(ap2);
+
+ return err;
+}
+
+atf_error_t
+atf_dynstr_prepend_fmt(atf_dynstr_t *ad, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = prepend_or_append(ad, fmt, ap, true);
+ va_end(ap);
+
+ return err;
+}
+
+/*
+ * Operators.
+ */
+
+bool
+atf_equal_dynstr_cstring(const atf_dynstr_t *ad, const char *str)
+{
+ return strcmp(ad->m_data, str) == 0;
+}
+
+bool
+atf_equal_dynstr_dynstr(const atf_dynstr_t *s1, const atf_dynstr_t *s2)
+{
+ return strcmp(s1->m_data, s2->m_data) == 0;
+}
diff --git a/unit/atf-src/atf-c/detail/dynstr.h b/unit/atf-src/atf-c/detail/dynstr.h
new file mode 100644
index 0000000..60b8e42
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/dynstr.h
@@ -0,0 +1,77 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_DETAIL_DYNSTR_H)
+#define ATF_C_DETAIL_DYNSTR_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <atf-c/error_fwd.h>
+
+/* ---------------------------------------------------------------------
+ * The "atf_dynstr" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_dynstr {
+ char *m_data;
+ size_t m_datasize;
+ size_t m_length;
+};
+typedef struct atf_dynstr atf_dynstr_t;
+
+/* Constants */
+extern const size_t atf_dynstr_npos;
+
+/* Constructors and destructors */
+atf_error_t atf_dynstr_init(atf_dynstr_t *);
+atf_error_t atf_dynstr_init_ap(atf_dynstr_t *, const char *, va_list);
+atf_error_t atf_dynstr_init_fmt(atf_dynstr_t *, const char *, ...);
+atf_error_t atf_dynstr_init_raw(atf_dynstr_t *, const void *, size_t);
+atf_error_t atf_dynstr_init_rep(atf_dynstr_t *, size_t, char);
+atf_error_t atf_dynstr_init_substr(atf_dynstr_t *, const atf_dynstr_t *,
+ size_t, size_t);
+atf_error_t atf_dynstr_copy(atf_dynstr_t *, const atf_dynstr_t *);
+void atf_dynstr_fini(atf_dynstr_t *);
+char *atf_dynstr_fini_disown(atf_dynstr_t *);
+
+/* Getters */
+const char *atf_dynstr_cstring(const atf_dynstr_t *);
+size_t atf_dynstr_length(const atf_dynstr_t *);
+size_t atf_dynstr_rfind_ch(const atf_dynstr_t *, char);
+
+/* Modifiers */
+atf_error_t atf_dynstr_append_ap(atf_dynstr_t *, const char *, va_list);
+atf_error_t atf_dynstr_append_fmt(atf_dynstr_t *, const char *, ...);
+void atf_dynstr_clear(atf_dynstr_t *);
+atf_error_t atf_dynstr_prepend_ap(atf_dynstr_t *, const char *, va_list);
+atf_error_t atf_dynstr_prepend_fmt(atf_dynstr_t *, const char *, ...);
+
+/* Operators */
+bool atf_equal_dynstr_cstring(const atf_dynstr_t *, const char *);
+bool atf_equal_dynstr_dynstr(const atf_dynstr_t *, const atf_dynstr_t *);
+
+#endif /* !defined(ATF_C_DETAIL_DYNSTR_H) */
diff --git a/unit/atf-src/atf-c/detail/dynstr_test.c b/unit/atf-src/atf-c/detail/dynstr_test.c
new file mode 100644
index 0000000..2512752
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/dynstr_test.c
@@ -0,0 +1,634 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/dynstr.h"
+
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+
+/* ---------------------------------------------------------------------
+ * Tests for the "atf_dynstr" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors and destructors.
+ */
+
+ATF_TC(init);
+ATF_TC_HEAD(init, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the empty constructor");
+}
+ATF_TC_BODY(init, tc)
+{
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init(&str));
+ ATF_REQUIRE_EQ(atf_dynstr_length(&str), 0);
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0);
+ atf_dynstr_fini(&str);
+}
+
+static
+void
+init_fmt(atf_dynstr_t *str, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ RE(atf_dynstr_init_ap(str, fmt, ap));
+ va_end(ap);
+}
+
+ATF_TC(init_ap);
+ATF_TC_HEAD(init_ap, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the formatted constructor using "
+ "a va_list argument");
+}
+ATF_TC_BODY(init_ap, tc)
+{
+ atf_dynstr_t str;
+
+ init_fmt(&str, "String 1");
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 1") == 0);
+ atf_dynstr_fini(&str);
+
+ init_fmt(&str, "String %d", 2);
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 2") == 0);
+ atf_dynstr_fini(&str);
+
+ init_fmt(&str, "%s %d", "String", 3);
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 3") == 0);
+ atf_dynstr_fini(&str);
+
+ init_fmt(&str, "%s%s%s%s%s%s%s", "This ", "should ", "be ", "a ",
+ "large ", "string ", "aaaabbbbccccdddd");
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str),
+ "This should be a large string "
+ "aaaabbbbccccdddd") == 0);
+ atf_dynstr_fini(&str);
+}
+
+ATF_TC(init_fmt);
+ATF_TC_HEAD(init_fmt, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the formatted constructor using "
+ "a variable list of parameters");
+}
+ATF_TC_BODY(init_fmt, tc)
+{
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_fmt(&str, "String 1"));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 1") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_fmt(&str, "String %d", 2));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 2") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_fmt(&str, "%s %d", "String", 3));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 3") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_fmt(&str, "%s%s%s%s%s%s%s", "This ", "should ",
+ "be ", "a ", "large ", "string ",
+ "aaaabbbbccccdddd"));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str),
+ "This should be a large string "
+ "aaaabbbbccccdddd") == 0);
+ atf_dynstr_fini(&str);
+}
+
+ATF_TC(init_raw);
+ATF_TC_HEAD(init_raw, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of a string "
+ "using a raw memory pointer");
+}
+ATF_TC_BODY(init_raw, tc)
+{
+ const char *src = "String 1, String 2";
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_raw(&str, src, 0));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_raw(&str, src, 8));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 1") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_raw(&str, src + 10, 8));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 2") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_raw(&str, "String\0Lost", 11));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String") == 0);
+ atf_dynstr_fini(&str);
+
+ {
+ atf_error_t err = atf_dynstr_init_raw(&str, "NULL", SIZE_MAX - 1);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "no_memory"));
+ atf_error_free(err);
+ }
+}
+
+ATF_TC(init_rep);
+ATF_TC_HEAD(init_rep, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of a string by "
+ "repeating characters");
+}
+ATF_TC_BODY(init_rep, tc)
+{
+ const size_t maxlen = 8192;
+ char buf[maxlen + 1];
+ size_t i;
+
+ buf[0] = '\0';
+
+ for (i = 0; i < maxlen; i++) {
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_rep(&str, i, 'a'));
+
+ if (strcmp(atf_dynstr_cstring(&str), buf) != 0) {
+ fprintf(stderr, "Failed at iteration %zd\n", i);
+ atf_tc_fail("Failed to construct dynstr by repeating %zd "
+ "times the '%c' character", i, 'a');
+ }
+
+ atf_dynstr_fini(&str);
+
+ strcat(buf, "a");
+ }
+
+ {
+ atf_dynstr_t str;
+ atf_error_t err;
+
+ err = atf_dynstr_init_rep(&str, SIZE_MAX, 'a');
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "no_memory"));
+ atf_error_free(err);
+
+ err = atf_dynstr_init_rep(&str, SIZE_MAX - 1, 'a');
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "no_memory"));
+ atf_error_free(err);
+ }
+}
+
+ATF_TC(init_substr);
+ATF_TC_HEAD(init_substr, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of a string "
+ "using a substring of another one");
+}
+ATF_TC_BODY(init_substr, tc)
+{
+ atf_dynstr_t src;
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_fmt(&src, "Str 1, Str 2"));
+
+ RE(atf_dynstr_init_substr(&str, &src, 0, 0));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_substr(&str, &src, 0, atf_dynstr_npos));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 1, Str 2") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_substr(&str, &src, 0, 100));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 1, Str 2") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_substr(&str, &src, 0, 5));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 1") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_substr(&str, &src, 100, atf_dynstr_npos));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_substr(&str, &src, 7, atf_dynstr_npos));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 2") == 0);
+ atf_dynstr_fini(&str);
+
+ atf_dynstr_fini(&src);
+}
+
+ATF_TC(copy);
+ATF_TC_HEAD(copy, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_dynstr_copy constructor");
+}
+ATF_TC_BODY(copy, tc)
+{
+ atf_dynstr_t str, str2;
+
+ RE(atf_dynstr_init_fmt(&str, "Test string"));
+ RE(atf_dynstr_copy(&str2, &str));
+
+ ATF_REQUIRE(atf_equal_dynstr_dynstr(&str, &str2));
+
+ RE(atf_dynstr_append_fmt(&str2, " non-shared text"));
+
+ ATF_REQUIRE(!atf_equal_dynstr_dynstr(&str, &str2));
+
+ atf_dynstr_fini(&str2);
+ atf_dynstr_fini(&str);
+}
+
+ATF_TC(fini_disown);
+ATF_TC_HEAD(fini_disown, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks grabbing ownership of the "
+ "internal plain C string");
+}
+ATF_TC_BODY(fini_disown, tc)
+{
+ const char *cstr;
+ char *cstr2;
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_fmt(&str, "Test string 1"));
+ cstr = atf_dynstr_cstring(&str);
+ cstr2 = atf_dynstr_fini_disown(&str);
+
+ ATF_REQUIRE_EQ(cstr, cstr2);
+ free(cstr2);
+}
+
+/*
+ * Getters.
+ */
+
+ATF_TC(cstring);
+ATF_TC_HEAD(cstring, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the method to obtain a plain C "
+ "string");
+}
+ATF_TC_BODY(cstring, tc)
+{
+ const char *cstr;
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_fmt(&str, "Test string 1"));
+ cstr = atf_dynstr_cstring(&str);
+ ATF_REQUIRE(cstr != NULL);
+ ATF_REQUIRE(strcmp(cstr, "Test string 1") == 0);
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_fmt(&str, "Test string 2"));
+ cstr = atf_dynstr_cstring(&str);
+ ATF_REQUIRE(cstr != NULL);
+ ATF_REQUIRE(strcmp(cstr, "Test string 2") == 0);
+ atf_dynstr_fini(&str);
+}
+
+ATF_TC(length);
+ATF_TC_HEAD(length, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the method to obtain the length");
+}
+ATF_TC_BODY(length, tc)
+{
+ size_t i;
+
+ for (i = 0; i < 8192; i++) {
+ atf_dynstr_t str;
+ RE(atf_dynstr_init_rep(&str, i, 'a'));
+ ATF_REQUIRE_EQ(atf_dynstr_length(&str), i);
+ atf_dynstr_fini(&str);
+ }
+}
+
+ATF_TC(rfind_ch);
+ATF_TC_HEAD(rfind_ch, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the method to locate the first "
+ "occurrence of a character starting from the end");
+}
+ATF_TC_BODY(rfind_ch, tc)
+{
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init_fmt(&str, "Foo1/Bar2/,.Baz"));
+
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, '\0'), atf_dynstr_npos);
+
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, '0'), atf_dynstr_npos);
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'b'), atf_dynstr_npos);
+
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'F'), 0);
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, '/'), 9);
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'a'), 13);
+ ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'z'), 14);
+
+ atf_dynstr_fini(&str);
+}
+
+/*
+ * Modifiers.
+ */
+
+static
+void
+check_append(atf_error_t (*append)(atf_dynstr_t *, const char *, ...))
+{
+ const size_t maxlen = 8192;
+ char buf[maxlen + 1];
+ size_t i;
+ atf_dynstr_t str;
+
+ printf("Appending with plain string\n");
+ buf[0] = '\0';
+ RE(atf_dynstr_init(&str));
+ for (i = 0; i < maxlen; i++) {
+ if (strcmp(atf_dynstr_cstring(&str), buf) != 0) {
+ fprintf(stderr, "Failed at iteration %zd\n", i);
+ atf_tc_fail("Failed to append character at iteration %zd", i);
+ }
+
+ RE(append(&str, "a"));
+ strcat(buf, "a");
+ }
+ atf_dynstr_fini(&str);
+
+ printf("Appending with formatted string\n");
+ buf[0] = '\0';
+ RE(atf_dynstr_init(&str));
+ for (i = 0; i < maxlen; i++) {
+ if (strcmp(atf_dynstr_cstring(&str), buf) != 0) {
+ fprintf(stderr, "Failed at iteration %zd\n", i);
+ atf_tc_fail("Failed to append character at iteration %zd", i);
+ }
+
+ RE(append(&str, "%s", "a"));
+ strcat(buf, "a");
+ }
+ atf_dynstr_fini(&str);
+}
+
+static
+atf_error_t
+append_ap_aux(atf_dynstr_t *str, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_dynstr_append_ap(str, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+ATF_TC(append_ap);
+ATF_TC_HEAD(append_ap, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that appending a string to "
+ "another one works");
+}
+ATF_TC_BODY(append_ap, tc)
+{
+ check_append(append_ap_aux);
+}
+
+ATF_TC(append_fmt);
+ATF_TC_HEAD(append_fmt, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that appending a string to "
+ "another one works");
+}
+ATF_TC_BODY(append_fmt, tc)
+{
+ check_append(atf_dynstr_append_fmt);
+}
+
+ATF_TC(clear);
+ATF_TC_HEAD(clear, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks clearing a string");
+}
+ATF_TC_BODY(clear, tc)
+{
+ atf_dynstr_t str;
+
+ printf("Clear an empty string\n");
+ RE(atf_dynstr_init(&str));
+ atf_dynstr_clear(&str);
+ ATF_REQUIRE_EQ(atf_dynstr_length(&str), 0);
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0);
+ atf_dynstr_fini(&str);
+
+ printf("Clear a non-empty string\n");
+ RE(atf_dynstr_init_fmt(&str, "Not empty"));
+ ATF_REQUIRE_EQ(atf_dynstr_length(&str), strlen("Not empty"));
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Not empty") == 0);
+ atf_dynstr_clear(&str);
+ ATF_REQUIRE_EQ(atf_dynstr_length(&str), 0);
+ ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0);
+ atf_dynstr_fini(&str);
+}
+
+static
+void
+check_prepend(atf_error_t (*prepend)(atf_dynstr_t *, const char *, ...))
+{
+ const size_t maxlen = 8192;
+ char buf[maxlen + 1];
+ size_t i;
+ atf_dynstr_t str;
+
+ printf("Prepending with plain string\n");
+ buf[0] = '\0';
+ RE(atf_dynstr_init(&str));
+ for (i = 0; i < maxlen; i++) {
+ if (strcmp(atf_dynstr_cstring(&str), buf) != 0) {
+ fprintf(stderr, "Failed at iteration %zd\n", i);
+ atf_tc_fail("Failed to prepend character at iteration %zd", i);
+ }
+
+ memmove(buf + 1, buf, i + 1);
+ if (i % 2 == 0) {
+ RE(prepend(&str, "%s", "a"));
+ buf[0] = 'a';
+ } else {
+ RE(prepend(&str, "%s", "b"));
+ buf[0] = 'b';
+ }
+ }
+ atf_dynstr_fini(&str);
+
+ printf("Prepending with formatted string\n");
+ buf[0] = '\0';
+ RE(atf_dynstr_init(&str));
+ for (i = 0; i < maxlen; i++) {
+ if (strcmp(atf_dynstr_cstring(&str), buf) != 0) {
+ fprintf(stderr, "Failed at iteration %zd\n", i);
+ atf_tc_fail("Failed to prepend character at iteration %zd", i);
+ }
+
+ memmove(buf + 1, buf, i + 1);
+ if (i % 2 == 0) {
+ RE(prepend(&str, "%s", "a"));
+ buf[0] = 'a';
+ } else {
+ RE(prepend(&str, "%s", "b"));
+ buf[0] = 'b';
+ }
+ }
+ atf_dynstr_fini(&str);
+}
+
+static
+atf_error_t
+prepend_ap_aux(atf_dynstr_t *str, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_dynstr_prepend_ap(str, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+ATF_TC(prepend_ap);
+ATF_TC_HEAD(prepend_ap, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that prepending a string to "
+ "another one works");
+}
+ATF_TC_BODY(prepend_ap, tc)
+{
+ check_prepend(prepend_ap_aux);
+}
+
+ATF_TC(prepend_fmt);
+ATF_TC_HEAD(prepend_fmt, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that prepending a string to "
+ "another one works");
+}
+ATF_TC_BODY(prepend_fmt, tc)
+{
+ check_prepend(atf_dynstr_prepend_fmt);
+}
+
+/*
+ * Operators.
+ */
+
+ATF_TC(equal_cstring);
+ATF_TC_HEAD(equal_cstring, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_equal_dynstr_cstring "
+ "function");
+}
+ATF_TC_BODY(equal_cstring, tc)
+{
+ atf_dynstr_t str;
+
+ RE(atf_dynstr_init(&str));
+ ATF_REQUIRE( atf_equal_dynstr_cstring(&str, ""));
+ ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "Test"));
+ atf_dynstr_fini(&str);
+
+ RE(atf_dynstr_init_fmt(&str, "Test"));
+ ATF_REQUIRE( atf_equal_dynstr_cstring(&str, "Test"));
+ ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, ""));
+ ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "Tes"));
+ ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "Test "));
+ atf_dynstr_fini(&str);
+}
+
+ATF_TC(equal_dynstr);
+ATF_TC_HEAD(equal_dynstr, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_equal_dynstr_dynstr "
+ "function");
+}
+ATF_TC_BODY(equal_dynstr, tc)
+{
+ atf_dynstr_t str, str2;
+
+ RE(atf_dynstr_init(&str));
+ RE(atf_dynstr_init_fmt(&str2, "Test"));
+ ATF_REQUIRE( atf_equal_dynstr_dynstr(&str, &str));
+ ATF_REQUIRE(!atf_equal_dynstr_dynstr(&str, &str2));
+ atf_dynstr_fini(&str2);
+ atf_dynstr_fini(&str);
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Constructors and destructors. */
+ ATF_TP_ADD_TC(tp, init);
+ ATF_TP_ADD_TC(tp, init_ap);
+ ATF_TP_ADD_TC(tp, init_fmt);
+ ATF_TP_ADD_TC(tp, init_raw);
+ ATF_TP_ADD_TC(tp, init_rep);
+ ATF_TP_ADD_TC(tp, init_substr);
+ ATF_TP_ADD_TC(tp, copy);
+ ATF_TP_ADD_TC(tp, fini_disown);
+
+ /* Getters. */
+ ATF_TP_ADD_TC(tp, cstring);
+ ATF_TP_ADD_TC(tp, length);
+ ATF_TP_ADD_TC(tp, rfind_ch);
+
+ /* Modifiers. */
+ ATF_TP_ADD_TC(tp, append_ap);
+ ATF_TP_ADD_TC(tp, append_fmt);
+ ATF_TP_ADD_TC(tp, clear);
+ ATF_TP_ADD_TC(tp, prepend_ap);
+ ATF_TP_ADD_TC(tp, prepend_fmt);
+
+ /* Operators. */
+ ATF_TP_ADD_TC(tp, equal_cstring);
+ ATF_TP_ADD_TC(tp, equal_dynstr);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/env.c b/unit/atf-src/atf-c/detail/env.c
new file mode 100644
index 0000000..8ee3d69
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/env.c
@@ -0,0 +1,114 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/env.h"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "atf-c/detail/sanity.h"
+#include "atf-c/detail/text.h"
+#include "atf-c/error.h"
+
+const char *
+atf_env_get(const char *name)
+{
+ const char* val = getenv(name);
+ PRE(val != NULL);
+ return val;
+}
+
+const char *
+atf_env_get_with_default(const char *name, const char *default_value)
+{
+ const char* val = getenv(name);
+ if (val == NULL)
+ return default_value;
+ else
+ return val;
+}
+
+bool
+atf_env_has(const char *name)
+{
+ return getenv(name) != NULL;
+}
+
+atf_error_t
+atf_env_set(const char *name, const char *val)
+{
+ atf_error_t err;
+
+#if defined(HAVE_SETENV)
+ if (setenv(name, val, 1) == -1)
+ err = atf_libc_error(errno, "Cannot set environment variable "
+ "'%s' to '%s'", name, val);
+ else
+ err = atf_no_error();
+#elif defined(HAVE_PUTENV)
+ char *buf;
+
+ err = atf_text_format(&buf, "%s=%s", name, val);
+ if (!atf_is_error(err)) {
+ if (putenv(buf) == -1)
+ err = atf_libc_error(errno, "Cannot set environment variable "
+ "'%s' to '%s'", name, val);
+ free(buf);
+ }
+#else
+# error "Don't know how to set an environment variable."
+#endif
+
+ return err;
+}
+
+atf_error_t
+atf_env_unset(const char *name)
+{
+ atf_error_t err;
+
+#if defined(HAVE_UNSETENV)
+ unsetenv(name);
+ err = atf_no_error();
+#elif defined(HAVE_PUTENV)
+ char *buf;
+
+ err = atf_text_format(&buf, "%s=", name);
+ if (!atf_is_error(err)) {
+ if (putenv(buf) == -1)
+ err = atf_libc_error(errno, "Cannot unset environment variable"
+ " '%s'", name);
+ free(buf);
+ }
+#else
+# error "Don't know how to unset an environment variable."
+#endif
+
+ return err;
+}
diff --git a/unit/atf-src/atf-c/detail/env.h b/unit/atf-src/atf-c/detail/env.h
new file mode 100644
index 0000000..5d29440
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/env.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_DETAIL_ENV_H)
+#define ATF_C_DETAIL_ENV_H
+
+#include <stdbool.h>
+
+#include <atf-c/error_fwd.h>
+
+const char *atf_env_get(const char *);
+const char *atf_env_get_with_default(const char *, const char *);
+bool atf_env_has(const char *);
+atf_error_t atf_env_set(const char *, const char *);
+atf_error_t atf_env_unset(const char *);
+
+#endif /* !defined(ATF_C_DETAIL_ENV_H) */
diff --git a/unit/atf-src/atf-c/detail/env_test.c b/unit/atf-src/atf-c/detail/env_test.c
new file mode 100644
index 0000000..3909c29
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/env_test.c
@@ -0,0 +1,133 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/env.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+#include "atf-c/detail/text.h"
+
+/* ---------------------------------------------------------------------
+ * Test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(has);
+ATF_TC_HEAD(has, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_env_has function");
+}
+ATF_TC_BODY(has, tc)
+{
+ ATF_REQUIRE(atf_env_has("PATH"));
+ ATF_REQUIRE(!atf_env_has("_UNDEFINED_VARIABLE_"));
+}
+
+ATF_TC(get);
+ATF_TC_HEAD(get, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_env_get function");
+}
+ATF_TC_BODY(get, tc)
+{
+ const char *val;
+
+ ATF_REQUIRE(atf_env_has("PATH"));
+
+ val = atf_env_get("PATH");
+ ATF_REQUIRE(strlen(val) > 0);
+ ATF_REQUIRE(strchr(val, ':') != NULL);
+}
+
+ATF_TC(get_with_default);
+ATF_TC_HEAD(get_with_default, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_env_get_with_default "
+ "function");
+}
+ATF_TC_BODY(get_with_default, tc)
+{
+ const char *val;
+
+ ATF_REQUIRE(atf_env_has("PATH"));
+
+ val = atf_env_get_with_default("PATH", "unknown");
+ ATF_REQUIRE(strcmp(val, "unknown") != 0);
+
+ val = atf_env_get_with_default("_UNKNOWN_VARIABLE_", "foo bar");
+ ATF_REQUIRE(strcmp(val, "foo bar") == 0);
+}
+
+ATF_TC(set);
+ATF_TC_HEAD(set, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_env_set function");
+}
+ATF_TC_BODY(set, tc)
+{
+ char *oldval;
+
+ ATF_REQUIRE(atf_env_has("PATH"));
+ RE(atf_text_format(&oldval, "%s", atf_env_get("PATH")));
+ RE(atf_env_set("PATH", "foo-bar"));
+ ATF_REQUIRE(strcmp(atf_env_get("PATH"), oldval) != 0);
+ ATF_REQUIRE(strcmp(atf_env_get("PATH"), "foo-bar") == 0);
+ free(oldval);
+
+ ATF_REQUIRE(!atf_env_has("_UNDEFINED_VARIABLE_"));
+ RE(atf_env_set("_UNDEFINED_VARIABLE_", "foo2-bar2"));
+ ATF_REQUIRE(strcmp(atf_env_get("_UNDEFINED_VARIABLE_"),
+ "foo2-bar2") == 0);
+}
+
+ATF_TC(unset);
+ATF_TC_HEAD(unset, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_env_unset function");
+}
+ATF_TC_BODY(unset, tc)
+{
+ ATF_REQUIRE(atf_env_has("PATH"));
+ RE(atf_env_unset("PATH"));
+ ATF_REQUIRE(!atf_env_has("PATH"));
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, has);
+ ATF_TP_ADD_TC(tp, get);
+ ATF_TP_ADD_TC(tp, get_with_default);
+ ATF_TP_ADD_TC(tp, set);
+ ATF_TP_ADD_TC(tp, unset);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/fs.c b/unit/atf-src/atf-c/detail/fs.c
new file mode 100644
index 0000000..5ff7648
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/fs.c
@@ -0,0 +1,884 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/fs.h"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atf-c/defs.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/detail/text.h"
+#include "atf-c/detail/user.h"
+#include "atf-c/error.h"
+
+/* ---------------------------------------------------------------------
+ * Prototypes for auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static bool check_umask(const mode_t, const mode_t);
+static atf_error_t copy_contents(const atf_fs_path_t *, char **);
+static mode_t current_umask(void);
+static atf_error_t do_mkdtemp(char *);
+static atf_error_t normalize(atf_dynstr_t *, char *);
+static atf_error_t normalize_ap(atf_dynstr_t *, const char *, va_list);
+static void replace_contents(atf_fs_path_t *, const char *);
+static const char *stat_type_to_string(const int);
+
+/* ---------------------------------------------------------------------
+ * The "invalid_umask" error type.
+ * --------------------------------------------------------------------- */
+
+struct invalid_umask_error_data {
+ /* One of atf_fs_stat_*_type. */
+ int m_type;
+
+ /* The original path causing the error. */
+ /* XXX: Ideally this would be an atf_fs_path_t, but if we create it
+ * from the error constructor, we cannot delete the path later on.
+ * Can't remember why atf_error_new does not take a hook for
+ * deletion. */
+ char m_path[1024];
+
+ /* The umask that caused the error. */
+ mode_t m_umask;
+};
+typedef struct invalid_umask_error_data invalid_umask_error_data_t;
+
+static
+void
+invalid_umask_format(const atf_error_t err, char *buf, size_t buflen)
+{
+ const invalid_umask_error_data_t *data;
+
+ PRE(atf_error_is(err, "invalid_umask"));
+
+ data = atf_error_data(err);
+ snprintf(buf, buflen, "Could not create the temporary %s %s because "
+ "it will not have enough access rights due to the current "
+ "umask %05o", stat_type_to_string(data->m_type),
+ data->m_path, (unsigned int)data->m_umask);
+}
+
+static
+atf_error_t
+invalid_umask_error(const atf_fs_path_t *path, const int type,
+ const mode_t failing_mask)
+{
+ atf_error_t err;
+ invalid_umask_error_data_t data;
+
+ data.m_type = type;
+
+ strncpy(data.m_path, atf_fs_path_cstring(path), sizeof(data.m_path));
+ data.m_path[sizeof(data.m_path) - 1] = '\0';
+
+ data.m_umask = failing_mask;
+
+ err = atf_error_new("invalid_umask", &data, sizeof(data),
+ invalid_umask_format);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * The "unknown_file_type" error type.
+ * --------------------------------------------------------------------- */
+
+struct unknown_type_error_data {
+ const char *m_path;
+ int m_type;
+};
+typedef struct unknown_type_error_data unknown_type_error_data_t;
+
+static
+void
+unknown_type_format(const atf_error_t err, char *buf, size_t buflen)
+{
+ const unknown_type_error_data_t *data;
+
+ PRE(atf_error_is(err, "unknown_type"));
+
+ data = atf_error_data(err);
+ snprintf(buf, buflen, "Unknown file type %d of %s", data->m_type,
+ data->m_path);
+}
+
+static
+atf_error_t
+unknown_type_error(const char *path, int type)
+{
+ atf_error_t err;
+ unknown_type_error_data_t data;
+
+ data.m_path = path;
+ data.m_type = type;
+
+ err = atf_error_new("unknown_type", &data, sizeof(data),
+ unknown_type_format);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+bool
+check_umask(const mode_t exp_mode, const mode_t min_mode)
+{
+ const mode_t actual_mode = (~current_umask() & exp_mode);
+ return (actual_mode & min_mode) == min_mode;
+}
+
+static
+atf_error_t
+copy_contents(const atf_fs_path_t *p, char **buf)
+{
+ atf_error_t err;
+ char *str;
+
+ str = (char *)malloc(atf_dynstr_length(&p->m_data) + 1);
+ if (str == NULL)
+ err = atf_no_memory_error();
+ else {
+ strcpy(str, atf_dynstr_cstring(&p->m_data));
+ *buf = str;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+static
+mode_t
+current_umask(void)
+{
+ const mode_t current = umask(0);
+ (void)umask(current);
+ return current;
+}
+
+static
+atf_error_t
+do_mkdtemp(char *tmpl)
+{
+ atf_error_t err;
+
+ PRE(strstr(tmpl, "XXXXXX") != NULL);
+
+ if (mkdtemp(tmpl) == NULL)
+ err = atf_libc_error(errno, "Cannot create temporary directory "
+ "with template '%s'", tmpl);
+ else
+ err = atf_no_error();
+
+ return err;
+}
+
+static
+atf_error_t
+do_mkstemp(char *tmpl, int *fdout)
+{
+ atf_error_t err;
+
+ PRE(strstr(tmpl, "XXXXXX") != NULL);
+
+ *fdout = mkstemp(tmpl);
+ if (*fdout == -1)
+ err = atf_libc_error(errno, "Cannot create temporary file "
+ "with template '%s'", tmpl);
+
+ else
+ err = atf_no_error();
+
+ return err;
+}
+
+static
+atf_error_t
+normalize(atf_dynstr_t *d, char *p)
+{
+ const char *ptr;
+ char *last;
+ atf_error_t err;
+ bool first;
+
+ PRE(strlen(p) > 0);
+ PRE(atf_dynstr_length(d) == 0);
+
+ if (p[0] == '/')
+ err = atf_dynstr_append_fmt(d, "/");
+ else
+ err = atf_no_error();
+
+ first = true;
+ last = NULL; /* Silence GCC warning. */
+ ptr = strtok_r(p, "/", &last);
+ while (!atf_is_error(err) && ptr != NULL) {
+ if (strlen(ptr) > 0) {
+ err = atf_dynstr_append_fmt(d, "%s%s", first ? "" : "/", ptr);
+ first = false;
+ }
+
+ ptr = strtok_r(NULL, "/", &last);
+ }
+
+ return err;
+}
+
+static
+atf_error_t
+normalize_ap(atf_dynstr_t *d, const char *p, va_list ap)
+{
+ char *str;
+ atf_error_t err;
+ va_list ap2;
+
+ err = atf_dynstr_init(d);
+ if (atf_is_error(err))
+ goto out;
+
+ va_copy(ap2, ap);
+ err = atf_text_format_ap(&str, p, ap2);
+ va_end(ap2);
+ if (atf_is_error(err))
+ atf_dynstr_fini(d);
+ else {
+ err = normalize(d, str);
+ free(str);
+ }
+
+out:
+ return err;
+}
+
+static
+void
+replace_contents(atf_fs_path_t *p, const char *buf)
+{
+ atf_error_t err;
+
+ PRE(atf_dynstr_length(&p->m_data) == strlen(buf));
+
+ atf_dynstr_clear(&p->m_data);
+ err = atf_dynstr_append_fmt(&p->m_data, "%s", buf);
+
+ INV(!atf_is_error(err));
+}
+
+static
+const char *
+stat_type_to_string(const int type)
+{
+ const char *str;
+
+ if (type == atf_fs_stat_blk_type)
+ str = "block device";
+ else if (type == atf_fs_stat_chr_type)
+ str = "character device";
+ else if (type == atf_fs_stat_dir_type)
+ str = "directory";
+ else if (type == atf_fs_stat_fifo_type)
+ str = "named pipe";
+ else if (type == atf_fs_stat_lnk_type)
+ str = "symbolic link";
+ else if (type == atf_fs_stat_reg_type)
+ str = "regular file";
+ else if (type == atf_fs_stat_sock_type)
+ str = "socket";
+ else if (type == atf_fs_stat_wht_type)
+ str = "whiteout";
+ else {
+ UNREACHABLE;
+ str = NULL;
+ }
+
+ return str;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_fs_path" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors/destructors.
+ */
+
+atf_error_t
+atf_fs_path_init_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
+{
+ atf_error_t err;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = normalize_ap(&p->m_data, fmt, ap2);
+ va_end(ap2);
+
+ return err;
+}
+
+atf_error_t
+atf_fs_path_init_fmt(atf_fs_path_t *p, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_fs_path_init_ap(p, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+atf_error_t
+atf_fs_path_copy(atf_fs_path_t *dest, const atf_fs_path_t *src)
+{
+ return atf_dynstr_copy(&dest->m_data, &src->m_data);
+}
+
+void
+atf_fs_path_fini(atf_fs_path_t *p)
+{
+ atf_dynstr_fini(&p->m_data);
+}
+
+/*
+ * Getters.
+ */
+
+atf_error_t
+atf_fs_path_branch_path(const atf_fs_path_t *p, atf_fs_path_t *bp)
+{
+ const size_t endpos = atf_dynstr_rfind_ch(&p->m_data, '/');
+ atf_error_t err;
+
+ if (endpos == atf_dynstr_npos)
+ err = atf_fs_path_init_fmt(bp, ".");
+ else if (endpos == 0)
+ err = atf_fs_path_init_fmt(bp, "/");
+ else
+ err = atf_dynstr_init_substr(&bp->m_data, &p->m_data, 0, endpos);
+
+#if defined(HAVE_CONST_DIRNAME)
+ INV(atf_equal_dynstr_cstring(&bp->m_data,
+ dirname(atf_dynstr_cstring(&p->m_data))));
+#endif /* defined(HAVE_CONST_DIRNAME) */
+
+ return err;
+}
+
+const char *
+atf_fs_path_cstring(const atf_fs_path_t *p)
+{
+ return atf_dynstr_cstring(&p->m_data);
+}
+
+atf_error_t
+atf_fs_path_leaf_name(const atf_fs_path_t *p, atf_dynstr_t *ln)
+{
+ size_t begpos = atf_dynstr_rfind_ch(&p->m_data, '/');
+ atf_error_t err;
+
+ if (begpos == atf_dynstr_npos)
+ begpos = 0;
+ else
+ begpos++;
+
+ err = atf_dynstr_init_substr(ln, &p->m_data, begpos, atf_dynstr_npos);
+
+#if defined(HAVE_CONST_BASENAME)
+ INV(atf_equal_dynstr_cstring(ln,
+ basename(atf_dynstr_cstring(&p->m_data))));
+#endif /* defined(HAVE_CONST_BASENAME) */
+
+ return err;
+}
+
+bool
+atf_fs_path_is_absolute(const atf_fs_path_t *p)
+{
+ return atf_dynstr_cstring(&p->m_data)[0] == '/';
+}
+
+bool
+atf_fs_path_is_root(const atf_fs_path_t *p)
+{
+ return atf_equal_dynstr_cstring(&p->m_data, "/");
+}
+
+/*
+ * Modifiers.
+ */
+
+atf_error_t
+atf_fs_path_append_ap(atf_fs_path_t *p, const char *fmt, va_list ap)
+{
+ atf_dynstr_t aux;
+ atf_error_t err;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = normalize_ap(&aux, fmt, ap2);
+ va_end(ap2);
+ if (!atf_is_error(err)) {
+ const char *auxstr = atf_dynstr_cstring(&aux);
+ const bool needslash = auxstr[0] != '/';
+
+ err = atf_dynstr_append_fmt(&p->m_data, "%s%s",
+ needslash ? "/" : "", auxstr);
+
+ atf_dynstr_fini(&aux);
+ }
+
+ return err;
+}
+
+atf_error_t
+atf_fs_path_append_fmt(atf_fs_path_t *p, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_fs_path_append_ap(p, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+atf_error_t
+atf_fs_path_append_path(atf_fs_path_t *p, const atf_fs_path_t *p2)
+{
+ return atf_fs_path_append_fmt(p, "%s", atf_dynstr_cstring(&p2->m_data));
+}
+
+atf_error_t
+atf_fs_path_to_absolute(const atf_fs_path_t *p, atf_fs_path_t *pa)
+{
+ atf_error_t err;
+
+ PRE(!atf_fs_path_is_absolute(p));
+
+ err = atf_fs_getcwd(pa);
+ if (atf_is_error(err))
+ goto out;
+
+ err = atf_fs_path_append_path(pa, p);
+ if (atf_is_error(err))
+ atf_fs_path_fini(pa);
+
+out:
+ return err;
+}
+
+/*
+ * Operators.
+ */
+
+bool atf_equal_fs_path_fs_path(const atf_fs_path_t *p1,
+ const atf_fs_path_t *p2)
+{
+ return atf_equal_dynstr_dynstr(&p1->m_data, &p2->m_data);
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_fs_path" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constants.
+ */
+
+const int atf_fs_stat_blk_type = 1;
+const int atf_fs_stat_chr_type = 2;
+const int atf_fs_stat_dir_type = 3;
+const int atf_fs_stat_fifo_type = 4;
+const int atf_fs_stat_lnk_type = 5;
+const int atf_fs_stat_reg_type = 6;
+const int atf_fs_stat_sock_type = 7;
+const int atf_fs_stat_wht_type = 8;
+
+/*
+ * Constructors/destructors.
+ */
+
+atf_error_t
+atf_fs_stat_init(atf_fs_stat_t *st, const atf_fs_path_t *p)
+{
+ atf_error_t err;
+ const char *pstr = atf_fs_path_cstring(p);
+
+ if (lstat(pstr, &st->m_sb) == -1) {
+ err = atf_libc_error(errno, "Cannot get information of %s; "
+ "lstat(2) failed", pstr);
+ } else {
+ int type = st->m_sb.st_mode & S_IFMT;
+ err = atf_no_error();
+ switch (type) {
+ case S_IFBLK: st->m_type = atf_fs_stat_blk_type; break;
+ case S_IFCHR: st->m_type = atf_fs_stat_chr_type; break;
+ case S_IFDIR: st->m_type = atf_fs_stat_dir_type; break;
+ case S_IFIFO: st->m_type = atf_fs_stat_fifo_type; break;
+ case S_IFLNK: st->m_type = atf_fs_stat_lnk_type; break;
+ case S_IFREG: st->m_type = atf_fs_stat_reg_type; break;
+ case S_IFSOCK: st->m_type = atf_fs_stat_sock_type; break;
+#if defined(S_IFWHT)
+ case S_IFWHT: st->m_type = atf_fs_stat_wht_type; break;
+#endif
+ default:
+ err = unknown_type_error(pstr, type);
+ }
+ }
+
+ return err;
+}
+
+void
+atf_fs_stat_copy(atf_fs_stat_t *dest, const atf_fs_stat_t *src)
+{
+ dest->m_type = src->m_type;
+ dest->m_sb = src->m_sb;
+}
+
+void
+atf_fs_stat_fini(atf_fs_stat_t *st ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+}
+
+/*
+ * Getters.
+ */
+
+dev_t
+atf_fs_stat_get_device(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_dev;
+}
+
+ino_t
+atf_fs_stat_get_inode(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_ino;
+}
+
+mode_t
+atf_fs_stat_get_mode(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & ~S_IFMT;
+}
+
+off_t
+atf_fs_stat_get_size(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_size;
+}
+
+int
+atf_fs_stat_get_type(const atf_fs_stat_t *st)
+{
+ return st->m_type;
+}
+
+bool
+atf_fs_stat_is_owner_readable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IRUSR;
+}
+
+bool
+atf_fs_stat_is_owner_writable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IWUSR;
+}
+
+bool
+atf_fs_stat_is_owner_executable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IXUSR;
+}
+
+bool
+atf_fs_stat_is_group_readable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IRGRP;
+}
+
+bool
+atf_fs_stat_is_group_writable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IWGRP;
+}
+
+bool
+atf_fs_stat_is_group_executable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IXGRP;
+}
+
+bool
+atf_fs_stat_is_other_readable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IROTH;
+}
+
+bool
+atf_fs_stat_is_other_writable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IWOTH;
+}
+
+bool
+atf_fs_stat_is_other_executable(const atf_fs_stat_t *st)
+{
+ return st->m_sb.st_mode & S_IXOTH;
+}
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+const int atf_fs_access_f = 1 << 0;
+const int atf_fs_access_r = 1 << 1;
+const int atf_fs_access_w = 1 << 2;
+const int atf_fs_access_x = 1 << 3;
+
+/*
+ * An implementation of access(2) but using the effective user value
+ * instead of the real one. Also avoids false positives for root when
+ * asking for execute permissions, which appear in SunOS.
+ */
+atf_error_t
+atf_fs_eaccess(const atf_fs_path_t *p, int mode)
+{
+ atf_error_t err;
+ struct stat st;
+ bool ok;
+
+ PRE(mode & atf_fs_access_f || mode & atf_fs_access_r ||
+ mode & atf_fs_access_w || mode & atf_fs_access_x);
+
+ if (lstat(atf_fs_path_cstring(p), &st) == -1) {
+ err = atf_libc_error(errno, "Cannot get information from file %s",
+ atf_fs_path_cstring(p));
+ goto out;
+ }
+
+ err = atf_no_error();
+
+ /* Early return if we are only checking for existence and the file
+ * exists (stat call returned). */
+ if (mode & atf_fs_access_f)
+ goto out;
+
+ ok = false;
+ if (atf_user_is_root()) {
+ if (!ok && !(mode & atf_fs_access_x)) {
+ /* Allow root to read/write any file. */
+ ok = true;
+ }
+
+ if (!ok && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) {
+ /* Allow root to execute the file if any of its execution bits
+ * are set. */
+ ok = true;
+ }
+ } else {
+ if (!ok && (atf_user_euid() == st.st_uid)) {
+ ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRUSR)) ||
+ ((mode & atf_fs_access_w) && (st.st_mode & S_IWUSR)) ||
+ ((mode & atf_fs_access_x) && (st.st_mode & S_IXUSR));
+ }
+ if (!ok && atf_user_is_member_of_group(st.st_gid)) {
+ ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRGRP)) ||
+ ((mode & atf_fs_access_w) && (st.st_mode & S_IWGRP)) ||
+ ((mode & atf_fs_access_x) && (st.st_mode & S_IXGRP));
+ }
+ if (!ok && ((atf_user_euid() != st.st_uid) &&
+ !atf_user_is_member_of_group(st.st_gid))) {
+ ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IROTH)) ||
+ ((mode & atf_fs_access_w) && (st.st_mode & S_IWOTH)) ||
+ ((mode & atf_fs_access_x) && (st.st_mode & S_IXOTH));
+ }
+ }
+
+ if (!ok)
+ err = atf_libc_error(EACCES, "Access check failed");
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_fs_exists(const atf_fs_path_t *p, bool *b)
+{
+ atf_error_t err;
+
+ err = atf_fs_eaccess(p, atf_fs_access_f);
+ if (atf_is_error(err)) {
+ if (atf_error_is(err, "libc") && atf_libc_error_code(err) == ENOENT) {
+ atf_error_free(err);
+ err = atf_no_error();
+ *b = false;
+ }
+ } else
+ *b = true;
+
+ return err;
+}
+
+atf_error_t
+atf_fs_getcwd(atf_fs_path_t *p)
+{
+ atf_error_t err;
+ char *cwd;
+
+#if defined(HAVE_GETCWD_DYN)
+ cwd = getcwd(NULL, 0);
+#else
+ cwd = getcwd(NULL, MAXPATHLEN);
+#endif
+ if (cwd == NULL) {
+ err = atf_libc_error(errno, "Cannot determine current directory");
+ goto out;
+ }
+
+ err = atf_fs_path_init_fmt(p, "%s", cwd);
+ free(cwd);
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_fs_mkdtemp(atf_fs_path_t *p)
+{
+ atf_error_t err;
+ char *buf;
+
+ if (!check_umask(S_IRWXU, S_IRWXU)) {
+ err = invalid_umask_error(p, atf_fs_stat_dir_type, current_umask());
+ goto out;
+ }
+
+ err = copy_contents(p, &buf);
+ if (atf_is_error(err))
+ goto out;
+
+ err = do_mkdtemp(buf);
+ if (atf_is_error(err))
+ goto out_buf;
+
+ replace_contents(p, buf);
+
+ INV(!atf_is_error(err));
+out_buf:
+ free(buf);
+out:
+ return err;
+}
+
+atf_error_t
+atf_fs_mkstemp(atf_fs_path_t *p, int *fdout)
+{
+ atf_error_t err;
+ char *buf;
+ int fd;
+
+ if (!check_umask(S_IRWXU, S_IRWXU)) {
+ err = invalid_umask_error(p, atf_fs_stat_reg_type, current_umask());
+ goto out;
+ }
+
+ err = copy_contents(p, &buf);
+ if (atf_is_error(err))
+ goto out;
+
+ err = do_mkstemp(buf, &fd);
+ if (atf_is_error(err))
+ goto out_buf;
+
+ replace_contents(p, buf);
+ *fdout = fd;
+
+ INV(!atf_is_error(err));
+out_buf:
+ free(buf);
+out:
+ return err;
+}
+
+atf_error_t
+atf_fs_rmdir(const atf_fs_path_t *p)
+{
+ atf_error_t err;
+
+ if (rmdir(atf_fs_path_cstring(p))) {
+ if (errno == EEXIST) {
+ /* Some operating systems (e.g. OpenSolaris 200906) return
+ * EEXIST instead of ENOTEMPTY for non-empty directories.
+ * Homogenize the return value so that callers don't need
+ * to bother about differences in operating systems. */
+ errno = ENOTEMPTY;
+ }
+ err = atf_libc_error(errno, "Cannot remove directory");
+ } else
+ err = atf_no_error();
+
+ return err;
+}
+
+atf_error_t
+atf_fs_unlink(const atf_fs_path_t *p)
+{
+ atf_error_t err;
+ const char *path;
+
+ path = atf_fs_path_cstring(p);
+
+ if (unlink(path) != 0)
+ err = atf_libc_error(errno, "Cannot unlink file: '%s'", path);
+ else
+ err = atf_no_error();
+
+ return err;
+}
diff --git a/unit/atf-src/atf-c/detail/fs.h b/unit/atf-src/atf-c/detail/fs.h
new file mode 100644
index 0000000..1c6c0eb
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/fs.h
@@ -0,0 +1,128 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_DETAIL_FS_H)
+#define ATF_C_DETAIL_FS_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include <atf-c/detail/dynstr.h>
+#include <atf-c/error_fwd.h>
+
+/* ---------------------------------------------------------------------
+ * The "atf_fs_path" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_fs_path {
+ atf_dynstr_t m_data;
+};
+typedef struct atf_fs_path atf_fs_path_t;
+
+/* Constructors/destructors. */
+atf_error_t atf_fs_path_init_ap(atf_fs_path_t *, const char *, va_list);
+atf_error_t atf_fs_path_init_fmt(atf_fs_path_t *, const char *, ...);
+atf_error_t atf_fs_path_copy(atf_fs_path_t *, const atf_fs_path_t *);
+void atf_fs_path_fini(atf_fs_path_t *);
+
+/* Getters. */
+atf_error_t atf_fs_path_branch_path(const atf_fs_path_t *, atf_fs_path_t *);
+const char *atf_fs_path_cstring(const atf_fs_path_t *);
+atf_error_t atf_fs_path_leaf_name(const atf_fs_path_t *, atf_dynstr_t *);
+bool atf_fs_path_is_absolute(const atf_fs_path_t *);
+bool atf_fs_path_is_root(const atf_fs_path_t *);
+
+/* Modifiers. */
+atf_error_t atf_fs_path_append_ap(atf_fs_path_t *, const char *, va_list);
+atf_error_t atf_fs_path_append_fmt(atf_fs_path_t *, const char *, ...);
+atf_error_t atf_fs_path_append_path(atf_fs_path_t *, const atf_fs_path_t *);
+atf_error_t atf_fs_path_to_absolute(const atf_fs_path_t *, atf_fs_path_t *);
+
+/* Operators. */
+bool atf_equal_fs_path_fs_path(const atf_fs_path_t *,
+ const atf_fs_path_t *);
+
+/* ---------------------------------------------------------------------
+ * The "atf_fs_stat" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_fs_stat {
+ int m_type;
+ struct stat m_sb;
+};
+typedef struct atf_fs_stat atf_fs_stat_t;
+
+/* Constants. */
+extern const int atf_fs_stat_blk_type;
+extern const int atf_fs_stat_chr_type;
+extern const int atf_fs_stat_dir_type;
+extern const int atf_fs_stat_fifo_type;
+extern const int atf_fs_stat_lnk_type;
+extern const int atf_fs_stat_reg_type;
+extern const int atf_fs_stat_sock_type;
+extern const int atf_fs_stat_wht_type;
+
+/* Constructors/destructors. */
+atf_error_t atf_fs_stat_init(atf_fs_stat_t *, const atf_fs_path_t *);
+void atf_fs_stat_copy(atf_fs_stat_t *, const atf_fs_stat_t *);
+void atf_fs_stat_fini(atf_fs_stat_t *);
+
+/* Getters. */
+dev_t atf_fs_stat_get_device(const atf_fs_stat_t *);
+ino_t atf_fs_stat_get_inode(const atf_fs_stat_t *);
+mode_t atf_fs_stat_get_mode(const atf_fs_stat_t *);
+off_t atf_fs_stat_get_size(const atf_fs_stat_t *);
+int atf_fs_stat_get_type(const atf_fs_stat_t *);
+bool atf_fs_stat_is_owner_readable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_owner_writable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_owner_executable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_group_readable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_group_writable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_group_executable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_other_readable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_other_writable(const atf_fs_stat_t *);
+bool atf_fs_stat_is_other_executable(const atf_fs_stat_t *);
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+extern const int atf_fs_access_f;
+extern const int atf_fs_access_r;
+extern const int atf_fs_access_w;
+extern const int atf_fs_access_x;
+
+atf_error_t atf_fs_eaccess(const atf_fs_path_t *, int);
+atf_error_t atf_fs_exists(const atf_fs_path_t *, bool *);
+atf_error_t atf_fs_getcwd(atf_fs_path_t *);
+atf_error_t atf_fs_mkdtemp(atf_fs_path_t *);
+atf_error_t atf_fs_mkstemp(atf_fs_path_t *, int *);
+atf_error_t atf_fs_rmdir(const atf_fs_path_t *);
+atf_error_t atf_fs_unlink(const atf_fs_path_t *);
+
+#endif /* !defined(ATF_C_DETAIL_FS_H) */
diff --git a/unit/atf-src/atf-c/detail/fs_test.c b/unit/atf-src/atf-c/detail/fs_test.c
new file mode 100644
index 0000000..3dbc4d3
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/fs_test.c
@@ -0,0 +1,1079 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/fs.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+#include "atf-c/detail/user.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+void
+create_dir(const char *p, int mode)
+{
+ int ret;
+
+ ret = mkdir(p, mode);
+ if (ret == -1)
+ atf_tc_fail("Could not create helper directory %s", p);
+}
+
+static
+void
+create_file(const char *p, int mode)
+{
+ int fd;
+
+ fd = open(p, O_CREAT | O_WRONLY | O_TRUNC, mode);
+ if (fd == -1)
+ atf_tc_fail("Could not create helper file %s", p);
+ close(fd);
+}
+
+static
+bool
+exists(const atf_fs_path_t *p)
+{
+ return access(atf_fs_path_cstring(p), F_OK) == 0;
+}
+
+static
+atf_error_t
+mkstemp_discard_fd(atf_fs_path_t *p)
+{
+ int fd;
+ atf_error_t err = atf_fs_mkstemp(p, &fd);
+ if (!atf_is_error(err))
+ close(fd);
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the "atf_fs_path" type.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(path_normalize);
+ATF_TC_HEAD(path_normalize, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the path's normalization");
+}
+ATF_TC_BODY(path_normalize, tc)
+{
+ struct test {
+ const char *in;
+ const char *out;
+ } tests[] = {
+ { ".", ".", },
+ { "..", "..", },
+
+ { "/", "/", },
+ { "//", "/", }, /* NO_CHECK_STYLE */
+ { "///", "/", }, /* NO_CHECK_STYLE */
+
+ { "foo", "foo", },
+ { "foo/", "foo", },
+ { "foo/bar", "foo/bar", },
+ { "foo/bar/", "foo/bar", },
+
+ { "/foo", "/foo", },
+ { "/foo/bar", "/foo/bar", },
+ { "/foo/bar/", "/foo/bar", },
+
+ { "///foo", "/foo", }, /* NO_CHECK_STYLE */
+ { "///foo///bar", "/foo/bar", }, /* NO_CHECK_STYLE */
+ { "///foo///bar///", "/foo/bar", }, /* NO_CHECK_STYLE */
+
+ { NULL, NULL }
+ };
+ struct test *t;
+
+ for (t = &tests[0]; t->in != NULL; t++) {
+ atf_fs_path_t p;
+
+ printf("Input : >%s<\n", t->in);
+ printf("Expected output: >%s<\n", t->out);
+
+ RE(atf_fs_path_init_fmt(&p, "%s", t->in));
+ printf("Output : >%s<\n", atf_fs_path_cstring(&p));
+ ATF_REQUIRE(strcmp(atf_fs_path_cstring(&p), t->out) == 0);
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_copy);
+ATF_TC_HEAD(path_copy, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_copy constructor");
+}
+ATF_TC_BODY(path_copy, tc)
+{
+ atf_fs_path_t str, str2;
+
+ RE(atf_fs_path_init_fmt(&str, "foo"));
+ RE(atf_fs_path_copy(&str2, &str));
+
+ ATF_REQUIRE(atf_equal_fs_path_fs_path(&str, &str2));
+
+ RE(atf_fs_path_append_fmt(&str2, "bar"));
+
+ ATF_REQUIRE(!atf_equal_fs_path_fs_path(&str, &str2));
+
+ atf_fs_path_fini(&str2);
+ atf_fs_path_fini(&str);
+}
+
+ATF_TC(path_is_absolute);
+ATF_TC_HEAD(path_is_absolute, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the path::is_absolute function");
+}
+ATF_TC_BODY(path_is_absolute, tc)
+{
+ struct test {
+ const char *in;
+ bool abs;
+ } tests[] = {
+ { "/", true },
+ { "////", true }, /* NO_CHECK_STYLE */
+ { "////a", true }, /* NO_CHECK_STYLE */
+ { "//a//", true }, /* NO_CHECK_STYLE */
+ { "a////", false }, /* NO_CHECK_STYLE */
+ { "../foo", false },
+ { NULL, false },
+ };
+ struct test *t;
+
+ for (t = &tests[0]; t->in != NULL; t++) {
+ atf_fs_path_t p;
+
+ printf("Input : %s\n", t->in);
+ printf("Expected result: %s\n", t->abs ? "true" : "false");
+
+ RE(atf_fs_path_init_fmt(&p, "%s", t->in));
+ printf("Result : %s\n",
+ atf_fs_path_is_absolute(&p) ? "true" : "false");
+ if (t->abs)
+ ATF_REQUIRE(atf_fs_path_is_absolute(&p));
+ else
+ ATF_REQUIRE(!atf_fs_path_is_absolute(&p));
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_is_root);
+ATF_TC_HEAD(path_is_root, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the path::is_root function");
+}
+ATF_TC_BODY(path_is_root, tc)
+{
+ struct test {
+ const char *in;
+ bool root;
+ } tests[] = {
+ { "/", true },
+ { "////", true }, /* NO_CHECK_STYLE */
+ { "////a", false }, /* NO_CHECK_STYLE */
+ { "//a//", false }, /* NO_CHECK_STYLE */
+ { "a////", false }, /* NO_CHECK_STYLE */
+ { "../foo", false },
+ { NULL, false },
+ };
+ struct test *t;
+
+ for (t = &tests[0]; t->in != NULL; t++) {
+ atf_fs_path_t p;
+
+ printf("Input : %s\n", t->in);
+ printf("Expected result: %s\n", t->root ? "true" : "false");
+
+ RE(atf_fs_path_init_fmt(&p, "%s", t->in));
+ printf("Result : %s\n",
+ atf_fs_path_is_root(&p) ? "true" : "false");
+ if (t->root)
+ ATF_REQUIRE(atf_fs_path_is_root(&p));
+ else
+ ATF_REQUIRE(!atf_fs_path_is_root(&p));
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_branch_path);
+ATF_TC_HEAD(path_branch_path, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_branch_path "
+ "function");
+}
+ATF_TC_BODY(path_branch_path, tc)
+{
+ struct test {
+ const char *in;
+ const char *branch;
+ } tests[] = {
+ { ".", "." },
+ { "foo", "." },
+ { "foo/bar", "foo" },
+ { "/foo", "/" },
+ { "/foo/bar", "/foo" },
+ { NULL, NULL },
+ };
+ struct test *t;
+
+ for (t = &tests[0]; t->in != NULL; t++) {
+ atf_fs_path_t p, bp;
+
+ printf("Input : %s\n", t->in);
+ printf("Expected output: %s\n", t->branch);
+
+ RE(atf_fs_path_init_fmt(&p, "%s", t->in));
+ RE(atf_fs_path_branch_path(&p, &bp));
+ printf("Output : %s\n", atf_fs_path_cstring(&bp));
+ ATF_REQUIRE(strcmp(atf_fs_path_cstring(&bp), t->branch) == 0);
+ atf_fs_path_fini(&bp);
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_leaf_name);
+ATF_TC_HEAD(path_leaf_name, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_leaf_name "
+ "function");
+}
+ATF_TC_BODY(path_leaf_name, tc)
+{
+ struct test {
+ const char *in;
+ const char *leaf;
+ } tests[] = {
+ { ".", "." },
+ { "foo", "foo" },
+ { "foo/bar", "bar" },
+ { "/foo", "foo" },
+ { "/foo/bar", "bar" },
+ { NULL, NULL },
+ };
+ struct test *t;
+
+ for (t = &tests[0]; t->in != NULL; t++) {
+ atf_fs_path_t p;
+ atf_dynstr_t ln;
+
+ printf("Input : %s\n", t->in);
+ printf("Expected output: %s\n", t->leaf);
+
+ RE(atf_fs_path_init_fmt(&p, "%s", t->in));
+ RE(atf_fs_path_leaf_name(&p, &ln));
+ printf("Output : %s\n", atf_dynstr_cstring(&ln));
+ ATF_REQUIRE(atf_equal_dynstr_cstring(&ln, t->leaf));
+ atf_dynstr_fini(&ln);
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_append);
+ATF_TC_HEAD(path_append, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the concatenation of multiple "
+ "paths");
+}
+ATF_TC_BODY(path_append, tc)
+{
+ struct test {
+ const char *in;
+ const char *ap;
+ const char *out;
+ } tests[] = {
+ { "foo", "bar", "foo/bar" },
+ { "foo/", "/bar", "foo/bar" },
+ { "foo/", "/bar/baz", "foo/bar/baz" },
+ { "foo/", "///bar///baz", "foo/bar/baz" }, /* NO_CHECK_STYLE */
+
+ { NULL, NULL, NULL }
+ };
+ struct test *t;
+
+ for (t = &tests[0]; t->in != NULL; t++) {
+ atf_fs_path_t p;
+
+ printf("Input : >%s<\n", t->in);
+ printf("Append : >%s<\n", t->ap);
+ printf("Expected output: >%s<\n", t->out);
+
+ RE(atf_fs_path_init_fmt(&p, "%s", t->in));
+
+ RE(atf_fs_path_append_fmt(&p, "%s", t->ap));
+
+ printf("Output : >%s<\n", atf_fs_path_cstring(&p));
+ ATF_REQUIRE(strcmp(atf_fs_path_cstring(&p), t->out) == 0);
+
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_to_absolute);
+ATF_TC_HEAD(path_to_absolute, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_to_absolute "
+ "function");
+}
+ATF_TC_BODY(path_to_absolute, tc)
+{
+ const char *names[] = { ".", "dir", NULL };
+ const char **n;
+
+ ATF_REQUIRE(mkdir("dir", 0755) != -1);
+
+ for (n = names; *n != NULL; n++) {
+ atf_fs_path_t p, p2;
+ atf_fs_stat_t st1, st2;
+
+ RE(atf_fs_path_init_fmt(&p, "%s", *n));
+ RE(atf_fs_stat_init(&st1, &p));
+ printf("Relative path: %s\n", atf_fs_path_cstring(&p));
+
+ RE(atf_fs_path_to_absolute(&p, &p2));
+ printf("Absolute path: %s\n", atf_fs_path_cstring(&p2));
+
+ ATF_REQUIRE(atf_fs_path_is_absolute(&p2));
+ RE(atf_fs_stat_init(&st2, &p2));
+
+ ATF_REQUIRE_EQ(atf_fs_stat_get_device(&st1),
+ atf_fs_stat_get_device(&st2));
+ ATF_REQUIRE_EQ(atf_fs_stat_get_inode(&st1),
+ atf_fs_stat_get_inode(&st2));
+
+ atf_fs_stat_fini(&st2);
+ atf_fs_stat_fini(&st1);
+ atf_fs_path_fini(&p2);
+ atf_fs_path_fini(&p);
+
+ printf("\n");
+ }
+}
+
+ATF_TC(path_equal);
+ATF_TC_HEAD(path_equal, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the equality operators for paths");
+}
+ATF_TC_BODY(path_equal, tc)
+{
+ atf_fs_path_t p1, p2;
+
+ RE(atf_fs_path_init_fmt(&p1, "foo"));
+
+ RE(atf_fs_path_init_fmt(&p2, "foo"));
+ ATF_REQUIRE(atf_equal_fs_path_fs_path(&p1, &p2));
+ atf_fs_path_fini(&p2);
+
+ RE(atf_fs_path_init_fmt(&p2, "bar"));
+ ATF_REQUIRE(!atf_equal_fs_path_fs_path(&p1, &p2));
+ atf_fs_path_fini(&p2);
+
+ atf_fs_path_fini(&p1);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the "atf_fs_stat" type.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(stat_mode);
+ATF_TC_HEAD(stat_mode, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_stat_get_mode function "
+ "and, indirectly, the constructor");
+}
+ATF_TC_BODY(stat_mode, tc)
+{
+ atf_fs_path_t p;
+ atf_fs_stat_t st;
+
+ create_file("f1", 0400);
+ create_file("f2", 0644);
+
+ RE(atf_fs_path_init_fmt(&p, "f1"));
+ RE(atf_fs_stat_init(&st, &p));
+ ATF_CHECK_EQ(0400, atf_fs_stat_get_mode(&st));
+ atf_fs_stat_fini(&st);
+ atf_fs_path_fini(&p);
+
+ RE(atf_fs_path_init_fmt(&p, "f2"));
+ RE(atf_fs_stat_init(&st, &p));
+ ATF_CHECK_EQ(0644, atf_fs_stat_get_mode(&st));
+ atf_fs_stat_fini(&st);
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(stat_type);
+ATF_TC_HEAD(stat_type, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_stat_get_type function "
+ "and, indirectly, the constructor");
+}
+ATF_TC_BODY(stat_type, tc)
+{
+ atf_fs_path_t p;
+ atf_fs_stat_t st;
+
+ create_dir("dir", 0755);
+ create_file("reg", 0644);
+
+ RE(atf_fs_path_init_fmt(&p, "dir"));
+ RE(atf_fs_stat_init(&st, &p));
+ ATF_REQUIRE_EQ(atf_fs_stat_get_type(&st), atf_fs_stat_dir_type);
+ atf_fs_stat_fini(&st);
+ atf_fs_path_fini(&p);
+
+ RE(atf_fs_path_init_fmt(&p, "reg"));
+ RE(atf_fs_stat_init(&st, &p));
+ ATF_REQUIRE_EQ(atf_fs_stat_get_type(&st), atf_fs_stat_reg_type);
+ atf_fs_stat_fini(&st);
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(stat_perms);
+ATF_TC_HEAD(stat_perms, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_stat_is_* functions");
+}
+ATF_TC_BODY(stat_perms, tc)
+{
+ atf_fs_path_t p;
+ atf_fs_stat_t st;
+
+ create_file("reg", 0);
+
+ RE(atf_fs_path_init_fmt(&p, "reg"));
+
+#define perms(ur, uw, ux, gr, gw, gx, othr, othw, othx) \
+ { \
+ RE(atf_fs_stat_init(&st, &p)); \
+ ATF_REQUIRE(atf_fs_stat_is_owner_readable(&st) == ur); \
+ ATF_REQUIRE(atf_fs_stat_is_owner_writable(&st) == uw); \
+ ATF_REQUIRE(atf_fs_stat_is_owner_executable(&st) == ux); \
+ ATF_REQUIRE(atf_fs_stat_is_group_readable(&st) == gr); \
+ ATF_REQUIRE(atf_fs_stat_is_group_writable(&st) == gw); \
+ ATF_REQUIRE(atf_fs_stat_is_group_executable(&st) == gx); \
+ ATF_REQUIRE(atf_fs_stat_is_other_readable(&st) == othr); \
+ ATF_REQUIRE(atf_fs_stat_is_other_writable(&st) == othw); \
+ ATF_REQUIRE(atf_fs_stat_is_other_executable(&st) == othx); \
+ atf_fs_stat_fini(&st); \
+ }
+
+ chmod("reg", 0000);
+ perms(false, false, false, false, false, false, false, false, false);
+
+ chmod("reg", 0001);
+ perms(false, false, false, false, false, false, false, false, true);
+
+ chmod("reg", 0010);
+ perms(false, false, false, false, false, true, false, false, false);
+
+ chmod("reg", 0100);
+ perms(false, false, true, false, false, false, false, false, false);
+
+ chmod("reg", 0002);
+ perms(false, false, false, false, false, false, false, true, false);
+
+ chmod("reg", 0020);
+ perms(false, false, false, false, true, false, false, false, false);
+
+ chmod("reg", 0200);
+ perms(false, true, false, false, false, false, false, false, false);
+
+ chmod("reg", 0004);
+ perms(false, false, false, false, false, false, true, false, false);
+
+ chmod("reg", 0040);
+ perms(false, false, false, true, false, false, false, false, false);
+
+ chmod("reg", 0400);
+ perms(true, false, false, false, false, false, false, false, false);
+
+ chmod("reg", 0644);
+ perms(true, true, false, true, false, false, true, false, false);
+
+ chmod("reg", 0755);
+ perms(true, true, true, true, false, true, true, false, true);
+
+ chmod("reg", 0777);
+ perms(true, true, true, true, true, true, true, true, true);
+
+#undef perms
+
+ atf_fs_path_fini(&p);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(exists);
+ATF_TC_HEAD(exists, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_exists function");
+}
+ATF_TC_BODY(exists, tc)
+{
+ atf_error_t err;
+ atf_fs_path_t pdir, pfile;
+ bool b;
+
+ RE(atf_fs_path_init_fmt(&pdir, "dir"));
+ RE(atf_fs_path_init_fmt(&pfile, "dir/file"));
+
+ create_dir(atf_fs_path_cstring(&pdir), 0755);
+ create_file(atf_fs_path_cstring(&pfile), 0644);
+
+ printf("Checking existence of a directory\n");
+ RE(atf_fs_exists(&pdir, &b));
+ ATF_REQUIRE(b);
+
+ printf("Checking existence of a file\n");
+ RE(atf_fs_exists(&pfile, &b));
+ ATF_REQUIRE(b);
+
+ /* XXX: This should probably be a separate test case to let the user
+ * be aware that some tests were skipped because privileges were not
+ * correct. */
+ if (!atf_user_is_root()) {
+ printf("Checking existence of a file inside a directory without "
+ "permissions\n");
+ ATF_REQUIRE(chmod(atf_fs_path_cstring(&pdir), 0000) != -1);
+ err = atf_fs_exists(&pfile, &b);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_REQUIRE(chmod(atf_fs_path_cstring(&pdir), 0755) != -1);
+ atf_error_free(err);
+ }
+
+ printf("Checking existence of a non-existent file\n");
+ ATF_REQUIRE(unlink(atf_fs_path_cstring(&pfile)) != -1);
+ RE(atf_fs_exists(&pfile, &b));
+ ATF_REQUIRE(!b);
+
+ atf_fs_path_fini(&pfile);
+ atf_fs_path_fini(&pdir);
+}
+
+ATF_TC(eaccess);
+ATF_TC_HEAD(eaccess, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_eaccess function");
+}
+ATF_TC_BODY(eaccess, tc)
+{
+ const int modes[] = { atf_fs_access_f, atf_fs_access_r, atf_fs_access_w,
+ atf_fs_access_x, 0 };
+ const int *m;
+ struct tests {
+ mode_t fmode;
+ int amode;
+ int uerror;
+ int rerror;
+ } tests[] = {
+ { 0000, atf_fs_access_r, EACCES, 0 },
+ { 0000, atf_fs_access_w, EACCES, 0 },
+ { 0000, atf_fs_access_x, EACCES, EACCES },
+
+ { 0001, atf_fs_access_r, EACCES, 0 },
+ { 0001, atf_fs_access_w, EACCES, 0 },
+ { 0001, atf_fs_access_x, EACCES, 0 },
+ { 0002, atf_fs_access_r, EACCES, 0 },
+ { 0002, atf_fs_access_w, EACCES, 0 },
+ { 0002, atf_fs_access_x, EACCES, EACCES },
+ { 0004, atf_fs_access_r, EACCES, 0 },
+ { 0004, atf_fs_access_w, EACCES, 0 },
+ { 0004, atf_fs_access_x, EACCES, EACCES },
+
+ { 0010, atf_fs_access_r, EACCES, 0 },
+ { 0010, atf_fs_access_w, EACCES, 0 },
+ { 0010, atf_fs_access_x, 0, 0 },
+ { 0020, atf_fs_access_r, EACCES, 0 },
+ { 0020, atf_fs_access_w, 0, 0 },
+ { 0020, atf_fs_access_x, EACCES, EACCES },
+ { 0040, atf_fs_access_r, 0, 0 },
+ { 0040, atf_fs_access_w, EACCES, 0 },
+ { 0040, atf_fs_access_x, EACCES, EACCES },
+
+ { 0100, atf_fs_access_r, EACCES, 0 },
+ { 0100, atf_fs_access_w, EACCES, 0 },
+ { 0100, atf_fs_access_x, 0, 0 },
+ { 0200, atf_fs_access_r, EACCES, 0 },
+ { 0200, atf_fs_access_w, 0, 0 },
+ { 0200, atf_fs_access_x, EACCES, EACCES },
+ { 0400, atf_fs_access_r, 0, 0 },
+ { 0400, atf_fs_access_w, EACCES, 0 },
+ { 0400, atf_fs_access_x, EACCES, EACCES },
+
+ { 0, 0, 0, 0 }
+ };
+ struct tests *t;
+ atf_fs_path_t p;
+ atf_error_t err;
+
+ RE(atf_fs_path_init_fmt(&p, "the-file"));
+
+ printf("Non-existent file checks\n");
+ for (m = &modes[0]; *m != 0; m++) {
+ err = atf_fs_eaccess(&p, *m);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_REQUIRE_EQ(atf_libc_error_code(err), ENOENT);
+ atf_error_free(err);
+ }
+
+ create_file(atf_fs_path_cstring(&p), 0000);
+ ATF_REQUIRE(chown(atf_fs_path_cstring(&p), geteuid(), getegid()) != -1);
+
+ for (t = &tests[0]; t->amode != 0; t++) {
+ const int experr = atf_user_is_root() ? t->rerror : t->uerror;
+
+ printf("\n");
+ printf("File mode : %04o\n", (unsigned int)t->fmode);
+ printf("Access mode : 0x%02x\n", t->amode);
+
+ ATF_REQUIRE(chmod(atf_fs_path_cstring(&p), t->fmode) != -1);
+
+ /* First, existence check. */
+ err = atf_fs_eaccess(&p, atf_fs_access_f);
+ ATF_REQUIRE(!atf_is_error(err));
+
+ /* Now do the specific test case. */
+ printf("Expected error: %d\n", experr);
+ err = atf_fs_eaccess(&p, t->amode);
+ if (atf_is_error(err)) {
+ if (atf_error_is(err, "libc"))
+ printf("Error : %d\n", atf_libc_error_code(err));
+ else
+ printf("Error : Non-libc error\n");
+ } else
+ printf("Error : None\n");
+ if (experr == 0) {
+ ATF_REQUIRE(!atf_is_error(err));
+ } else {
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_REQUIRE_EQ(atf_libc_error_code(err), experr);
+ atf_error_free(err);
+ }
+ }
+
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(getcwd);
+ATF_TC_HEAD(getcwd, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_getcwd function");
+}
+ATF_TC_BODY(getcwd, tc)
+{
+ atf_fs_path_t cwd1, cwd2;
+
+ create_dir ("root", 0755);
+
+ RE(atf_fs_getcwd(&cwd1));
+ ATF_REQUIRE(chdir("root") != -1);
+ RE(atf_fs_getcwd(&cwd2));
+
+ RE(atf_fs_path_append_fmt(&cwd1, "root"));
+
+ ATF_REQUIRE(atf_equal_fs_path_fs_path(&cwd1, &cwd2));
+
+ atf_fs_path_fini(&cwd2);
+ atf_fs_path_fini(&cwd1);
+}
+
+ATF_TC(rmdir_empty);
+ATF_TC_HEAD(rmdir_empty, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_rmdir function");
+}
+ATF_TC_BODY(rmdir_empty, tc)
+{
+ atf_fs_path_t p;
+
+ RE(atf_fs_path_init_fmt(&p, "test-dir"));
+
+ ATF_REQUIRE(mkdir("test-dir", 0755) != -1);
+ ATF_REQUIRE(exists(&p));
+ RE(atf_fs_rmdir(&p));
+ ATF_REQUIRE(!exists(&p));
+
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(rmdir_enotempty);
+ATF_TC_HEAD(rmdir_enotempty, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_rmdir function");
+}
+ATF_TC_BODY(rmdir_enotempty, tc)
+{
+ atf_fs_path_t p;
+ atf_error_t err;
+
+ RE(atf_fs_path_init_fmt(&p, "test-dir"));
+
+ ATF_REQUIRE(mkdir("test-dir", 0755) != -1);
+ ATF_REQUIRE(exists(&p));
+ create_file("test-dir/foo", 0644);
+
+ err = atf_fs_rmdir(&p);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_REQUIRE_EQ(atf_libc_error_code(err), ENOTEMPTY);
+ atf_error_free(err);
+
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(rmdir_eperm);
+ATF_TC_HEAD(rmdir_eperm, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_rmdir function");
+}
+ATF_TC_BODY(rmdir_eperm, tc)
+{
+ atf_fs_path_t p;
+ atf_error_t err;
+
+ RE(atf_fs_path_init_fmt(&p, "test-dir/foo"));
+
+ ATF_REQUIRE(mkdir("test-dir", 0755) != -1);
+ ATF_REQUIRE(mkdir("test-dir/foo", 0755) != -1);
+ ATF_REQUIRE(chmod("test-dir", 0555) != -1);
+ ATF_REQUIRE(exists(&p));
+
+ err = atf_fs_rmdir(&p);
+ if (atf_user_is_root()) {
+ ATF_REQUIRE(!atf_is_error(err));
+ } else {
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_REQUIRE_EQ(atf_libc_error_code(err), EACCES);
+ atf_error_free(err);
+ }
+
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(mkdtemp_ok);
+ATF_TC_HEAD(mkdtemp_ok, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkdtemp function, "
+ "successful execution");
+}
+ATF_TC_BODY(mkdtemp_ok, tc)
+{
+ atf_fs_path_t p1, p2;
+ atf_fs_stat_t s1, s2;
+
+ RE(atf_fs_path_init_fmt(&p1, "testdir.XXXXXX"));
+ RE(atf_fs_path_init_fmt(&p2, "testdir.XXXXXX"));
+ RE(atf_fs_mkdtemp(&p1));
+ RE(atf_fs_mkdtemp(&p2));
+ ATF_REQUIRE(!atf_equal_fs_path_fs_path(&p1, &p2));
+ ATF_REQUIRE(exists(&p1));
+ ATF_REQUIRE(exists(&p2));
+
+ RE(atf_fs_stat_init(&s1, &p1));
+ ATF_REQUIRE_EQ(atf_fs_stat_get_type(&s1), atf_fs_stat_dir_type);
+ ATF_REQUIRE( atf_fs_stat_is_owner_readable(&s1));
+ ATF_REQUIRE( atf_fs_stat_is_owner_writable(&s1));
+ ATF_REQUIRE( atf_fs_stat_is_owner_executable(&s1));
+ ATF_REQUIRE(!atf_fs_stat_is_group_readable(&s1));
+ ATF_REQUIRE(!atf_fs_stat_is_group_writable(&s1));
+ ATF_REQUIRE(!atf_fs_stat_is_group_executable(&s1));
+ ATF_REQUIRE(!atf_fs_stat_is_other_readable(&s1));
+ ATF_REQUIRE(!atf_fs_stat_is_other_writable(&s1));
+ ATF_REQUIRE(!atf_fs_stat_is_other_executable(&s1));
+
+ RE(atf_fs_stat_init(&s2, &p2));
+ ATF_REQUIRE_EQ(atf_fs_stat_get_type(&s2), atf_fs_stat_dir_type);
+ ATF_REQUIRE( atf_fs_stat_is_owner_readable(&s2));
+ ATF_REQUIRE( atf_fs_stat_is_owner_writable(&s2));
+ ATF_REQUIRE( atf_fs_stat_is_owner_executable(&s2));
+ ATF_REQUIRE(!atf_fs_stat_is_group_readable(&s2));
+ ATF_REQUIRE(!atf_fs_stat_is_group_writable(&s2));
+ ATF_REQUIRE(!atf_fs_stat_is_group_executable(&s2));
+ ATF_REQUIRE(!atf_fs_stat_is_other_readable(&s2));
+ ATF_REQUIRE(!atf_fs_stat_is_other_writable(&s2));
+ ATF_REQUIRE(!atf_fs_stat_is_other_executable(&s2));
+
+ atf_fs_stat_fini(&s2);
+ atf_fs_stat_fini(&s1);
+ atf_fs_path_fini(&p2);
+ atf_fs_path_fini(&p1);
+}
+
+ATF_TC(mkdtemp_err);
+ATF_TC_HEAD(mkdtemp_err, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkdtemp function, "
+ "error conditions");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(mkdtemp_err, tc)
+{
+ atf_error_t err;
+ atf_fs_path_t p;
+
+ ATF_REQUIRE(mkdir("dir", 0555) != -1);
+
+ RE(atf_fs_path_init_fmt(&p, "dir/testdir.XXXXXX"));
+
+ err = atf_fs_mkdtemp(&p);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_CHECK_EQ(atf_libc_error_code(err), EACCES);
+ atf_error_free(err);
+
+ ATF_CHECK(!exists(&p));
+ ATF_CHECK(strcmp(atf_fs_path_cstring(&p), "dir/testdir.XXXXXX") == 0);
+
+ atf_fs_path_fini(&p);
+}
+
+static
+void
+do_umask_check(atf_error_t (*const mk_func)(atf_fs_path_t *),
+ atf_fs_path_t *path, const mode_t test_mask,
+ const char *str_mask, const char *exp_name)
+{
+ char buf[1024];
+ int old_umask;
+ atf_error_t err;
+
+ printf("Creating temporary %s with umask %s\n", exp_name, str_mask);
+
+ old_umask = umask(test_mask);
+ err = mk_func(path);
+ (void)umask(old_umask);
+
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "invalid_umask"));
+ atf_error_format(err, buf, sizeof(buf));
+ ATF_CHECK(strstr(buf, exp_name) != NULL);
+ ATF_CHECK(strstr(buf, str_mask) != NULL);
+ atf_error_free(err);
+}
+
+ATF_TC(mkdtemp_umask);
+ATF_TC_HEAD(mkdtemp_umask, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkdtemp function "
+ "causing an error due to a too strict umask");
+}
+ATF_TC_BODY(mkdtemp_umask, tc)
+{
+ atf_fs_path_t p;
+
+ RE(atf_fs_path_init_fmt(&p, "testdir.XXXXXX"));
+
+ do_umask_check(atf_fs_mkdtemp, &p, 00100, "00100", "directory");
+ do_umask_check(atf_fs_mkdtemp, &p, 00200, "00200", "directory");
+ do_umask_check(atf_fs_mkdtemp, &p, 00400, "00400", "directory");
+ do_umask_check(atf_fs_mkdtemp, &p, 00500, "00500", "directory");
+ do_umask_check(atf_fs_mkdtemp, &p, 00600, "00600", "directory");
+
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(mkstemp_ok);
+ATF_TC_HEAD(mkstemp_ok, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkstemp function, "
+ "successful execution");
+}
+ATF_TC_BODY(mkstemp_ok, tc)
+{
+ int fd1, fd2;
+ atf_fs_path_t p1, p2;
+ atf_fs_stat_t s1, s2;
+
+ RE(atf_fs_path_init_fmt(&p1, "testfile.XXXXXX"));
+ RE(atf_fs_path_init_fmt(&p2, "testfile.XXXXXX"));
+ fd1 = fd2 = -1;
+ RE(atf_fs_mkstemp(&p1, &fd1));
+ RE(atf_fs_mkstemp(&p2, &fd2));
+ ATF_REQUIRE(!atf_equal_fs_path_fs_path(&p1, &p2));
+ ATF_REQUIRE(exists(&p1));
+ ATF_REQUIRE(exists(&p2));
+
+ ATF_CHECK(fd1 != -1);
+ ATF_CHECK(fd2 != -1);
+ ATF_CHECK(write(fd1, "foo", 3) == 3);
+ ATF_CHECK(write(fd2, "bar", 3) == 3);
+ close(fd1);
+ close(fd2);
+
+ RE(atf_fs_stat_init(&s1, &p1));
+ ATF_CHECK_EQ(atf_fs_stat_get_type(&s1), atf_fs_stat_reg_type);
+ ATF_CHECK( atf_fs_stat_is_owner_readable(&s1));
+ ATF_CHECK( atf_fs_stat_is_owner_writable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_owner_executable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_group_readable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_group_writable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_group_executable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_other_readable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_other_writable(&s1));
+ ATF_CHECK(!atf_fs_stat_is_other_executable(&s1));
+
+ RE(atf_fs_stat_init(&s2, &p2));
+ ATF_CHECK_EQ(atf_fs_stat_get_type(&s2), atf_fs_stat_reg_type);
+ ATF_CHECK( atf_fs_stat_is_owner_readable(&s2));
+ ATF_CHECK( atf_fs_stat_is_owner_writable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_owner_executable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_group_readable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_group_writable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_group_executable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_other_readable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_other_writable(&s2));
+ ATF_CHECK(!atf_fs_stat_is_other_executable(&s2));
+
+ atf_fs_stat_fini(&s2);
+ atf_fs_stat_fini(&s1);
+ atf_fs_path_fini(&p2);
+ atf_fs_path_fini(&p1);
+}
+
+ATF_TC(mkstemp_err);
+ATF_TC_HEAD(mkstemp_err, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkstemp function, "
+ "error conditions");
+ atf_tc_set_md_var(tc, "require.user", "unprivileged");
+}
+ATF_TC_BODY(mkstemp_err, tc)
+{
+ int fd;
+ atf_error_t err;
+ atf_fs_path_t p;
+
+ ATF_REQUIRE(mkdir("dir", 0555) != -1);
+
+ RE(atf_fs_path_init_fmt(&p, "dir/testfile.XXXXXX"));
+ fd = 1234;
+
+ err = atf_fs_mkstemp(&p, &fd);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_CHECK_EQ(atf_libc_error_code(err), EACCES);
+ atf_error_free(err);
+
+ ATF_CHECK(!exists(&p));
+ ATF_CHECK(strcmp(atf_fs_path_cstring(&p), "dir/testfile.XXXXXX") == 0);
+ ATF_CHECK_EQ(fd, 1234);
+
+ atf_fs_path_fini(&p);
+}
+
+ATF_TC(mkstemp_umask);
+ATF_TC_HEAD(mkstemp_umask, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkstemp function "
+ "causing an error due to a too strict umask");
+}
+ATF_TC_BODY(mkstemp_umask, tc)
+{
+ atf_fs_path_t p;
+
+ RE(atf_fs_path_init_fmt(&p, "testfile.XXXXXX"));
+
+ do_umask_check(mkstemp_discard_fd, &p, 00100, "00100", "regular file");
+ do_umask_check(mkstemp_discard_fd, &p, 00200, "00200", "regular file");
+ do_umask_check(mkstemp_discard_fd, &p, 00400, "00400", "regular file");
+
+ atf_fs_path_fini(&p);
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Add the tests for the "atf_fs_path" type. */
+ ATF_TP_ADD_TC(tp, path_normalize);
+ ATF_TP_ADD_TC(tp, path_copy);
+ ATF_TP_ADD_TC(tp, path_is_absolute);
+ ATF_TP_ADD_TC(tp, path_is_root);
+ ATF_TP_ADD_TC(tp, path_branch_path);
+ ATF_TP_ADD_TC(tp, path_leaf_name);
+ ATF_TP_ADD_TC(tp, path_append);
+ ATF_TP_ADD_TC(tp, path_to_absolute);
+ ATF_TP_ADD_TC(tp, path_equal);
+
+ /* Add the tests for the "atf_fs_stat" type. */
+ ATF_TP_ADD_TC(tp, stat_mode);
+ ATF_TP_ADD_TC(tp, stat_type);
+ ATF_TP_ADD_TC(tp, stat_perms);
+
+ /* Add the tests for the free functions. */
+ ATF_TP_ADD_TC(tp, eaccess);
+ ATF_TP_ADD_TC(tp, exists);
+ ATF_TP_ADD_TC(tp, getcwd);
+ ATF_TP_ADD_TC(tp, rmdir_empty);
+ ATF_TP_ADD_TC(tp, rmdir_enotempty);
+ ATF_TP_ADD_TC(tp, rmdir_eperm);
+ ATF_TP_ADD_TC(tp, mkdtemp_ok);
+ ATF_TP_ADD_TC(tp, mkdtemp_err);
+ ATF_TP_ADD_TC(tp, mkdtemp_umask);
+ ATF_TP_ADD_TC(tp, mkstemp_ok);
+ ATF_TP_ADD_TC(tp, mkstemp_err);
+ ATF_TP_ADD_TC(tp, mkstemp_umask);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/list.c b/unit/atf-src/atf-c/detail/list.c
new file mode 100644
index 0000000..d14216e
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/list.c
@@ -0,0 +1,388 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/list.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "atf-c/detail/sanity.h"
+#include "atf-c/error.h"
+#include "atf-c/utils.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+struct list_entry {
+ struct list_entry *m_prev;
+ struct list_entry *m_next;
+ void *m_object;
+ bool m_managed;
+};
+
+static
+atf_list_citer_t
+entry_to_citer(const atf_list_t *l, const struct list_entry *le)
+{
+ atf_list_citer_t iter;
+ iter.m_list = l;
+ iter.m_entry = le;
+ return iter;
+}
+
+static
+atf_list_iter_t
+entry_to_iter(atf_list_t *l, struct list_entry *le)
+{
+ atf_list_iter_t iter;
+ iter.m_list = l;
+ iter.m_entry = le;
+ return iter;
+}
+
+static
+struct list_entry *
+new_entry(void *object, bool managed)
+{
+ struct list_entry *le;
+
+ le = (struct list_entry *)malloc(sizeof(*le));
+ if (le != NULL) {
+ le->m_prev = le->m_next = NULL;
+ le->m_object = object;
+ le->m_managed = managed;
+ } else
+ free(object);
+
+ return le;
+}
+
+static
+void
+delete_entry(struct list_entry *le)
+{
+ if (le->m_managed)
+ free(le->m_object);
+
+ free(le);
+}
+
+static
+struct list_entry *
+new_entry_and_link(void *object, bool managed, struct list_entry *prev,
+ struct list_entry *next)
+{
+ struct list_entry *le;
+
+ le = new_entry(object, managed);
+ if (le != NULL) {
+ le->m_prev = prev;
+ le->m_next = next;
+
+ prev->m_next = le;
+ next->m_prev = le;
+ }
+
+ return le;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_list_citer" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Getters.
+ */
+
+const void *
+atf_list_citer_data(const atf_list_citer_t citer)
+{
+ const struct list_entry *le = citer.m_entry;
+ PRE(le != NULL);
+ return le->m_object;
+}
+
+atf_list_citer_t
+atf_list_citer_next(const atf_list_citer_t citer)
+{
+ const struct list_entry *le = citer.m_entry;
+ atf_list_citer_t newciter;
+
+ PRE(le != NULL);
+
+ newciter = citer;
+ newciter.m_entry = le->m_next;
+
+ return newciter;
+}
+
+bool
+atf_equal_list_citer_list_citer(const atf_list_citer_t i1,
+ const atf_list_citer_t i2)
+{
+ return i1.m_list == i2.m_list && i1.m_entry == i2.m_entry;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_list_iter" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Getters.
+ */
+
+void *
+atf_list_iter_data(const atf_list_iter_t iter)
+{
+ const struct list_entry *le = iter.m_entry;
+ PRE(le != NULL);
+ return le->m_object;
+}
+
+atf_list_iter_t
+atf_list_iter_next(const atf_list_iter_t iter)
+{
+ const struct list_entry *le = iter.m_entry;
+ atf_list_iter_t newiter;
+
+ PRE(le != NULL);
+
+ newiter = iter;
+ newiter.m_entry = le->m_next;
+
+ return newiter;
+}
+
+bool
+atf_equal_list_iter_list_iter(const atf_list_iter_t i1,
+ const atf_list_iter_t i2)
+{
+ return i1.m_list == i2.m_list && i1.m_entry == i2.m_entry;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_list" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors and destructors.
+ */
+
+atf_error_t
+atf_list_init(atf_list_t *l)
+{
+ struct list_entry *lebeg, *leend;
+
+ lebeg = new_entry(NULL, false);
+ if (lebeg == NULL) {
+ return atf_no_memory_error();
+ }
+
+ leend = new_entry(NULL, false);
+ if (leend == NULL) {
+ free(lebeg);
+ return atf_no_memory_error();
+ }
+
+ lebeg->m_next = leend;
+ lebeg->m_prev = NULL;
+
+ leend->m_next = NULL;
+ leend->m_prev = lebeg;
+
+ l->m_size = 0;
+ l->m_begin = lebeg;
+ l->m_end = leend;
+
+ return atf_no_error();
+}
+
+void
+atf_list_fini(atf_list_t *l)
+{
+ struct list_entry *le;
+ size_t freed;
+
+ le = (struct list_entry *)l->m_begin;
+ freed = 0;
+ while (le != NULL) {
+ struct list_entry *lenext;
+
+ lenext = le->m_next;
+ delete_entry(le);
+ le = lenext;
+
+ freed++;
+ }
+ INV(freed == l->m_size + 2);
+}
+
+/*
+ * Getters.
+ */
+
+atf_list_iter_t
+atf_list_begin(atf_list_t *l)
+{
+ struct list_entry *le = l->m_begin;
+ return entry_to_iter(l, le->m_next);
+}
+
+atf_list_citer_t
+atf_list_begin_c(const atf_list_t *l)
+{
+ const struct list_entry *le = l->m_begin;
+ return entry_to_citer(l, le->m_next);
+}
+
+atf_list_iter_t
+atf_list_end(atf_list_t *l)
+{
+ return entry_to_iter(l, l->m_end);
+}
+
+atf_list_citer_t
+atf_list_end_c(const atf_list_t *l)
+{
+ return entry_to_citer(l, l->m_end);
+}
+
+void *
+atf_list_index(atf_list_t *list, const size_t idx)
+{
+ atf_list_iter_t iter;
+
+ PRE(idx < atf_list_size(list));
+
+ iter = atf_list_begin(list);
+ {
+ size_t pos = 0;
+ while (pos < idx &&
+ !atf_equal_list_iter_list_iter((iter), atf_list_end(list))) {
+ iter = atf_list_iter_next(iter);
+ pos++;
+ }
+ }
+ return atf_list_iter_data(iter);
+}
+
+const void *
+atf_list_index_c(const atf_list_t *list, const size_t idx)
+{
+ atf_list_citer_t iter;
+
+ PRE(idx < atf_list_size(list));
+
+ iter = atf_list_begin_c(list);
+ {
+ size_t pos = 0;
+ while (pos < idx &&
+ !atf_equal_list_citer_list_citer((iter),
+ atf_list_end_c(list))) {
+ iter = atf_list_citer_next(iter);
+ pos++;
+ }
+ }
+ return atf_list_citer_data(iter);
+}
+
+size_t
+atf_list_size(const atf_list_t *l)
+{
+ return l->m_size;
+}
+
+char **
+atf_list_to_charpp(const atf_list_t *l)
+{
+ char **array;
+ atf_list_citer_t iter;
+ size_t i;
+
+ array = malloc(sizeof(char *) * (atf_list_size(l) + 1));
+ if (array == NULL)
+ goto out;
+
+ i = 0;
+ atf_list_for_each_c(iter, l) {
+ array[i] = strdup((const char *)atf_list_citer_data(iter));
+ if (array[i] == NULL) {
+ atf_utils_free_charpp(array);
+ array = NULL;
+ goto out;
+ }
+
+ i++;
+ }
+ array[i] = NULL;
+
+out:
+ return array;
+}
+
+/*
+ * Modifiers.
+ */
+
+atf_error_t
+atf_list_append(atf_list_t *l, void *data, bool managed)
+{
+ struct list_entry *le, *next, *prev;
+ atf_error_t err;
+
+ next = (struct list_entry *)l->m_end;
+ prev = next->m_prev;
+ le = new_entry_and_link(data, managed, prev, next);
+ if (le == NULL)
+ err = atf_no_memory_error();
+ else {
+ l->m_size++;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+void
+atf_list_append_list(atf_list_t *l, atf_list_t *src)
+{
+ struct list_entry *e1, *e2, *ghost1, *ghost2;
+
+ ghost1 = (struct list_entry *)l->m_end;
+ ghost2 = (struct list_entry *)src->m_begin;
+
+ e1 = ghost1->m_prev;
+ e2 = ghost2->m_next;
+
+ delete_entry(ghost1);
+ delete_entry(ghost2);
+
+ e1->m_next = e2;
+ e2->m_prev = e1;
+
+ l->m_end = src->m_end;
+ l->m_size += src->m_size;
+}
diff --git a/unit/atf-src/atf-c/detail/list.h b/unit/atf-src/atf-c/detail/list.h
new file mode 100644
index 0000000..b250066
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/list.h
@@ -0,0 +1,111 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_DETAIL_LIST_H)
+#define ATF_C_DETAIL_LIST_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <atf-c/error_fwd.h>
+
+/* ---------------------------------------------------------------------
+ * The "atf_list_citer" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_list_citer {
+ const struct atf_list *m_list;
+ const void *m_entry;
+};
+typedef struct atf_list_citer atf_list_citer_t;
+
+/* Getters. */
+const void *atf_list_citer_data(const atf_list_citer_t);
+atf_list_citer_t atf_list_citer_next(const atf_list_citer_t);
+
+/* Operators. */
+bool atf_equal_list_citer_list_citer(const atf_list_citer_t,
+ const atf_list_citer_t);
+
+/* ---------------------------------------------------------------------
+ * The "atf_list_iter" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_list_iter {
+ struct atf_list *m_list;
+ void *m_entry;
+};
+typedef struct atf_list_iter atf_list_iter_t;
+
+/* Getters. */
+void *atf_list_iter_data(const atf_list_iter_t);
+atf_list_iter_t atf_list_iter_next(const atf_list_iter_t);
+
+/* Operators. */
+bool atf_equal_list_iter_list_iter(const atf_list_iter_t,
+ const atf_list_iter_t);
+
+/* ---------------------------------------------------------------------
+ * The "atf_list" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_list {
+ void *m_begin;
+ void *m_end;
+
+ size_t m_size;
+};
+typedef struct atf_list atf_list_t;
+
+/* Constructors and destructors */
+atf_error_t atf_list_init(atf_list_t *);
+void atf_list_fini(atf_list_t *);
+
+/* Getters. */
+atf_list_iter_t atf_list_begin(atf_list_t *);
+atf_list_citer_t atf_list_begin_c(const atf_list_t *);
+atf_list_iter_t atf_list_end(atf_list_t *);
+atf_list_citer_t atf_list_end_c(const atf_list_t *);
+void *atf_list_index(atf_list_t *, const size_t);
+const void *atf_list_index_c(const atf_list_t *, const size_t);
+size_t atf_list_size(const atf_list_t *);
+char **atf_list_to_charpp(const atf_list_t *);
+
+/* Modifiers. */
+atf_error_t atf_list_append(atf_list_t *, void *, bool);
+void atf_list_append_list(atf_list_t *, atf_list_t *);
+
+/* Macros. */
+#define atf_list_for_each(iter, list) \
+ for (iter = atf_list_begin(list); \
+ !atf_equal_list_iter_list_iter((iter), atf_list_end(list)); \
+ iter = atf_list_iter_next(iter))
+#define atf_list_for_each_c(iter, list) \
+ for (iter = atf_list_begin_c(list); \
+ !atf_equal_list_citer_list_citer((iter), atf_list_end_c(list)); \
+ iter = atf_list_citer_next(iter))
+
+#endif /* !defined(ATF_C_DETAIL_LIST_H) */
diff --git a/unit/atf-src/atf-c/detail/list_test.c b/unit/atf-src/atf-c/detail/list_test.c
new file mode 100644
index 0000000..82a715d
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/list_test.c
@@ -0,0 +1,365 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/list.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+#include "atf-c/utils.h"
+
+/* ---------------------------------------------------------------------
+ * Tests for the "atf_list" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors and destructors.
+ */
+
+ATF_TC(list_init);
+ATF_TC_HEAD(list_init, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_init function");
+}
+ATF_TC_BODY(list_init, tc)
+{
+ atf_list_t list;
+
+ RE(atf_list_init(&list));
+ ATF_REQUIRE_EQ(atf_list_size(&list), 0);
+ atf_list_fini(&list);
+}
+
+/*
+ * Getters.
+ */
+
+ATF_TC(list_index);
+ATF_TC_HEAD(list_index, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_index function");
+}
+ATF_TC_BODY(list_index, tc)
+{
+ atf_list_t list;
+ int i1 = 1;
+ int i2 = 5;
+ int i3 = 9;
+
+ RE(atf_list_init(&list));
+ RE(atf_list_append(&list, &i1, false));
+ RE(atf_list_append(&list, &i2, false));
+ RE(atf_list_append(&list, &i3, false));
+
+ ATF_CHECK_EQ(*(int *)atf_list_index(&list, 0), 1);
+ ATF_CHECK_EQ(*(int *)atf_list_index(&list, 1), 5);
+ ATF_CHECK_EQ(*(int *)atf_list_index(&list, 2), 9);
+
+ atf_list_fini(&list);
+}
+
+ATF_TC(list_index_c);
+ATF_TC_HEAD(list_index_c, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_index_c function");
+}
+ATF_TC_BODY(list_index_c, tc)
+{
+ atf_list_t list;
+ int i1 = 1;
+ int i2 = 5;
+ int i3 = 9;
+
+ RE(atf_list_init(&list));
+ RE(atf_list_append(&list, &i1, false));
+ RE(atf_list_append(&list, &i2, false));
+ RE(atf_list_append(&list, &i3, false));
+
+ ATF_CHECK_EQ(*(const int *)atf_list_index_c(&list, 0), 1);
+ ATF_CHECK_EQ(*(const int *)atf_list_index_c(&list, 1), 5);
+ ATF_CHECK_EQ(*(const int *)atf_list_index_c(&list, 2), 9);
+
+ atf_list_fini(&list);
+}
+
+ATF_TC_WITHOUT_HEAD(list_to_charpp_empty);
+ATF_TC_BODY(list_to_charpp_empty, tc)
+{
+ atf_list_t list;
+ char **array;
+
+ RE(atf_list_init(&list));
+ ATF_REQUIRE((array = atf_list_to_charpp(&list)) != NULL);
+ atf_list_fini(&list);
+
+ ATF_CHECK_EQ(NULL, array[0]);
+ atf_utils_free_charpp(array);
+}
+
+ATF_TC_WITHOUT_HEAD(list_to_charpp_some);
+ATF_TC_BODY(list_to_charpp_some, tc)
+{
+ atf_list_t list;
+ char **array;
+
+ char s1[] = "one";
+ char s2[] = "two";
+ char s3[] = "three";
+
+ RE(atf_list_init(&list));
+ RE(atf_list_append(&list, s1, false));
+ RE(atf_list_append(&list, s2, false));
+ RE(atf_list_append(&list, s3, false));
+ ATF_REQUIRE((array = atf_list_to_charpp(&list)) != NULL);
+ atf_list_fini(&list);
+
+ ATF_CHECK_STREQ("one", array[0]);
+ ATF_CHECK_STREQ("two", array[1]);
+ ATF_CHECK_STREQ("three", array[2]);
+ ATF_CHECK_EQ(NULL, array[3]);
+ atf_utils_free_charpp(array);
+}
+
+/*
+ * Modifiers.
+ */
+
+ATF_TC(list_append);
+ATF_TC_HEAD(list_append, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_append function");
+}
+ATF_TC_BODY(list_append, tc)
+{
+ atf_list_t list;
+ size_t i;
+ char buf[] = "Test string";
+
+ RE(atf_list_init(&list));
+ for (i = 0; i < 1024; i++) {
+ ATF_REQUIRE_EQ(atf_list_size(&list), i);
+ RE(atf_list_append(&list, buf, false));
+ }
+ atf_list_fini(&list);
+}
+
+ATF_TC(list_append_list);
+ATF_TC_HEAD(list_append_list, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_append_list "
+ "function");
+}
+ATF_TC_BODY(list_append_list, tc)
+{
+ {
+ atf_list_t l1, l2;
+
+ RE(atf_list_init(&l1));
+ RE(atf_list_init(&l2));
+
+ atf_list_append_list(&l1, &l2);
+ ATF_CHECK_EQ(atf_list_size(&l1), 0);
+
+ atf_list_fini(&l1);
+ }
+
+ {
+ atf_list_t l1, l2;
+ int item = 5;
+
+ RE(atf_list_init(&l1));
+ RE(atf_list_append(&l1, &item, false));
+ RE(atf_list_init(&l2));
+
+ atf_list_append_list(&l1, &l2);
+ ATF_CHECK_EQ(atf_list_size(&l1), 1);
+ ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 0), item);
+
+ atf_list_fini(&l1);
+ }
+
+ {
+ atf_list_t l1, l2;
+ int item = 5;
+
+ RE(atf_list_init(&l1));
+ RE(atf_list_init(&l2));
+ RE(atf_list_append(&l2, &item, false));
+
+ atf_list_append_list(&l1, &l2);
+ ATF_CHECK_EQ(atf_list_size(&l1), 1);
+ ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 0), item);
+
+ atf_list_fini(&l1);
+ }
+
+ {
+ atf_list_t l1, l2;
+ int item1 = 5;
+ int item2 = 9;
+
+ RE(atf_list_init(&l1));
+ RE(atf_list_append(&l1, &item1, false));
+ RE(atf_list_init(&l2));
+ RE(atf_list_append(&l2, &item2, false));
+
+ atf_list_append_list(&l1, &l2);
+ ATF_CHECK_EQ(atf_list_size(&l1), 2);
+ ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 0), item1);
+ ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 1), item2);
+
+ atf_list_fini(&l1);
+ }
+
+ {
+ atf_list_t l1, l2;
+ atf_list_citer_t end1, end2;
+
+ RE(atf_list_init(&l1));
+ RE(atf_list_init(&l2));
+
+ end1 = atf_list_end_c(&l1);
+ end2 = atf_list_end_c(&l2);
+ /* XXX Shouldn't query m_entry here. */
+ ATF_CHECK(end1.m_entry != end2.m_entry);
+
+ atf_list_append_list(&l1, &l2);
+ ATF_CHECK(atf_list_end_c(&l1).m_entry == end2.m_entry);
+
+ atf_list_fini(&l1);
+ }
+}
+
+/*
+ * Macros.
+ */
+
+ATF_TC(list_for_each);
+ATF_TC_HEAD(list_for_each, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_for_each macro");
+}
+ATF_TC_BODY(list_for_each, tc)
+{
+ atf_list_t list;
+ atf_list_iter_t iter;
+ size_t count, i, size;
+ int nums[10];
+
+ printf("Iterating over empty list\n");
+ RE(atf_list_init(&list));
+ count = 0;
+ atf_list_for_each(iter, &list) {
+ count++;
+ printf("Item count is now %zd\n", count);
+ }
+ ATF_REQUIRE_EQ(count, 0);
+ atf_list_fini(&list);
+
+ for (size = 0; size <= 10; size++) {
+ printf("Iterating over list of %zd elements\n", size);
+ RE(atf_list_init(&list));
+ for (i = 0; i < size; i++) {
+ nums[i] = i + 1;
+ RE(atf_list_append(&list, &nums[i], false));
+ }
+ count = 0;
+ atf_list_for_each(iter, &list) {
+ printf("Retrieved item: %d\n", *(int *)atf_list_iter_data(iter));
+ count++;
+ }
+ ATF_REQUIRE_EQ(count, size);
+ atf_list_fini(&list);
+ }
+}
+
+ATF_TC(list_for_each_c);
+ATF_TC_HEAD(list_for_each_c, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_list_for_each_c macro");
+}
+ATF_TC_BODY(list_for_each_c, tc)
+{
+ atf_list_t list;
+ atf_list_citer_t iter;
+ size_t count, i, size;
+ int nums[10];
+
+ printf("Iterating over empty list\n");
+ RE(atf_list_init(&list));
+ count = 0;
+ atf_list_for_each_c(iter, &list) {
+ count++;
+ printf("Item count is now %zd\n", count);
+ }
+ ATF_REQUIRE_EQ(count, 0);
+ atf_list_fini(&list);
+
+ for (size = 0; size <= 10; size++) {
+ printf("Iterating over list of %zd elements\n", size);
+ RE(atf_list_init(&list));
+ for (i = 0; i < size; i++) {
+ nums[i] = i + 1;
+ RE(atf_list_append(&list, &nums[i], false));
+ }
+ count = 0;
+ atf_list_for_each_c(iter, &list) {
+ printf("Retrieved item: %d\n",
+ *(const int *)atf_list_citer_data(iter));
+ count++;
+ }
+ ATF_REQUIRE_EQ(count, size);
+ atf_list_fini(&list);
+ }
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Constructors and destructors. */
+ ATF_TP_ADD_TC(tp, list_init);
+
+ /* Getters. */
+ ATF_TP_ADD_TC(tp, list_index);
+ ATF_TP_ADD_TC(tp, list_index_c);
+ ATF_TP_ADD_TC(tp, list_to_charpp_empty);
+ ATF_TP_ADD_TC(tp, list_to_charpp_some);
+
+ /* Modifiers. */
+ ATF_TP_ADD_TC(tp, list_append);
+ ATF_TP_ADD_TC(tp, list_append_list);
+
+ /* Macros. */
+ ATF_TP_ADD_TC(tp, list_for_each);
+ ATF_TP_ADD_TC(tp, list_for_each_c);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/map.c b/unit/atf-src/atf-c/detail/map.c
new file mode 100644
index 0000000..45de47f
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/map.c
@@ -0,0 +1,380 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/map.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "atf-c/detail/sanity.h"
+#include "atf-c/error.h"
+#include "atf-c/utils.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+struct map_entry {
+ char *m_key;
+ void *m_value;
+ bool m_managed;
+};
+
+static
+struct map_entry *
+new_entry(const char *key, void *value, bool managed)
+{
+ struct map_entry *me;
+
+ me = (struct map_entry *)malloc(sizeof(*me));
+ if (me != NULL) {
+ me->m_key = strdup(key);
+ if (me->m_key == NULL) {
+ free(me);
+ me = NULL;
+ } else {
+ me->m_value = value;
+ me->m_managed = managed;
+ }
+ }
+
+ return me;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_map_citer" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Getters.
+ */
+
+const char *
+atf_map_citer_key(const atf_map_citer_t citer)
+{
+ const struct map_entry *me = citer.m_entry;
+ PRE(me != NULL);
+ return me->m_key;
+}
+
+const void *
+atf_map_citer_data(const atf_map_citer_t citer)
+{
+ const struct map_entry *me = citer.m_entry;
+ PRE(me != NULL);
+ return me->m_value;
+}
+
+atf_map_citer_t
+atf_map_citer_next(const atf_map_citer_t citer)
+{
+ atf_map_citer_t newciter;
+
+ newciter = citer;
+ newciter.m_listiter = atf_list_citer_next(citer.m_listiter);
+ newciter.m_entry = ((const struct map_entry *)
+ atf_list_citer_data(newciter.m_listiter));
+
+ return newciter;
+}
+
+bool
+atf_equal_map_citer_map_citer(const atf_map_citer_t i1,
+ const atf_map_citer_t i2)
+{
+ return i1.m_map == i2.m_map && i1.m_entry == i2.m_entry;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_map_iter" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Getters.
+ */
+
+const char *
+atf_map_iter_key(const atf_map_iter_t iter)
+{
+ const struct map_entry *me = iter.m_entry;
+ PRE(me != NULL);
+ return me->m_key;
+}
+
+void *
+atf_map_iter_data(const atf_map_iter_t iter)
+{
+ const struct map_entry *me = iter.m_entry;
+ PRE(me != NULL);
+ return me->m_value;
+}
+
+atf_map_iter_t
+atf_map_iter_next(const atf_map_iter_t iter)
+{
+ atf_map_iter_t newiter;
+
+ newiter = iter;
+ newiter.m_listiter = atf_list_iter_next(iter.m_listiter);
+ newiter.m_entry = ((struct map_entry *)
+ atf_list_iter_data(newiter.m_listiter));
+
+ return newiter;
+}
+
+bool
+atf_equal_map_iter_map_iter(const atf_map_iter_t i1,
+ const atf_map_iter_t i2)
+{
+ return i1.m_map == i2.m_map && i1.m_entry == i2.m_entry;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_map" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors and destructors.
+ */
+
+atf_error_t
+atf_map_init(atf_map_t *m)
+{
+ return atf_list_init(&m->m_list);
+}
+
+atf_error_t
+atf_map_init_charpp(atf_map_t *m, const char *const *array)
+{
+ atf_error_t err;
+ const char *const *ptr = array;
+
+ err = atf_map_init(m);
+ if (array != NULL) {
+ while (!atf_is_error(err) && *ptr != NULL) {
+ const char *key, *value;
+
+ key = *ptr;
+ INV(key != NULL);
+ ptr++;
+
+ if ((value = *ptr) == NULL) {
+ err = atf_libc_error(EINVAL, "List too short; no value for "
+ "key '%s' provided", key); /* XXX: Not really libc_error */
+ break;
+ }
+ ptr++;
+
+ err = atf_map_insert(m, key, strdup(value), true);
+ }
+ }
+
+ if (atf_is_error(err))
+ atf_map_fini(m);
+
+ return err;
+}
+
+void
+atf_map_fini(atf_map_t *m)
+{
+ atf_list_iter_t iter;
+
+ atf_list_for_each(iter, &m->m_list) {
+ struct map_entry *me = atf_list_iter_data(iter);
+
+ if (me->m_managed)
+ free(me->m_value);
+ free(me->m_key);
+ free(me);
+ }
+ atf_list_fini(&m->m_list);
+}
+
+/*
+ * Getters.
+ */
+
+atf_map_iter_t
+atf_map_begin(atf_map_t *m)
+{
+ atf_map_iter_t iter;
+ iter.m_map = m;
+ iter.m_listiter = atf_list_begin(&m->m_list);
+ iter.m_entry = atf_list_iter_data(iter.m_listiter);
+ return iter;
+}
+
+atf_map_citer_t
+atf_map_begin_c(const atf_map_t *m)
+{
+ atf_map_citer_t citer;
+ citer.m_map = m;
+ citer.m_listiter = atf_list_begin_c(&m->m_list);
+ citer.m_entry = atf_list_citer_data(citer.m_listiter);
+ return citer;
+}
+
+atf_map_iter_t
+atf_map_end(atf_map_t *m)
+{
+ atf_map_iter_t iter;
+ iter.m_map = m;
+ iter.m_entry = NULL;
+ iter.m_listiter = atf_list_end(&m->m_list);
+ return iter;
+}
+
+atf_map_citer_t
+atf_map_end_c(const atf_map_t *m)
+{
+ atf_map_citer_t iter;
+ iter.m_map = m;
+ iter.m_entry = NULL;
+ iter.m_listiter = atf_list_end_c(&m->m_list);
+ return iter;
+}
+
+atf_map_iter_t
+atf_map_find(atf_map_t *m, const char *key)
+{
+ atf_list_iter_t iter;
+
+ atf_list_for_each(iter, &m->m_list) {
+ struct map_entry *me = atf_list_iter_data(iter);
+
+ if (strcmp(me->m_key, key) == 0) {
+ atf_map_iter_t i;
+ i.m_map = m;
+ i.m_entry = me;
+ i.m_listiter = iter;
+ return i;
+ }
+ }
+
+ return atf_map_end(m);
+}
+
+atf_map_citer_t
+atf_map_find_c(const atf_map_t *m, const char *key)
+{
+ atf_list_citer_t iter;
+
+ atf_list_for_each_c(iter, &m->m_list) {
+ const struct map_entry *me = atf_list_citer_data(iter);
+
+ if (strcmp(me->m_key, key) == 0) {
+ atf_map_citer_t i;
+ i.m_map = m;
+ i.m_entry = me;
+ i.m_listiter = iter;
+ return i;
+ }
+ }
+
+ return atf_map_end_c(m);
+}
+
+size_t
+atf_map_size(const atf_map_t *m)
+{
+ return atf_list_size(&m->m_list);
+}
+
+char **
+atf_map_to_charpp(const atf_map_t *l)
+{
+ char **array;
+ atf_map_citer_t iter;
+ size_t i;
+
+ array = malloc(sizeof(char *) * (atf_map_size(l) * 2 + 1));
+ if (array == NULL)
+ goto out;
+
+ i = 0;
+ atf_map_for_each_c(iter, l) {
+ array[i] = strdup(atf_map_citer_key(iter));
+ if (array[i] == NULL) {
+ atf_utils_free_charpp(array);
+ array = NULL;
+ goto out;
+ }
+
+ array[i + 1] = strdup((const char *)atf_map_citer_data(iter));
+ if (array[i + 1] == NULL) {
+ atf_utils_free_charpp(array);
+ array = NULL;
+ goto out;
+ }
+
+ i += 2;
+ }
+ array[i] = NULL;
+
+out:
+ return array;
+}
+
+/*
+ * Modifiers.
+ */
+
+atf_error_t
+atf_map_insert(atf_map_t *m, const char *key, void *value, bool managed)
+{
+ struct map_entry *me;
+ atf_error_t err;
+ atf_map_iter_t iter;
+
+ iter = atf_map_find(m, key);
+ if (atf_equal_map_iter_map_iter(iter, atf_map_end(m))) {
+ me = new_entry(key, value, managed);
+ if (me == NULL)
+ err = atf_no_memory_error();
+ else {
+ err = atf_list_append(&m->m_list, me, false);
+ if (atf_is_error(err)) {
+ if (managed)
+ free(value);
+ free(me->m_key);
+ free(me);
+ }
+ }
+ } else {
+ me = iter.m_entry;
+ if (me->m_managed)
+ free(me->m_value);
+
+ INV(strcmp(me->m_key, key) == 0);
+ me->m_value = value;
+ me->m_managed = managed;
+
+ err = atf_no_error();
+ }
+
+ return err;
+}
diff --git a/unit/atf-src/atf-c/detail/map.h b/unit/atf-src/atf-c/detail/map.h
new file mode 100644
index 0000000..7ca8e8a
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/map.h
@@ -0,0 +1,114 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_DETAIL_MAP_H)
+#define ATF_C_DETAIL_MAP_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include <atf-c/detail/list.h>
+#include <atf-c/error_fwd.h>
+
+/* ---------------------------------------------------------------------
+ * The "atf_map_citer" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_map_citer {
+ const struct atf_map *m_map;
+ const void *m_entry;
+ atf_list_citer_t m_listiter;
+};
+typedef struct atf_map_citer atf_map_citer_t;
+
+/* Getters. */
+const char *atf_map_citer_key(const atf_map_citer_t);
+const void *atf_map_citer_data(const atf_map_citer_t);
+atf_map_citer_t atf_map_citer_next(const atf_map_citer_t);
+
+/* Operators. */
+bool atf_equal_map_citer_map_citer(const atf_map_citer_t,
+ const atf_map_citer_t);
+
+/* ---------------------------------------------------------------------
+ * The "atf_map_iter" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_map_iter {
+ struct atf_map *m_map;
+ void *m_entry;
+ atf_list_iter_t m_listiter;
+};
+typedef struct atf_map_iter atf_map_iter_t;
+
+/* Getters. */
+const char *atf_map_iter_key(const atf_map_iter_t);
+void *atf_map_iter_data(const atf_map_iter_t);
+atf_map_iter_t atf_map_iter_next(const atf_map_iter_t);
+
+/* Operators. */
+bool atf_equal_map_iter_map_iter(const atf_map_iter_t,
+ const atf_map_iter_t);
+
+/* ---------------------------------------------------------------------
+ * The "atf_map" type.
+ * --------------------------------------------------------------------- */
+
+/* A list-based map. Typically very inefficient, but our maps are small
+ * enough. */
+struct atf_map {
+ atf_list_t m_list;
+};
+typedef struct atf_map atf_map_t;
+
+/* Constructors and destructors */
+atf_error_t atf_map_init(atf_map_t *);
+atf_error_t atf_map_init_charpp(atf_map_t *, const char *const *);
+void atf_map_fini(atf_map_t *);
+
+/* Getters. */
+atf_map_iter_t atf_map_begin(atf_map_t *);
+atf_map_citer_t atf_map_begin_c(const atf_map_t *);
+atf_map_iter_t atf_map_end(atf_map_t *);
+atf_map_citer_t atf_map_end_c(const atf_map_t *);
+atf_map_iter_t atf_map_find(atf_map_t *, const char *);
+atf_map_citer_t atf_map_find_c(const atf_map_t *, const char *);
+size_t atf_map_size(const atf_map_t *);
+char **atf_map_to_charpp(const atf_map_t *);
+
+/* Modifiers. */
+atf_error_t atf_map_insert(atf_map_t *, const char *, void *, bool);
+
+/* Macros. */
+#define atf_map_for_each(iter, map) \
+ for (iter = atf_map_begin(map); \
+ !atf_equal_map_iter_map_iter((iter), atf_map_end(map)); \
+ iter = atf_map_iter_next(iter))
+#define atf_map_for_each_c(iter, map) \
+ for (iter = atf_map_begin_c(map); \
+ !atf_equal_map_citer_map_citer((iter), atf_map_end_c(map)); \
+ iter = atf_map_citer_next(iter))
+
+#endif /* !defined(ATF_C_DETAIL_MAP_H) */
diff --git a/unit/atf-src/atf-c/detail/map_test.c b/unit/atf-src/atf-c/detail/map_test.c
new file mode 100644
index 0000000..0982e20
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/map_test.c
@@ -0,0 +1,421 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/map.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+#include "atf-c/utils.h"
+
+/* ---------------------------------------------------------------------
+ * Tests for the "atf_map" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors and destructors.
+ */
+
+ATF_TC(map_init);
+ATF_TC_HEAD(map_init, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_map_init function");
+}
+ATF_TC_BODY(map_init, tc)
+{
+ atf_map_t map;
+
+ RE(atf_map_init(&map));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 0);
+ atf_map_fini(&map);
+}
+
+ATF_TC_WITHOUT_HEAD(map_init_charpp_null);
+ATF_TC_BODY(map_init_charpp_null, tc)
+{
+ atf_map_t map;
+
+ RE(atf_map_init_charpp(&map, NULL));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 0);
+ atf_map_fini(&map);
+}
+
+ATF_TC_WITHOUT_HEAD(map_init_charpp_empty);
+ATF_TC_BODY(map_init_charpp_empty, tc)
+{
+ const char *const array[] = { NULL };
+ atf_map_t map;
+
+ RE(atf_map_init_charpp(&map, array));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 0);
+ atf_map_fini(&map);
+}
+
+ATF_TC_WITHOUT_HEAD(map_init_charpp_some);
+ATF_TC_BODY(map_init_charpp_some, tc)
+{
+ const char *const array[] = { "K1", "V1", "K2", "V2", NULL };
+ atf_map_t map;
+ atf_map_citer_t iter;
+
+ RE(atf_map_init_charpp(&map, array));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 2);
+
+ iter = atf_map_find_c(&map, "K1");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K1") == 0);
+ ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V1") == 0);
+
+ iter = atf_map_find_c(&map, "K2");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K2") == 0);
+ ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V2") == 0);
+
+ atf_map_fini(&map);
+}
+
+ATF_TC_WITHOUT_HEAD(map_init_charpp_short);
+ATF_TC_BODY(map_init_charpp_short, tc)
+{
+ const char *const array[] = { "K1", "V1", "K2", NULL };
+ atf_map_t map;
+
+ atf_error_t err = atf_map_init_charpp(&map, array);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+}
+
+/*
+ * Getters.
+ */
+
+ATF_TC(find);
+ATF_TC_HEAD(find, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_map_find function");
+}
+ATF_TC_BODY(find, tc)
+{
+ atf_map_t map;
+ char val1[] = "V1";
+ char val2[] = "V2";
+ atf_map_iter_t iter;
+
+ RE(atf_map_init(&map));
+ RE(atf_map_insert(&map, "K1", val1, false));
+ RE(atf_map_insert(&map, "K2", val2, false));
+
+ iter = atf_map_find(&map, "K0");
+ ATF_REQUIRE(atf_equal_map_iter_map_iter(iter, atf_map_end(&map)));
+
+ iter = atf_map_find(&map, "K1");
+ ATF_REQUIRE(!atf_equal_map_iter_map_iter(iter, atf_map_end(&map)));
+ ATF_REQUIRE(strcmp(atf_map_iter_key(iter), "K1") == 0);
+ ATF_REQUIRE(strcmp(atf_map_iter_data(iter), "V1") == 0);
+ strcpy(atf_map_iter_data(iter), "Z1");
+
+ iter = atf_map_find(&map, "K1");
+ ATF_REQUIRE(!atf_equal_map_iter_map_iter(iter, atf_map_end(&map)));
+ ATF_REQUIRE(strcmp(atf_map_iter_key(iter), "K1") == 0);
+ ATF_REQUIRE(strcmp(atf_map_iter_data(iter), "Z1") == 0);
+
+ iter = atf_map_find(&map, "K2");
+ ATF_REQUIRE(!atf_equal_map_iter_map_iter(iter, atf_map_end(&map)));
+ ATF_REQUIRE(strcmp(atf_map_iter_key(iter), "K2") == 0);
+ ATF_REQUIRE(strcmp(atf_map_iter_data(iter), "V2") == 0);
+
+ atf_map_fini(&map);
+}
+
+ATF_TC(find_c);
+ATF_TC_HEAD(find_c, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_map_find_c function");
+}
+ATF_TC_BODY(find_c, tc)
+{
+ atf_map_t map;
+ char val1[] = "V1";
+ char val2[] = "V2";
+ atf_map_citer_t iter;
+
+ RE(atf_map_init(&map));
+ RE(atf_map_insert(&map, "K1", val1, false));
+ RE(atf_map_insert(&map, "K2", val2, false));
+
+ iter = atf_map_find_c(&map, "K0");
+ ATF_REQUIRE(atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+
+ iter = atf_map_find_c(&map, "K1");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K1") == 0);
+ ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V1") == 0);
+
+ iter = atf_map_find_c(&map, "K2");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K2") == 0);
+ ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V2") == 0);
+
+ atf_map_fini(&map);
+}
+
+ATF_TC_WITHOUT_HEAD(to_charpp_empty);
+ATF_TC_BODY(to_charpp_empty, tc)
+{
+ atf_map_t map;
+ char **array;
+
+ RE(atf_map_init(&map));
+ ATF_REQUIRE((array = atf_map_to_charpp(&map)) != NULL);
+ atf_map_fini(&map);
+
+ ATF_CHECK_EQ(NULL, array[0]);
+ atf_utils_free_charpp(array);
+}
+
+ATF_TC_WITHOUT_HEAD(to_charpp_some);
+ATF_TC_BODY(to_charpp_some, tc)
+{
+ atf_map_t map;
+ char **array;
+
+ char s1[] = "one";
+ char s2[] = "two";
+ char s3[] = "three";
+
+ RE(atf_map_init(&map));
+ RE(atf_map_insert(&map, "K1", s1, false));
+ RE(atf_map_insert(&map, "K2", s2, false));
+ RE(atf_map_insert(&map, "K3", s3, false));
+ ATF_REQUIRE((array = atf_map_to_charpp(&map)) != NULL);
+ atf_map_fini(&map);
+
+ ATF_CHECK_STREQ("K1", array[0]);
+ ATF_CHECK_STREQ("one", array[1]);
+ ATF_CHECK_STREQ("K2", array[2]);
+ ATF_CHECK_STREQ("two", array[3]);
+ ATF_CHECK_STREQ("K3", array[4]);
+ ATF_CHECK_STREQ("three", array[5]);
+ ATF_CHECK_EQ(NULL, array[6]);
+ atf_utils_free_charpp(array);
+}
+
+/*
+ * Modifiers.
+ */
+
+ATF_TC(map_insert);
+ATF_TC_HEAD(map_insert, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_map_insert function");
+}
+ATF_TC_BODY(map_insert, tc)
+{
+ atf_map_t map;
+ char buf[] = "1st test string";
+ char buf2[] = "2nd test string";
+ const char *ptr;
+ atf_map_citer_t iter;
+
+ RE(atf_map_init(&map));
+
+ printf("Inserting some values\n");
+ ATF_REQUIRE_EQ(atf_map_size(&map), 0);
+ RE(atf_map_insert(&map, "K1", buf, false));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 1);
+ RE(atf_map_insert(&map, "K2", buf, false));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 2);
+ RE(atf_map_insert(&map, "K3", buf, false));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 3);
+
+ printf("Replacing a value\n");
+ iter = atf_map_find_c(&map, "K3");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ ptr = atf_map_citer_data(iter);
+ ATF_REQUIRE_EQ(ptr, buf);
+ RE(atf_map_insert(&map, "K3", buf2, false));
+ ATF_REQUIRE_EQ(atf_map_size(&map), 3);
+ iter = atf_map_find_c(&map, "K3");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ ptr = atf_map_citer_data(iter);
+ ATF_REQUIRE_EQ(ptr, buf2);
+
+ atf_map_fini(&map);
+}
+
+/*
+ * Macros.
+ */
+
+ATF_TC(map_for_each);
+ATF_TC_HEAD(map_for_each, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_map_for_each macro");
+}
+ATF_TC_BODY(map_for_each, tc)
+{
+ atf_map_t map;
+ atf_map_iter_t iter;
+ size_t count, i, size;
+ char keys[10][5];
+ int nums[10];
+
+ printf("Iterating over empty map\n");
+ RE(atf_map_init(&map));
+ count = 0;
+ atf_map_for_each(iter, &map) {
+ count++;
+ printf("Item count is now %zd\n", count);
+ }
+ ATF_REQUIRE_EQ(count, 0);
+ atf_map_fini(&map);
+
+ for (size = 0; size <= 10; size++) {
+ printf("Iterating over map of %zd elements\n", size);
+ RE(atf_map_init(&map));
+ for (i = 0; i < size; i++) {
+ nums[i] = i + 1;
+ snprintf(keys[i], sizeof(keys[i]), "%d", nums[i]);
+ RE(atf_map_insert(&map, keys[i], &nums[i], false));
+ }
+ count = 0;
+ atf_map_for_each(iter, &map) {
+ printf("Retrieved item: %d\n", *(int *)atf_map_iter_data(iter));
+ count++;
+ }
+ ATF_REQUIRE_EQ(count, size);
+ atf_map_fini(&map);
+ }
+}
+
+ATF_TC(map_for_each_c);
+ATF_TC_HEAD(map_for_each_c, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_map_for_each_c macro");
+}
+ATF_TC_BODY(map_for_each_c, tc)
+{
+ atf_map_t map;
+ atf_map_citer_t iter;
+ size_t count, i, size;
+ char keys[10][5];
+ int nums[10];
+
+ printf("Iterating over empty map\n");
+ RE(atf_map_init(&map));
+ count = 0;
+ atf_map_for_each_c(iter, &map) {
+ count++;
+ printf("Item count is now %zd\n", count);
+ }
+ ATF_REQUIRE_EQ(count, 0);
+ atf_map_fini(&map);
+
+ for (size = 0; size <= 10; size++) {
+ printf("Iterating over map of %zd elements\n", size);
+ RE(atf_map_init(&map));
+ for (i = 0; i < size; i++) {
+ nums[i] = i + 1;
+ snprintf(keys[i], sizeof(keys[i]), "%d", nums[i]);
+ RE(atf_map_insert(&map, keys[i], &nums[i], false));
+ }
+ count = 0;
+ atf_map_for_each_c(iter, &map) {
+ printf("Retrieved item: %d\n",
+ *(const int *)atf_map_citer_data(iter));
+ count++;
+ }
+ ATF_REQUIRE_EQ(count, size);
+ atf_map_fini(&map);
+ }
+}
+
+/*
+ * Other.
+ */
+
+ATF_TC(stable_keys);
+ATF_TC_HEAD(stable_keys, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that the keys do not change "
+ "even if their original values do");
+}
+ATF_TC_BODY(stable_keys, tc)
+{
+ atf_map_t map;
+ atf_map_citer_t iter;
+ char key[] = "K1";
+
+ RE(atf_map_init(&map));
+
+ RE(atf_map_insert(&map, key, strdup("test-value"), true));
+ iter = atf_map_find_c(&map, "K1");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ iter = atf_map_find_c(&map, "K2");
+ ATF_REQUIRE(atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+
+ strcpy(key, "K2");
+ iter = atf_map_find_c(&map, "K1");
+ ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+ iter = atf_map_find_c(&map, "K2");
+ ATF_REQUIRE(atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map)));
+
+ atf_map_fini(&map);
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Constructors and destructors. */
+ ATF_TP_ADD_TC(tp, map_init);
+ ATF_TP_ADD_TC(tp, map_init_charpp_null);
+ ATF_TP_ADD_TC(tp, map_init_charpp_empty);
+ ATF_TP_ADD_TC(tp, map_init_charpp_some);
+ ATF_TP_ADD_TC(tp, map_init_charpp_short);
+
+ /* Getters. */
+ ATF_TP_ADD_TC(tp, find);
+ ATF_TP_ADD_TC(tp, find_c);
+ ATF_TP_ADD_TC(tp, to_charpp_empty);
+ ATF_TP_ADD_TC(tp, to_charpp_some);
+
+ /* Modifiers. */
+ ATF_TP_ADD_TC(tp, map_insert);
+
+ /* Macros. */
+ ATF_TP_ADD_TC(tp, map_for_each);
+ ATF_TP_ADD_TC(tp, map_for_each_c);
+
+ /* Other. */
+ ATF_TP_ADD_TC(tp, stable_keys);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/process.c b/unit/atf-src/atf-c/detail/process.c
new file mode 100644
index 0000000..8e08b6c
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/process.c
@@ -0,0 +1,670 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/process.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atf-c/defs.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/error.h"
+
+/* This prototype is not in the header file because this is a private
+ * function; however, we need to access it during testing. */
+atf_error_t atf_process_status_init(atf_process_status_t *, int);
+
+/* ---------------------------------------------------------------------
+ * The "stream_prepare" auxiliary type.
+ * --------------------------------------------------------------------- */
+
+struct stream_prepare {
+ const atf_process_stream_t *m_sb;
+
+ bool m_pipefds_ok;
+ int m_pipefds[2];
+};
+typedef struct stream_prepare stream_prepare_t;
+
+static
+atf_error_t
+stream_prepare_init(stream_prepare_t *sp, const atf_process_stream_t *sb)
+{
+ atf_error_t err;
+
+ const int type = atf_process_stream_type(sb);
+
+ sp->m_sb = sb;
+ sp->m_pipefds_ok = false;
+
+ if (type == atf_process_stream_type_capture) {
+ if (pipe(sp->m_pipefds) == -1)
+ err = atf_libc_error(errno, "Failed to create pipe");
+ else {
+ err = atf_no_error();
+ sp->m_pipefds_ok = true;
+ }
+ } else
+ err = atf_no_error();
+
+ return err;
+}
+
+static
+void
+stream_prepare_fini(stream_prepare_t *sp)
+{
+ if (sp->m_pipefds_ok) {
+ close(sp->m_pipefds[0]);
+ close(sp->m_pipefds[1]);
+ }
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_stream" type.
+ * --------------------------------------------------------------------- */
+
+const int atf_process_stream_type_capture = 1;
+const int atf_process_stream_type_connect = 2;
+const int atf_process_stream_type_inherit = 3;
+const int atf_process_stream_type_redirect_fd = 4;
+const int atf_process_stream_type_redirect_path = 5;
+
+static
+bool
+stream_is_valid(const atf_process_stream_t *sb)
+{
+ return (sb->m_type == atf_process_stream_type_capture) ||
+ (sb->m_type == atf_process_stream_type_connect) ||
+ (sb->m_type == atf_process_stream_type_inherit) ||
+ (sb->m_type == atf_process_stream_type_redirect_fd) ||
+ (sb->m_type == atf_process_stream_type_redirect_path);
+}
+
+atf_error_t
+atf_process_stream_init_capture(atf_process_stream_t *sb)
+{
+ sb->m_type = atf_process_stream_type_capture;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_connect(atf_process_stream_t *sb,
+ const int src_fd, const int tgt_fd)
+{
+ PRE(src_fd >= 0);
+ PRE(tgt_fd >= 0);
+ PRE(src_fd != tgt_fd);
+
+ sb->m_type = atf_process_stream_type_connect;
+ sb->m_src_fd = src_fd;
+ sb->m_tgt_fd = tgt_fd;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_inherit(atf_process_stream_t *sb)
+{
+ sb->m_type = atf_process_stream_type_inherit;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_redirect_fd(atf_process_stream_t *sb,
+ const int fd)
+{
+ sb->m_type = atf_process_stream_type_redirect_fd;
+ sb->m_fd = fd;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+atf_error_t
+atf_process_stream_init_redirect_path(atf_process_stream_t *sb,
+ const atf_fs_path_t *path)
+{
+ sb->m_type = atf_process_stream_type_redirect_path;
+ sb->m_path = path;
+
+ POST(stream_is_valid(sb));
+ return atf_no_error();
+}
+
+void
+atf_process_stream_fini(atf_process_stream_t *sb)
+{
+ PRE(stream_is_valid(sb));
+}
+
+int
+atf_process_stream_type(const atf_process_stream_t *sb)
+{
+ PRE(stream_is_valid(sb));
+
+ return sb->m_type;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_status" type.
+ * --------------------------------------------------------------------- */
+
+atf_error_t
+atf_process_status_init(atf_process_status_t *s, int status)
+{
+ s->m_status = status;
+
+ return atf_no_error();
+}
+
+void
+atf_process_status_fini(atf_process_status_t *s ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+}
+
+bool
+atf_process_status_exited(const atf_process_status_t *s)
+{
+ int mutable_status = s->m_status;
+ return WIFEXITED(mutable_status);
+}
+
+int
+atf_process_status_exitstatus(const atf_process_status_t *s)
+{
+ PRE(atf_process_status_exited(s));
+ int mutable_status = s->m_status;
+ return WEXITSTATUS(mutable_status);
+}
+
+bool
+atf_process_status_signaled(const atf_process_status_t *s)
+{
+ int mutable_status = s->m_status;
+ return WIFSIGNALED(mutable_status);
+}
+
+int
+atf_process_status_termsig(const atf_process_status_t *s)
+{
+ PRE(atf_process_status_signaled(s));
+ int mutable_status = s->m_status;
+ return WTERMSIG(mutable_status);
+}
+
+bool
+atf_process_status_coredump(const atf_process_status_t *s)
+{
+ PRE(atf_process_status_signaled(s));
+#if defined(WCOREDUMP)
+ int mutable_status = s->m_status;
+ return WCOREDUMP(mutable_status);
+#else
+ return false;
+#endif
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_child" type.
+ * --------------------------------------------------------------------- */
+
+static
+atf_error_t
+atf_process_child_init(atf_process_child_t *c)
+{
+ c->m_pid = 0;
+ c->m_stdout = -1;
+ c->m_stderr = -1;
+
+ return atf_no_error();
+}
+
+static
+void
+atf_process_child_fini(atf_process_child_t *c)
+{
+ if (c->m_stdout != -1)
+ close(c->m_stdout);
+ if (c->m_stderr != -1)
+ close(c->m_stderr);
+}
+
+atf_error_t
+atf_process_child_wait(atf_process_child_t *c, atf_process_status_t *s)
+{
+ atf_error_t err;
+ int status;
+
+ if (waitpid(c->m_pid, &status, 0) == -1)
+ err = atf_libc_error(errno, "Failed waiting for process %d",
+ c->m_pid);
+ else {
+ atf_process_child_fini(c);
+ err = atf_process_status_init(s, status);
+ }
+
+ return err;
+}
+
+pid_t
+atf_process_child_pid(const atf_process_child_t *c)
+{
+ return c->m_pid;
+}
+
+int
+atf_process_child_stdout(atf_process_child_t *c)
+{
+ PRE(c->m_stdout != -1);
+ return c->m_stdout;
+}
+
+int
+atf_process_child_stderr(atf_process_child_t *c)
+{
+ PRE(c->m_stderr != -1);
+ return c->m_stderr;
+}
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+static
+atf_error_t
+safe_dup(const int oldfd, const int newfd)
+{
+ atf_error_t err;
+
+ if (oldfd != newfd) {
+ if (dup2(oldfd, newfd) == -1) {
+ err = atf_libc_error(errno, "Could not allocate file descriptor");
+ } else {
+ close(oldfd);
+ err = atf_no_error();
+ }
+ } else
+ err = atf_no_error();
+
+ return err;
+}
+
+static
+atf_error_t
+child_connect(const stream_prepare_t *sp, int procfd)
+{
+ atf_error_t err;
+ const int type = atf_process_stream_type(sp->m_sb);
+
+ if (type == atf_process_stream_type_capture) {
+ close(sp->m_pipefds[0]);
+ err = safe_dup(sp->m_pipefds[1], procfd);
+ } else if (type == atf_process_stream_type_connect) {
+ if (dup2(sp->m_sb->m_tgt_fd, sp->m_sb->m_src_fd) == -1)
+ err = atf_libc_error(errno, "Cannot connect descriptor %d to %d",
+ sp->m_sb->m_tgt_fd, sp->m_sb->m_src_fd);
+ else
+ err = atf_no_error();
+ } else if (type == atf_process_stream_type_inherit) {
+ err = atf_no_error();
+ } else if (type == atf_process_stream_type_redirect_fd) {
+ err = safe_dup(sp->m_sb->m_fd, procfd);
+ } else if (type == atf_process_stream_type_redirect_path) {
+ int aux = open(atf_fs_path_cstring(sp->m_sb->m_path),
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (aux == -1)
+ err = atf_libc_error(errno, "Could not create %s",
+ atf_fs_path_cstring(sp->m_sb->m_path));
+ else {
+ err = safe_dup(aux, procfd);
+ if (atf_is_error(err))
+ close(aux);
+ }
+ } else {
+ UNREACHABLE;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+static
+void
+parent_connect(const stream_prepare_t *sp, int *fd)
+{
+ const int type = atf_process_stream_type(sp->m_sb);
+
+ if (type == atf_process_stream_type_capture) {
+ close(sp->m_pipefds[1]);
+ *fd = sp->m_pipefds[0];
+ } else if (type == atf_process_stream_type_connect) {
+ /* Do nothing. */
+ } else if (type == atf_process_stream_type_inherit) {
+ /* Do nothing. */
+ } else if (type == atf_process_stream_type_redirect_fd) {
+ /* Do nothing. */
+ } else if (type == atf_process_stream_type_redirect_path) {
+ /* Do nothing. */
+ } else {
+ UNREACHABLE;
+ }
+}
+
+static
+atf_error_t
+do_parent(atf_process_child_t *c,
+ const pid_t pid,
+ const stream_prepare_t *outsp,
+ const stream_prepare_t *errsp)
+{
+ atf_error_t err;
+
+ err = atf_process_child_init(c);
+ if (atf_is_error(err))
+ goto out;
+
+ c->m_pid = pid;
+
+ parent_connect(outsp, &c->m_stdout);
+ parent_connect(errsp, &c->m_stderr);
+
+out:
+ return err;
+}
+
+static
+void
+do_child(void (*)(void *),
+ void *,
+ const stream_prepare_t *,
+ const stream_prepare_t *) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+static
+void
+do_child(void (*start)(void *),
+ void *v,
+ const stream_prepare_t *outsp,
+ const stream_prepare_t *errsp)
+{
+ atf_error_t err;
+
+ err = child_connect(outsp, STDOUT_FILENO);
+ if (atf_is_error(err))
+ goto out;
+
+ err = child_connect(errsp, STDERR_FILENO);
+ if (atf_is_error(err))
+ goto out;
+
+ start(v);
+ UNREACHABLE;
+
+out:
+ if (atf_is_error(err)) {
+ char buf[1024];
+
+ atf_error_format(err, buf, sizeof(buf));
+ fprintf(stderr, "Unhandled error: %s\n", buf);
+ atf_error_free(err);
+
+ exit(EXIT_FAILURE);
+ } else
+ exit(EXIT_SUCCESS);
+}
+
+static
+atf_error_t
+fork_with_streams(atf_process_child_t *c,
+ void (*start)(void *),
+ const atf_process_stream_t *outsb,
+ const atf_process_stream_t *errsb,
+ void *v)
+{
+ atf_error_t err;
+ stream_prepare_t outsp;
+ stream_prepare_t errsp;
+ pid_t pid;
+
+ err = stream_prepare_init(&outsp, outsb);
+ if (atf_is_error(err))
+ goto out;
+
+ err = stream_prepare_init(&errsp, errsb);
+ if (atf_is_error(err))
+ goto err_outpipe;
+
+ pid = fork();
+ if (pid == -1) {
+ err = atf_libc_error(errno, "Failed to fork");
+ goto err_errpipe;
+ }
+
+ if (pid == 0) {
+ do_child(start, v, &outsp, &errsp);
+ UNREACHABLE;
+ abort();
+ err = atf_no_error();
+ } else {
+ err = do_parent(c, pid, &outsp, &errsp);
+ if (atf_is_error(err))
+ goto err_errpipe;
+ }
+
+ goto out;
+
+err_errpipe:
+ stream_prepare_fini(&errsp);
+err_outpipe:
+ stream_prepare_fini(&outsp);
+
+out:
+ return err;
+}
+
+static
+atf_error_t
+init_stream_w_default(const atf_process_stream_t *usersb,
+ atf_process_stream_t *inheritsb,
+ const atf_process_stream_t **realsb)
+{
+ atf_error_t err;
+
+ if (usersb == NULL) {
+ err = atf_process_stream_init_inherit(inheritsb);
+ if (!atf_is_error(err))
+ *realsb = inheritsb;
+ } else {
+ err = atf_no_error();
+ *realsb = usersb;
+ }
+
+ return err;
+}
+
+atf_error_t
+atf_process_fork(atf_process_child_t *c,
+ void (*start)(void *),
+ const atf_process_stream_t *outsb,
+ const atf_process_stream_t *errsb,
+ void *v)
+{
+ atf_error_t err;
+ atf_process_stream_t inherit_outsb, inherit_errsb;
+ const atf_process_stream_t *real_outsb, *real_errsb;
+
+ real_outsb = NULL; /* Shut up GCC warning. */
+ err = init_stream_w_default(outsb, &inherit_outsb, &real_outsb);
+ if (atf_is_error(err))
+ goto out;
+
+ real_errsb = NULL; /* Shut up GCC warning. */
+ err = init_stream_w_default(errsb, &inherit_errsb, &real_errsb);
+ if (atf_is_error(err))
+ goto out_out;
+
+ err = fork_with_streams(c, start, real_outsb, real_errsb, v);
+
+ if (errsb == NULL)
+ atf_process_stream_fini(&inherit_errsb);
+out_out:
+ if (outsb == NULL)
+ atf_process_stream_fini(&inherit_outsb);
+out:
+ return err;
+}
+
+static
+int
+const_execvp(const char *file, const char *const *argv)
+{
+#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+ return execvp(file, UNCONST(argv));
+#undef UNCONST
+}
+
+static
+atf_error_t
+list_to_array(const atf_list_t *l, const char ***ap)
+{
+ atf_error_t err;
+ const char **a;
+
+ a = (const char **)malloc((atf_list_size(l) + 1) * sizeof(const char *));
+ if (a == NULL)
+ err = atf_no_memory_error();
+ else {
+ const char **aiter;
+ atf_list_citer_t liter;
+
+ aiter = a;
+ atf_list_for_each_c(liter, l) {
+ *aiter = (const char *)atf_list_citer_data(liter);
+ aiter++;
+ }
+ *aiter = NULL;
+
+ err = atf_no_error();
+ *ap = a;
+ }
+
+ return err;
+}
+
+struct exec_args {
+ const atf_fs_path_t *m_prog;
+ const char *const *m_argv;
+ void (*m_prehook)(void);
+};
+
+static
+void
+do_exec(void *v)
+{
+ struct exec_args *ea = v;
+
+ if (ea->m_prehook != NULL)
+ ea->m_prehook();
+
+ const int ret = const_execvp(atf_fs_path_cstring(ea->m_prog), ea->m_argv);
+ const int errnocopy = errno;
+ INV(ret == -1);
+ fprintf(stderr, "exec(%s) failed: %s\n",
+ atf_fs_path_cstring(ea->m_prog), strerror(errnocopy));
+ exit(EXIT_FAILURE);
+}
+
+atf_error_t
+atf_process_exec_array(atf_process_status_t *s,
+ const atf_fs_path_t *prog,
+ const char *const *argv,
+ const atf_process_stream_t *outsb,
+ const atf_process_stream_t *errsb,
+ void (*prehook)(void))
+{
+ atf_error_t err;
+ atf_process_child_t c;
+ struct exec_args ea = { prog, argv, prehook };
+
+ PRE(outsb == NULL ||
+ atf_process_stream_type(outsb) != atf_process_stream_type_capture);
+ PRE(errsb == NULL ||
+ atf_process_stream_type(errsb) != atf_process_stream_type_capture);
+
+ err = atf_process_fork(&c, do_exec, outsb, errsb, &ea);
+ if (atf_is_error(err))
+ goto out;
+
+again:
+ err = atf_process_child_wait(&c, s);
+ if (atf_is_error(err)) {
+ INV(atf_error_is(err, "libc") && atf_libc_error_code(err) == EINTR);
+ atf_error_free(err);
+ goto again;
+ }
+
+out:
+ return err;
+}
+
+atf_error_t
+atf_process_exec_list(atf_process_status_t *s,
+ const atf_fs_path_t *prog,
+ const atf_list_t *argv,
+ const atf_process_stream_t *outsb,
+ const atf_process_stream_t *errsb,
+ void (*prehook)(void))
+{
+ atf_error_t err;
+ const char **argv2;
+
+ PRE(outsb == NULL ||
+ atf_process_stream_type(outsb) != atf_process_stream_type_capture);
+ PRE(errsb == NULL ||
+ atf_process_stream_type(errsb) != atf_process_stream_type_capture);
+
+ argv2 = NULL; /* Silence GCC warning. */
+ err = list_to_array(argv, &argv2);
+ if (atf_is_error(err))
+ goto out;
+
+ err = atf_process_exec_array(s, prog, argv2, outsb, errsb, prehook);
+
+ free(argv2);
+out:
+ return err;
+}
diff --git a/unit/atf-src/atf-c/detail/process.h b/unit/atf-src/atf-c/detail/process.h
new file mode 100644
index 0000000..81158d0
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/process.h
@@ -0,0 +1,131 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_DETAIL_PROCESS_H)
+#define ATF_C_DETAIL_PROCESS_H
+
+#include <sys/types.h>
+
+#include <stdbool.h>
+
+#include <atf-c/detail/fs.h>
+#include <atf-c/detail/list.h>
+#include <atf-c/error_fwd.h>
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_stream" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_process_stream {
+ int m_type;
+
+ /* Valid if m_type == connect. */
+ int m_src_fd;
+ int m_tgt_fd;
+
+ /* Valid if m_type == redirect_fd. */
+ int m_fd;
+
+ /* Valid if m_type == redirect_path. */
+ const atf_fs_path_t *m_path;
+};
+typedef struct atf_process_stream atf_process_stream_t;
+
+extern const int atf_process_stream_type_capture;
+extern const int atf_process_stream_type_connect;
+extern const int atf_process_stream_type_inherit;
+extern const int atf_process_stream_type_redirect_fd;
+extern const int atf_process_stream_type_redirect_path;
+
+atf_error_t atf_process_stream_init_capture(atf_process_stream_t *);
+atf_error_t atf_process_stream_init_connect(atf_process_stream_t *,
+ const int, const int);
+atf_error_t atf_process_stream_init_inherit(atf_process_stream_t *);
+atf_error_t atf_process_stream_init_redirect_fd(atf_process_stream_t *,
+ const int fd);
+atf_error_t atf_process_stream_init_redirect_path(atf_process_stream_t *,
+ const atf_fs_path_t *);
+void atf_process_stream_fini(atf_process_stream_t *);
+
+int atf_process_stream_type(const atf_process_stream_t *);
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_status" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_process_status {
+ int m_status;
+};
+typedef struct atf_process_status atf_process_status_t;
+
+void atf_process_status_fini(atf_process_status_t *);
+
+bool atf_process_status_exited(const atf_process_status_t *);
+int atf_process_status_exitstatus(const atf_process_status_t *);
+bool atf_process_status_signaled(const atf_process_status_t *);
+int atf_process_status_termsig(const atf_process_status_t *);
+bool atf_process_status_coredump(const atf_process_status_t *);
+
+/* ---------------------------------------------------------------------
+ * The "atf_process_child" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_process_child {
+ pid_t m_pid;
+
+ int m_stdout;
+ int m_stderr;
+};
+typedef struct atf_process_child atf_process_child_t;
+
+atf_error_t atf_process_child_wait(atf_process_child_t *,
+ atf_process_status_t *);
+pid_t atf_process_child_pid(const atf_process_child_t *);
+int atf_process_child_stdout(atf_process_child_t *);
+int atf_process_child_stderr(atf_process_child_t *);
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+atf_error_t atf_process_fork(atf_process_child_t *,
+ void (*)(void *),
+ const atf_process_stream_t *,
+ const atf_process_stream_t *,
+ void *);
+atf_error_t atf_process_exec_array(atf_process_status_t *,
+ const atf_fs_path_t *,
+ const char *const *,
+ const atf_process_stream_t *,
+ const atf_process_stream_t *,
+ void (*)(void));
+atf_error_t atf_process_exec_list(atf_process_status_t *,
+ const atf_fs_path_t *,
+ const atf_list_t *,
+ const atf_process_stream_t *,
+ const atf_process_stream_t *,
+ void (*)(void));
+
+#endif /* !defined(ATF_C_DETAIL_PROCESS_H) */
diff --git a/unit/atf-src/atf-c/detail/process_helpers.c b/unit/atf-src/atf-c/detail/process_helpers.c
new file mode 100644
index 0000000..47b90bc
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/process_helpers.c
@@ -0,0 +1,113 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <sys/types.h>
+
+#include <assert.h> /* NO_CHECK_STYLE */
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static
+int
+h_echo(const char *msg)
+{
+ printf("%s\n", msg);
+ return EXIT_SUCCESS;
+}
+
+static
+int
+h_exit_failure(void)
+{
+ return EXIT_FAILURE;
+}
+
+static
+int
+h_exit_signal(void)
+{
+ kill(getpid(), SIGKILL);
+ assert(0); /* NO_CHECK_STYLE */
+ return EXIT_FAILURE;
+}
+
+static
+int
+h_exit_success(void)
+{
+ return EXIT_SUCCESS;
+}
+
+static
+int
+h_stdout_stderr(const char *id)
+{
+ fprintf(stdout, "Line 1 to stdout for %s\n", id);
+ fprintf(stdout, "Line 2 to stdout for %s\n", id);
+ fprintf(stderr, "Line 1 to stderr for %s\n", id);
+ fprintf(stderr, "Line 2 to stderr for %s\n", id);
+
+ return EXIT_SUCCESS;
+}
+
+static
+void
+check_args(const int argc, const char *const argv[], const int required)
+{
+ if (argc < required) {
+ fprintf(stderr, "Usage: %s helper-name [args]\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+}
+
+int
+main(int argc, const char *const argv[])
+{
+ int exitcode;
+
+ check_args(argc, argv, 2);
+
+ if (strcmp(argv[1], "echo") == 0) {
+ check_args(argc, argv, 3);
+ exitcode = h_echo(argv[2]);
+ } else if (strcmp(argv[1], "exit-failure") == 0)
+ exitcode = h_exit_failure();
+ else if (strcmp(argv[1], "exit-signal") == 0)
+ exitcode = h_exit_signal();
+ else if (strcmp(argv[1], "exit-success") == 0)
+ exitcode = h_exit_success();
+ else if (strcmp(argv[1], "stdout-stderr") == 0) {
+ check_args(argc, argv, 3);
+ exitcode = h_stdout_stderr(argv[2]);
+ } else {
+ fprintf(stderr, "%s: Unknown helper %s\n", argv[0], argv[1]);
+ exitcode = EXIT_FAILURE;
+ }
+
+ return exitcode;
+}
diff --git a/unit/atf-src/atf-c/detail/process_test.c b/unit/atf-src/atf-c/detail/process_test.c
new file mode 100644
index 0000000..5ae5565
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/process_test.c
@@ -0,0 +1,1159 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/process.h"
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/defs.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/detail/test_helpers.h"
+
+atf_error_t atf_process_status_init(atf_process_status_t *, int);
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions for testing of 'atf_process_fork'.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Testing of atf_process_fork is quite messy. We want to be able to test
+ * all the possible combinations of stdout and stderr behavior to ensure
+ * that the streams are manipulated correctly.
+ *
+ * To do this, the do_fork function is a wrapper for atf_process_fork that
+ * issues stream-specific hooks before fork, while the child is running and
+ * after the child terminates. We then provide test cases that just call
+ * do_fork with different hooks.
+ *
+ * The hooks are described by base_stream, and we then have one *_stream
+ * type for ever possible stream behavior.
+ */
+
+enum out_type { stdout_type, stderr_type };
+
+struct base_stream {
+ void (*init)(void *);
+ void (*process)(void *, atf_process_child_t *);
+ void (*fini)(void *);
+
+ /* m_sb is initialized by subclasses that need it, but all consumers
+ * must use m_sb_ptr, which may or may not point to m_sb. This allows
+ * us to test the interface with a NULL value, which triggers a
+ * default behavior. */
+ atf_process_stream_t m_sb;
+ atf_process_stream_t *m_sb_ptr;
+ enum out_type m_type;
+};
+#define BASE_STREAM(ihook, phook, fhook, type) \
+ { .init = ihook, \
+ .process = phook, \
+ .fini = fhook, \
+ .m_type = type }
+
+static
+void
+check_file(const enum out_type type)
+{
+ switch (type) {
+ case stdout_type:
+ ATF_CHECK(atf_utils_grep_file("stdout: msg", "stdout"));
+ ATF_CHECK(!atf_utils_grep_file("stderr: msg", "stdout"));
+ break;
+ case stderr_type:
+ ATF_CHECK(atf_utils_grep_file("stderr: msg", "stderr"));
+ ATF_CHECK(!atf_utils_grep_file("stdout: msg", "stderr"));
+ break;
+ default:
+ UNREACHABLE;
+ }
+}
+
+struct capture_stream {
+ struct base_stream m_base;
+
+ char *m_msg;
+};
+#define CAPTURE_STREAM(type) \
+ { .m_base = BASE_STREAM(capture_stream_init, \
+ capture_stream_process, \
+ capture_stream_fini, \
+ type) }
+
+static
+void
+capture_stream_init(void *v)
+{
+ struct capture_stream *s = v;
+
+ s->m_base.m_sb_ptr = &s->m_base.m_sb;
+ RE(atf_process_stream_init_capture(&s->m_base.m_sb));
+ s->m_msg = NULL;
+}
+
+static
+void
+capture_stream_process(void *v, atf_process_child_t *c)
+{
+ struct capture_stream *s = v;
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ s->m_msg = atf_utils_readline(atf_process_child_stdout(c));
+ break;
+ case stderr_type:
+ s->m_msg = atf_utils_readline(atf_process_child_stderr(c));
+ break;
+ default:
+ UNREACHABLE;
+ }
+}
+
+static
+void
+capture_stream_fini(void *v)
+{
+ struct capture_stream *s = v;
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ ATF_CHECK(atf_utils_grep_string("stdout: msg", s->m_msg));
+ ATF_CHECK(!atf_utils_grep_string("stderr: msg", s->m_msg));
+ break;
+ case stderr_type:
+ ATF_CHECK(!atf_utils_grep_string("stdout: msg", s->m_msg));
+ ATF_CHECK(atf_utils_grep_string("stderr: msg", s->m_msg));
+ break;
+ default:
+ UNREACHABLE;
+ }
+
+ free(s->m_msg);
+ atf_process_stream_fini(&s->m_base.m_sb);
+}
+
+struct connect_stream {
+ struct base_stream m_base;
+
+ int m_fd;
+};
+#define CONNECT_STREAM(type) \
+ { .m_base = BASE_STREAM(connect_stream_init, \
+ NULL, \
+ connect_stream_fini, \
+ type) }
+
+static
+void
+connect_stream_init(void *v)
+{
+ struct connect_stream *s = v;
+ int src_fd;
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ src_fd = STDOUT_FILENO;
+ s->m_fd = open("stdout", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ break;
+ case stderr_type:
+ src_fd = STDERR_FILENO;
+ s->m_fd = open("stderr", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ break;
+ default:
+ UNREACHABLE;
+ src_fd = -1;
+ }
+ ATF_REQUIRE(s->m_fd != -1);
+
+ s->m_base.m_sb_ptr = &s->m_base.m_sb;
+ RE(atf_process_stream_init_connect(&s->m_base.m_sb, src_fd, s->m_fd));
+}
+
+static
+void
+connect_stream_fini(void *v)
+{
+ struct connect_stream *s = v;
+
+ ATF_REQUIRE(close(s->m_fd) != -1);
+
+ atf_process_stream_fini(&s->m_base.m_sb);
+
+ check_file(s->m_base.m_type);
+}
+
+struct inherit_stream {
+ struct base_stream m_base;
+ int m_fd;
+
+ int m_old_fd;
+};
+#define INHERIT_STREAM(type) \
+ { .m_base = BASE_STREAM(inherit_stream_init, \
+ NULL, \
+ inherit_stream_fini, \
+ type) }
+
+static
+void
+inherit_stream_init(void *v)
+{
+ struct inherit_stream *s = v;
+ const char *name;
+
+ s->m_base.m_sb_ptr = &s->m_base.m_sb;
+ RE(atf_process_stream_init_inherit(&s->m_base.m_sb));
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ s->m_fd = STDOUT_FILENO;
+ name = "stdout";
+ break;
+ case stderr_type:
+ s->m_fd = STDERR_FILENO;
+ name = "stderr";
+ break;
+ default:
+ UNREACHABLE;
+ name = NULL;
+ }
+
+ s->m_old_fd = dup(s->m_fd);
+ ATF_REQUIRE(s->m_old_fd != -1);
+ ATF_REQUIRE(close(s->m_fd) != -1);
+ ATF_REQUIRE_EQ(open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644),
+ s->m_fd);
+}
+
+static
+void
+inherit_stream_fini(void *v)
+{
+ struct inherit_stream *s = v;
+
+ ATF_REQUIRE(dup2(s->m_old_fd, s->m_fd) != -1);
+ ATF_REQUIRE(close(s->m_old_fd) != -1);
+
+ atf_process_stream_fini(&s->m_base.m_sb);
+
+ check_file(s->m_base.m_type);
+}
+
+#define default_stream inherit_stream
+#define DEFAULT_STREAM(type) \
+ { .m_base = BASE_STREAM(default_stream_init, \
+ NULL, \
+ default_stream_fini, \
+ type) }
+
+static
+void
+default_stream_init(void *v)
+{
+ struct inherit_stream *s = v;
+
+ inherit_stream_init(v);
+ s->m_base.m_sb_ptr = NULL;
+}
+
+static
+void
+default_stream_fini(void *v)
+{
+ inherit_stream_fini(v);
+}
+
+struct redirect_fd_stream {
+ struct base_stream m_base;
+
+ int m_fd;
+};
+#define REDIRECT_FD_STREAM(type) \
+ { .m_base = BASE_STREAM(redirect_fd_stream_init, \
+ NULL, \
+ redirect_fd_stream_fini, \
+ type) }
+
+static
+void
+redirect_fd_stream_init(void *v)
+{
+ struct redirect_fd_stream *s = v;
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ s->m_fd = open("stdout", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ break;
+ case stderr_type:
+ s->m_fd = open("stderr", O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ break;
+ default:
+ UNREACHABLE;
+ }
+ ATF_REQUIRE(s->m_fd != -1);
+
+ s->m_base.m_sb_ptr = &s->m_base.m_sb;
+ RE(atf_process_stream_init_redirect_fd(&s->m_base.m_sb, s->m_fd));
+}
+
+static
+void
+redirect_fd_stream_fini(void *v)
+{
+ struct redirect_fd_stream *s = v;
+
+ ATF_REQUIRE(close(s->m_fd) != -1);
+
+ atf_process_stream_fini(&s->m_base.m_sb);
+
+ check_file(s->m_base.m_type);
+}
+
+struct redirect_path_stream {
+ struct base_stream m_base;
+
+ atf_fs_path_t m_path;
+};
+#define REDIRECT_PATH_STREAM(type) \
+ { .m_base = BASE_STREAM(redirect_path_stream_init, \
+ NULL, \
+ redirect_path_stream_fini, \
+ type) }
+
+static
+void
+redirect_path_stream_init(void *v)
+{
+ struct redirect_path_stream *s = v;
+
+ switch (s->m_base.m_type) {
+ case stdout_type:
+ RE(atf_fs_path_init_fmt(&s->m_path, "stdout"));
+ break;
+ case stderr_type:
+ RE(atf_fs_path_init_fmt(&s->m_path, "stderr"));
+ break;
+ default:
+ UNREACHABLE;
+ }
+
+ s->m_base.m_sb_ptr = &s->m_base.m_sb;
+ RE(atf_process_stream_init_redirect_path(&s->m_base.m_sb, &s->m_path));
+}
+
+static
+void
+redirect_path_stream_fini(void *v)
+{
+ struct redirect_path_stream *s = v;
+
+ atf_process_stream_fini(&s->m_base.m_sb);
+
+ atf_fs_path_fini(&s->m_path);
+
+ check_file(s->m_base.m_type);
+}
+
+static void child_print(void *) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+struct child_print_data {
+ const char *m_msg;
+};
+
+static
+void
+child_print(void *v)
+{
+ struct child_print_data *cpd = v;
+
+ fprintf(stdout, "stdout: %s\n", cpd->m_msg);
+ fprintf(stderr, "stderr: %s\n", cpd->m_msg);
+
+ exit(EXIT_SUCCESS);
+}
+
+static
+void
+do_fork(const struct base_stream *outfs, void *out,
+ const struct base_stream *errfs, void *err)
+{
+ atf_process_child_t child;
+ atf_process_status_t status;
+ struct child_print_data cpd = { "msg" };
+
+ outfs->init(out);
+ errfs->init(err);
+
+ RE(atf_process_fork(&child, child_print, outfs->m_sb_ptr,
+ errfs->m_sb_ptr, &cpd));
+ if (outfs->process != NULL)
+ outfs->process(out, &child);
+ if (errfs->process != NULL)
+ errfs->process(err, &child);
+ RE(atf_process_child_wait(&child, &status));
+
+ outfs->fini(out);
+ errfs->fini(err);
+
+ atf_process_status_fini(&status);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the "stream" type.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(stream_init_capture);
+ATF_TC_HEAD(stream_init_capture, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_capture function");
+}
+ATF_TC_BODY(stream_init_capture, tc)
+{
+ atf_process_stream_t sb;
+
+ RE(atf_process_stream_init_capture(&sb));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_capture);
+
+ atf_process_stream_fini(&sb);
+}
+
+ATF_TC(stream_init_connect);
+ATF_TC_HEAD(stream_init_connect, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_connect function");
+}
+ATF_TC_BODY(stream_init_connect, tc)
+{
+ atf_process_stream_t sb;
+
+ RE(atf_process_stream_init_connect(&sb, 1, 2));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_connect);
+
+ atf_process_stream_fini(&sb);
+}
+
+ATF_TC(stream_init_inherit);
+ATF_TC_HEAD(stream_init_inherit, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_inherit function");
+}
+ATF_TC_BODY(stream_init_inherit, tc)
+{
+ atf_process_stream_t sb;
+
+ RE(atf_process_stream_init_inherit(&sb));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_inherit);
+
+ atf_process_stream_fini(&sb);
+}
+
+ATF_TC(stream_init_redirect_fd);
+ATF_TC_HEAD(stream_init_redirect_fd, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_redirect_fd function");
+}
+ATF_TC_BODY(stream_init_redirect_fd, tc)
+{
+ atf_process_stream_t sb;
+
+ RE(atf_process_stream_init_redirect_fd(&sb, 1));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_redirect_fd);
+
+ atf_process_stream_fini(&sb);
+}
+
+ATF_TC(stream_init_redirect_path);
+ATF_TC_HEAD(stream_init_redirect_path, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the "
+ "atf_process_stream_init_redirect_path function");
+}
+ATF_TC_BODY(stream_init_redirect_path, tc)
+{
+ atf_process_stream_t sb;
+ atf_fs_path_t path;
+
+ RE(atf_fs_path_init_fmt(&path, "foo"));
+ RE(atf_process_stream_init_redirect_path(&sb, &path));
+
+ ATF_CHECK_EQ(atf_process_stream_type(&sb),
+ atf_process_stream_type_redirect_path);
+
+ atf_process_stream_fini(&sb);
+ atf_fs_path_fini(&path);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the "status" type.
+ * --------------------------------------------------------------------- */
+
+static void child_exit_success(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void child_exit_failure(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void child_sigkill(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void child_sigquit(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void child_sigterm(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+void
+child_exit_success(void)
+{
+ exit(EXIT_SUCCESS);
+}
+
+void
+child_exit_failure(void)
+{
+ exit(EXIT_FAILURE);
+}
+
+void
+child_sigkill(void)
+{
+ kill(getpid(), SIGKILL);
+ abort();
+}
+
+void
+child_sigquit(void)
+{
+ kill(getpid(), SIGQUIT);
+ abort();
+}
+
+void
+child_sigterm(void)
+{
+ kill(getpid(), SIGTERM);
+ abort();
+}
+
+static
+int
+fork_and_wait_child(void (*child_func)(void))
+{
+ pid_t pid;
+ int status;
+
+ pid = fork();
+ ATF_REQUIRE(pid != -1);
+ if (pid == 0) {
+ status = 0; /* Silence compiler warnings */
+ child_func();
+ UNREACHABLE;
+ } else {
+ ATF_REQUIRE(waitpid(pid, &status, 0) != 0);
+ }
+
+ return status;
+}
+
+ATF_TC(status_exited);
+ATF_TC_HEAD(status_exited, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the status type for processes "
+ "that exit cleanly");
+}
+ATF_TC_BODY(status_exited, tc)
+{
+ {
+ const int rawstatus = fork_and_wait_child(child_exit_success);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(atf_process_status_exited(&s));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&s), EXIT_SUCCESS);
+ ATF_CHECK(!atf_process_status_signaled(&s));
+ atf_process_status_fini(&s);
+ }
+
+ {
+ const int rawstatus = fork_and_wait_child(child_exit_failure);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(atf_process_status_exited(&s));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&s), EXIT_FAILURE);
+ ATF_CHECK(!atf_process_status_signaled(&s));
+ atf_process_status_fini(&s);
+ }
+}
+
+ATF_TC(status_signaled);
+ATF_TC_HEAD(status_signaled, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the status type for processes "
+ "that end due to a signal");
+}
+ATF_TC_BODY(status_signaled, tc)
+{
+ {
+ const int rawstatus = fork_and_wait_child(child_sigkill);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(!atf_process_status_exited(&s));
+ ATF_CHECK(atf_process_status_signaled(&s));
+ ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGKILL);
+ ATF_CHECK(!atf_process_status_coredump(&s));
+ atf_process_status_fini(&s);
+ }
+
+ {
+ const int rawstatus = fork_and_wait_child(child_sigterm);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(!atf_process_status_exited(&s));
+ ATF_CHECK(atf_process_status_signaled(&s));
+ ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGTERM);
+ ATF_CHECK(!atf_process_status_coredump(&s));
+ atf_process_status_fini(&s);
+ }
+}
+
+ATF_TC(status_coredump);
+ATF_TC_HEAD(status_coredump, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the status type for processes "
+ "that crash");
+}
+ATF_TC_BODY(status_coredump, tc)
+{
+ struct rlimit rl;
+ rl.rlim_cur = RLIM_INFINITY;
+ rl.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_CORE, &rl) == -1)
+ atf_tc_skip("Cannot unlimit the core file size; check limits "
+ "manually");
+
+ const int rawstatus = fork_and_wait_child(child_sigquit);
+ atf_process_status_t s;
+ RE(atf_process_status_init(&s, rawstatus));
+ ATF_CHECK(!atf_process_status_exited(&s));
+ ATF_CHECK(atf_process_status_signaled(&s));
+ ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGQUIT);
+ ATF_CHECK(atf_process_status_coredump(&s));
+ atf_process_status_fini(&s);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the "child" type.
+ * --------------------------------------------------------------------- */
+
+static void child_report_pid(void *) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+static
+void
+child_report_pid(void *v ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+ const pid_t pid = getpid();
+ if (write(STDOUT_FILENO, &pid, sizeof(pid)) != sizeof(pid))
+ abort();
+ fprintf(stderr, "Reporting %d to parent\n", (int)getpid());
+ exit(EXIT_SUCCESS);
+}
+
+ATF_TC(child_pid);
+ATF_TC_HEAD(child_pid, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the correctness of the pid "
+ "stored in the child type");
+}
+ATF_TC_BODY(child_pid, tc)
+{
+ atf_process_stream_t outsb, errsb;
+ atf_process_child_t child;
+ atf_process_status_t status;
+ pid_t pid;
+
+ RE(atf_process_stream_init_capture(&outsb));
+ RE(atf_process_stream_init_inherit(&errsb));
+
+ RE(atf_process_fork(&child, child_report_pid, &outsb, &errsb, NULL));
+ ATF_CHECK_EQ(read(atf_process_child_stdout(&child), &pid, sizeof(pid)),
+ sizeof(pid));
+ printf("Expected PID: %d\n", (int)atf_process_child_pid(&child));
+ printf("Actual PID: %d\n", (int)pid);
+ ATF_CHECK_EQ(atf_process_child_pid(&child), pid);
+
+ RE(atf_process_child_wait(&child, &status));
+ atf_process_status_fini(&status);
+
+ atf_process_stream_fini(&outsb);
+ atf_process_stream_fini(&errsb);
+}
+
+static
+void
+child_loop(void *v ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+ for (;;)
+ sleep(1);
+}
+
+static
+void
+nop_signal(int sig ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+}
+
+static
+void
+child_spawn_loop_and_wait_eintr(void *v ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+ atf_process_child_t child;
+ atf_process_status_t status;
+ struct sigaction sighup, old_sighup;
+
+#define RE_ABORT(expr) \
+ do { \
+ atf_error_t _aux_err = expr; \
+ if (atf_is_error(_aux_err)) { \
+ atf_error_free(_aux_err); \
+ abort(); \
+ } \
+ } while (0)
+
+ {
+ atf_process_stream_t outsb, errsb;
+
+ RE_ABORT(atf_process_stream_init_capture(&outsb));
+ RE_ABORT(atf_process_stream_init_inherit(&errsb));
+ RE_ABORT(atf_process_fork(&child, child_loop, &outsb, &errsb, NULL));
+ atf_process_stream_fini(&outsb);
+ atf_process_stream_fini(&errsb);
+ }
+
+ sighup.sa_handler = nop_signal;
+ sigemptyset(&sighup.sa_mask);
+ sighup.sa_flags = 0;
+ if (sigaction(SIGHUP, &sighup, &old_sighup) == -1)
+ abort();
+
+ printf("waiting\n");
+ fflush(stdout);
+
+ fprintf(stderr, "Child entering wait(2)\n");
+ atf_error_t err = atf_process_child_wait(&child, &status);
+ fprintf(stderr, "Child's wait(2) terminated\n");
+ if (!atf_is_error(err)) {
+ fprintf(stderr, "wait completed successfully (not interrupted)\n");
+ abort();
+ }
+ if (!atf_error_is(err, "libc")) {
+ fprintf(stderr, "wait did not raise libc_error\n");
+ abort();
+ }
+ if (atf_libc_error_code(err) != EINTR) {
+ fprintf(stderr, "libc_error is not EINTR\n");
+ abort();
+ }
+ atf_error_free(err);
+
+ sigaction(SIGHUP, &old_sighup, NULL);
+
+ fprintf(stderr, "Child is killing subchild\n");
+ kill(atf_process_child_pid(&child), SIGTERM);
+
+ RE_ABORT(atf_process_child_wait(&child, &status));
+ atf_process_status_fini(&status);
+
+#undef RE_ABORT
+
+ exit(EXIT_SUCCESS);
+}
+
+ATF_TC(child_wait_eintr);
+ATF_TC_HEAD(child_wait_eintr, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the interruption of the wait "
+ "method by an external signal, and the return of "
+ "an EINTR error");
+ atf_tc_set_md_var(tc, "timeout", "30");
+}
+ATF_TC_BODY(child_wait_eintr, tc)
+{
+ atf_process_child_t child;
+ atf_process_status_t status;
+
+ {
+ atf_process_stream_t outsb, errsb;
+
+ RE(atf_process_stream_init_capture(&outsb));
+ RE(atf_process_stream_init_inherit(&errsb));
+ RE(atf_process_fork(&child, child_spawn_loop_and_wait_eintr,
+ &outsb, &errsb, NULL));
+ atf_process_stream_fini(&outsb);
+ atf_process_stream_fini(&errsb);
+ }
+
+ {
+ /* Wait until the child process performs the wait call. This is
+ * racy, because the message we get from it is sent *before*
+ * doing the real system call... but I can't figure any other way
+ * to do this. */
+ char buf[16];
+ printf("Waiting for child to issue wait(2)\n");
+ ATF_REQUIRE(read(atf_process_child_stdout(&child), buf,
+ sizeof(buf)) > 0);
+ sleep(1);
+ }
+
+ printf("Interrupting child's wait(2) call\n");
+ kill(atf_process_child_pid(&child), SIGHUP);
+
+ printf("Waiting for child's completion\n");
+ RE(atf_process_child_wait(&child, &status));
+ ATF_REQUIRE(atf_process_status_exited(&status));
+ ATF_REQUIRE_EQ(atf_process_status_exitstatus(&status), EXIT_SUCCESS);
+ atf_process_status_fini(&status);
+}
+
+/* ---------------------------------------------------------------------
+ * Tests cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+static
+void
+do_exec(const atf_tc_t *tc, const char *helper_name, atf_process_status_t *s,
+ void (*prehook)(void))
+{
+ atf_fs_path_t process_helpers;
+ const char *argv[3];
+
+ get_process_helpers_path(tc, true, &process_helpers);
+
+ argv[0] = atf_fs_path_cstring(&process_helpers);
+ argv[1] = helper_name;
+ argv[2] = NULL;
+ printf("Executing %s %s\n", argv[0], argv[1]);
+
+ RE(atf_process_exec_array(s, &process_helpers, argv, NULL, NULL, prehook));
+ atf_fs_path_fini(&process_helpers);
+}
+
+static
+void
+check_line(int fd, const char *exp)
+{
+ char *line = atf_utils_readline(fd);
+ ATF_CHECK(line != NULL);
+ ATF_CHECK_STREQ_MSG(exp, line, "read: '%s', expected: '%s'", line, exp);
+ free(line);
+}
+
+ATF_TC(exec_failure);
+ATF_TC_HEAD(exec_failure, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests execing a command");
+}
+ATF_TC_BODY(exec_failure, tc)
+{
+ atf_process_status_t status;
+
+ do_exec(tc, "exit-failure", &status, NULL);
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), EXIT_FAILURE);
+ atf_process_status_fini(&status);
+}
+
+ATF_TC(exec_list);
+ATF_TC_HEAD(exec_list, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests execing a command");
+}
+ATF_TC_BODY(exec_list, tc)
+{
+ atf_fs_path_t process_helpers;
+ atf_list_t argv;
+ atf_process_status_t status;
+
+ RE(atf_list_init(&argv));
+
+ get_process_helpers_path(tc, true, &process_helpers);
+ atf_list_append(&argv, strdup(atf_fs_path_cstring(&process_helpers)), true);
+ atf_list_append(&argv, strdup("echo"), true);
+ atf_list_append(&argv, strdup("test-message"), true);
+ {
+ atf_fs_path_t outpath;
+ atf_process_stream_t outsb;
+
+ RE(atf_fs_path_init_fmt(&outpath, "stdout"));
+ RE(atf_process_stream_init_redirect_path(&outsb, &outpath));
+ RE(atf_process_exec_list(&status, &process_helpers, &argv, &outsb,
+ NULL, NULL));
+ atf_process_stream_fini(&outsb);
+ atf_fs_path_fini(&outpath);
+ }
+ atf_list_fini(&argv);
+
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), EXIT_SUCCESS);
+
+ {
+ int fd = open("stdout", O_RDONLY);
+ ATF_CHECK(fd != -1);
+ check_line(fd, "test-message");
+ close(fd);
+ }
+
+ atf_process_status_fini(&status);
+ atf_fs_path_fini(&process_helpers);
+}
+
+static void
+exit_early(void)
+{
+ exit(80);
+}
+
+ATF_TC(exec_prehook);
+ATF_TC_HEAD(exec_prehook, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests execing a command with a prehook");
+}
+ATF_TC_BODY(exec_prehook, tc)
+{
+ atf_process_status_t status;
+
+ do_exec(tc, "exit-success", &status, exit_early);
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), 80);
+ atf_process_status_fini(&status);
+}
+
+ATF_TC(exec_success);
+ATF_TC_HEAD(exec_success, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests execing a command");
+}
+ATF_TC_BODY(exec_success, tc)
+{
+ atf_process_status_t status;
+
+ do_exec(tc, "exit-success", &status, NULL);
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), EXIT_SUCCESS);
+ atf_process_status_fini(&status);
+}
+
+static const int exit_v_null = 1;
+static const int exit_v_notnull = 2;
+
+static
+void
+child_cookie(void *v)
+{
+ if (v == NULL)
+ exit(exit_v_null);
+ else
+ exit(exit_v_notnull);
+
+ UNREACHABLE;
+}
+
+ATF_TC(fork_cookie);
+ATF_TC_HEAD(fork_cookie, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests forking a child, with "
+ "a null and non-null data cookie");
+}
+ATF_TC_BODY(fork_cookie, tc)
+{
+ atf_process_stream_t outsb, errsb;
+
+ RE(atf_process_stream_init_inherit(&outsb));
+ RE(atf_process_stream_init_inherit(&errsb));
+
+ {
+ atf_process_child_t child;
+ atf_process_status_t status;
+
+ RE(atf_process_fork(&child, child_cookie, &outsb, &errsb, NULL));
+ RE(atf_process_child_wait(&child, &status));
+
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), exit_v_null);
+
+ atf_process_status_fini(&status);
+ }
+
+ {
+ atf_process_child_t child;
+ atf_process_status_t status;
+ int dummy_int;
+
+ RE(atf_process_fork(&child, child_cookie, &outsb, &errsb, &dummy_int));
+ RE(atf_process_child_wait(&child, &status));
+
+ ATF_CHECK(atf_process_status_exited(&status));
+ ATF_CHECK_EQ(atf_process_status_exitstatus(&status), exit_v_notnull);
+
+ atf_process_status_fini(&status);
+ }
+
+ atf_process_stream_fini(&errsb);
+ atf_process_stream_fini(&outsb);
+}
+
+#define TC_FORK_STREAMS(outlc, outuc, errlc, erruc) \
+ ATF_TC(fork_out_ ## outlc ## _err_ ## errlc); \
+ ATF_TC_HEAD(fork_out_ ## outlc ## _err_ ## errlc, tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr", "Tests forking a child, with " \
+ "stdout " #outlc " and stderr " #errlc); \
+ } \
+ ATF_TC_BODY(fork_out_ ## outlc ## _err_ ## errlc, tc) \
+ { \
+ struct outlc ## _stream out = outuc ## _STREAM(stdout_type); \
+ struct errlc ## _stream err = erruc ## _STREAM(stderr_type); \
+ do_fork(&out.m_base, &out, &err.m_base, &err); \
+ }
+
+TC_FORK_STREAMS(capture, CAPTURE, capture, CAPTURE);
+TC_FORK_STREAMS(capture, CAPTURE, connect, CONNECT);
+TC_FORK_STREAMS(capture, CAPTURE, default, DEFAULT);
+TC_FORK_STREAMS(capture, CAPTURE, inherit, INHERIT);
+TC_FORK_STREAMS(capture, CAPTURE, redirect_fd, REDIRECT_FD);
+TC_FORK_STREAMS(capture, CAPTURE, redirect_path, REDIRECT_PATH);
+TC_FORK_STREAMS(connect, CONNECT, capture, CAPTURE);
+TC_FORK_STREAMS(connect, CONNECT, connect, CONNECT);
+TC_FORK_STREAMS(connect, CONNECT, default, DEFAULT);
+TC_FORK_STREAMS(connect, CONNECT, inherit, INHERIT);
+TC_FORK_STREAMS(connect, CONNECT, redirect_fd, REDIRECT_FD);
+TC_FORK_STREAMS(connect, CONNECT, redirect_path, REDIRECT_PATH);
+TC_FORK_STREAMS(default, DEFAULT, capture, CAPTURE);
+TC_FORK_STREAMS(default, DEFAULT, connect, CONNECT);
+TC_FORK_STREAMS(default, DEFAULT, default, DEFAULT);
+TC_FORK_STREAMS(default, DEFAULT, inherit, INHERIT);
+TC_FORK_STREAMS(default, DEFAULT, redirect_fd, REDIRECT_FD);
+TC_FORK_STREAMS(default, DEFAULT, redirect_path, REDIRECT_PATH);
+TC_FORK_STREAMS(inherit, INHERIT, capture, CAPTURE);
+TC_FORK_STREAMS(inherit, INHERIT, connect, CONNECT);
+TC_FORK_STREAMS(inherit, INHERIT, default, DEFAULT);
+TC_FORK_STREAMS(inherit, INHERIT, inherit, INHERIT);
+TC_FORK_STREAMS(inherit, INHERIT, redirect_fd, REDIRECT_FD);
+TC_FORK_STREAMS(inherit, INHERIT, redirect_path, REDIRECT_PATH);
+TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, capture, CAPTURE);
+TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, connect, CONNECT);
+TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, default, DEFAULT);
+TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, inherit, INHERIT);
+TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, redirect_fd, REDIRECT_FD);
+TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, redirect_path, REDIRECT_PATH);
+TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, capture, CAPTURE);
+TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, connect, CONNECT);
+TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, default, DEFAULT);
+TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, inherit, INHERIT);
+TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, redirect_fd, REDIRECT_FD);
+TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, redirect_path, REDIRECT_PATH);
+
+#undef TC_FORK_STREAMS
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Add the tests for the "stream" type. */
+ ATF_TP_ADD_TC(tp, stream_init_capture);
+ ATF_TP_ADD_TC(tp, stream_init_connect);
+ ATF_TP_ADD_TC(tp, stream_init_inherit);
+ ATF_TP_ADD_TC(tp, stream_init_redirect_fd);
+ ATF_TP_ADD_TC(tp, stream_init_redirect_path);
+
+ /* Add the tests for the "status" type. */
+ ATF_TP_ADD_TC(tp, status_exited);
+ ATF_TP_ADD_TC(tp, status_signaled);
+ ATF_TP_ADD_TC(tp, status_coredump);
+
+ /* Add the tests for the "child" type. */
+ ATF_TP_ADD_TC(tp, child_pid);
+ ATF_TP_ADD_TC(tp, child_wait_eintr);
+
+ /* Add the tests for the free functions. */
+ ATF_TP_ADD_TC(tp, exec_failure);
+ ATF_TP_ADD_TC(tp, exec_list);
+ ATF_TP_ADD_TC(tp, exec_prehook);
+ ATF_TP_ADD_TC(tp, exec_success);
+ ATF_TP_ADD_TC(tp, fork_cookie);
+ ATF_TP_ADD_TC(tp, fork_out_capture_err_capture);
+ ATF_TP_ADD_TC(tp, fork_out_capture_err_connect);
+ ATF_TP_ADD_TC(tp, fork_out_capture_err_default);
+ ATF_TP_ADD_TC(tp, fork_out_capture_err_inherit);
+ ATF_TP_ADD_TC(tp, fork_out_capture_err_redirect_fd);
+ ATF_TP_ADD_TC(tp, fork_out_capture_err_redirect_path);
+ ATF_TP_ADD_TC(tp, fork_out_connect_err_capture);
+ ATF_TP_ADD_TC(tp, fork_out_connect_err_connect);
+ ATF_TP_ADD_TC(tp, fork_out_connect_err_default);
+ ATF_TP_ADD_TC(tp, fork_out_connect_err_inherit);
+ ATF_TP_ADD_TC(tp, fork_out_connect_err_redirect_fd);
+ ATF_TP_ADD_TC(tp, fork_out_connect_err_redirect_path);
+ ATF_TP_ADD_TC(tp, fork_out_default_err_capture);
+ ATF_TP_ADD_TC(tp, fork_out_default_err_connect);
+ ATF_TP_ADD_TC(tp, fork_out_default_err_default);
+ ATF_TP_ADD_TC(tp, fork_out_default_err_inherit);
+ ATF_TP_ADD_TC(tp, fork_out_default_err_redirect_fd);
+ ATF_TP_ADD_TC(tp, fork_out_default_err_redirect_path);
+ ATF_TP_ADD_TC(tp, fork_out_inherit_err_capture);
+ ATF_TP_ADD_TC(tp, fork_out_inherit_err_connect);
+ ATF_TP_ADD_TC(tp, fork_out_inherit_err_default);
+ ATF_TP_ADD_TC(tp, fork_out_inherit_err_inherit);
+ ATF_TP_ADD_TC(tp, fork_out_inherit_err_redirect_fd);
+ ATF_TP_ADD_TC(tp, fork_out_inherit_err_redirect_path);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_capture);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_connect);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_default);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_inherit);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_redirect_fd);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_redirect_path);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_capture);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_connect);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_default);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_inherit);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_redirect_fd);
+ ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_redirect_path);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/sanity.c b/unit/atf-src/atf-c/detail/sanity.c
new file mode 100644
index 0000000..cfc8676
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/sanity.c
@@ -0,0 +1,74 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/sanity.h"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <err.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static
+void
+fail(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[4096];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ warnx("%s", buf);
+ warnx("%s", "");
+ warnx("This is probably a bug in this application or one of the "
+ "libraries it uses. If you believe this problem is caused "
+ "by, or is related to " PACKAGE_STRING ", please report it "
+ "to " PACKAGE_BUGREPORT " and provide as many details as "
+ "possible describing how you got to this condition.");
+
+ abort();
+}
+
+void
+atf_sanity_inv(const char *file, int line, const char *cond)
+{
+ fail("Invariant check failed at %s:%d: %s", file, line, cond);
+}
+
+void
+atf_sanity_pre(const char *file, int line, const char *cond)
+{
+ fail("Precondition check failed at %s:%d: %s", file, line, cond);
+}
+
+void
+atf_sanity_post(const char *file, int line, const char *cond)
+{
+ fail("Postcondition check failed at %s:%d: %s", file, line, cond);
+}
diff --git a/unit/atf-src/atf-c/detail/sanity.h b/unit/atf-src/atf-c/detail/sanity.h
new file mode 100644
index 0000000..33b5528
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/sanity.h
@@ -0,0 +1,72 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_DETAIL_SANITY_H)
+#define ATF_C_DETAIL_SANITY_H
+
+void atf_sanity_inv(const char *, int, const char *);
+void atf_sanity_pre(const char *, int, const char *);
+void atf_sanity_post(const char *, int, const char *);
+
+#if !defined(NDEBUG)
+
+#define INV(x) \
+ do { \
+ if (!(x)) \
+ atf_sanity_inv(__FILE__, __LINE__, #x); \
+ } while (0)
+#define PRE(x) \
+ do { \
+ if (!(x)) \
+ atf_sanity_pre(__FILE__, __LINE__, #x); \
+ } while (0)
+#define POST(x) \
+ do { \
+ if (!(x)) \
+ atf_sanity_post(__FILE__, __LINE__, #x); \
+ } while (0)
+
+#else /* defined(NDEBUG) */
+
+#define INV(x) \
+ do { \
+ (void)(x); \
+ } while (0)
+
+#define PRE(x) \
+ do { \
+ (void)(x); \
+ } while (0)
+
+#define POST(x) \
+ do { \
+ (void)(x); \
+ } while (0)
+
+#endif /* !defined(NDEBUG) */
+
+#define UNREACHABLE INV(0)
+
+#endif /* !defined(ATF_C_DETAIL_SANITY_H) */
diff --git a/unit/atf-src/atf-c/detail/sanity_test.c b/unit/atf-src/atf-c/detail/sanity_test.c
new file mode 100644
index 0000000..a986c32
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/sanity_test.c
@@ -0,0 +1,230 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/sanity.h"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <signal.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/dynstr.h"
+#include "atf-c/detail/process.h"
+#include "atf-c/detail/test_helpers.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+enum type { inv, pre, post, unreachable };
+
+struct test_data {
+ enum type m_type;
+ bool m_cond;
+};
+
+static void do_test_child(void *) ATF_DEFS_ATTRIBUTE_NORETURN;
+
+static
+void
+do_test_child(void *v)
+{
+ struct test_data *td = v;
+
+ switch (td->m_type) {
+ case inv:
+ INV(td->m_cond);
+ break;
+
+ case pre:
+ PRE(td->m_cond);
+ break;
+
+ case post:
+ POST(td->m_cond);
+ break;
+
+ case unreachable:
+ if (!td->m_cond)
+ UNREACHABLE;
+ break;
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
+static
+void
+do_test(enum type t, bool cond)
+{
+ atf_process_child_t child;
+ atf_process_status_t status;
+ int nlines;
+ char *lines[3];
+
+ {
+ atf_process_stream_t outsb, errsb;
+ struct test_data td = { t, cond };
+
+ RE(atf_process_stream_init_inherit(&outsb));
+ RE(atf_process_stream_init_capture(&errsb));
+ RE(atf_process_fork(&child, do_test_child, &outsb, &errsb, &td));
+ atf_process_stream_fini(&errsb);
+ atf_process_stream_fini(&outsb);
+ }
+
+ nlines = 0;
+ while (nlines < 3 && (lines[nlines] =
+ atf_utils_readline(atf_process_child_stderr(&child))) != NULL)
+ nlines++;
+ ATF_REQUIRE(nlines == 0 || nlines == 3);
+
+ RE(atf_process_child_wait(&child, &status));
+ if (!cond) {
+ ATF_REQUIRE(atf_process_status_signaled(&status));
+ ATF_REQUIRE(atf_process_status_termsig(&status) == SIGABRT);
+ } else {
+ ATF_REQUIRE(atf_process_status_exited(&status));
+ ATF_REQUIRE(atf_process_status_exitstatus(&status) == EXIT_SUCCESS);
+ }
+ atf_process_status_fini(&status);
+
+ if (!cond) {
+ switch (t) {
+ case inv:
+ ATF_REQUIRE(atf_utils_grep_string("Invariant", lines[0]));
+ break;
+
+ case pre:
+ ATF_REQUIRE(atf_utils_grep_string("Precondition", lines[0]));
+ break;
+
+ case post:
+ ATF_REQUIRE(atf_utils_grep_string("Postcondition", lines[0]));
+ break;
+
+ case unreachable:
+ ATF_REQUIRE(atf_utils_grep_string("Invariant", lines[0]));
+ break;
+ }
+
+ ATF_REQUIRE(atf_utils_grep_string(__FILE__, lines[0]));
+ ATF_REQUIRE(atf_utils_grep_string(PACKAGE_BUGREPORT, lines[2]));
+ }
+
+ while (nlines > 0) {
+ nlines--;
+ free(lines[nlines]);
+ }
+}
+
+static
+void
+require_ndebug(void)
+{
+#if defined(NDEBUG)
+ atf_tc_skip("Sanity checks not available; code built with -DNDEBUG");
+#endif
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(inv);
+ATF_TC_HEAD(inv, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the INV macro");
+}
+ATF_TC_BODY(inv, tc)
+{
+ require_ndebug();
+
+ do_test(inv, false);
+ do_test(inv, true);
+}
+
+ATF_TC(pre);
+ATF_TC_HEAD(pre, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the PRE macro");
+}
+ATF_TC_BODY(pre, tc)
+{
+ require_ndebug();
+
+ do_test(pre, false);
+ do_test(pre, true);
+}
+
+ATF_TC(post);
+ATF_TC_HEAD(post, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the POST macro");
+}
+ATF_TC_BODY(post, tc)
+{
+ require_ndebug();
+
+ do_test(post, false);
+ do_test(post, true);
+}
+
+ATF_TC(unreachable);
+ATF_TC_HEAD(unreachable, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the UNREACHABLE macro");
+}
+ATF_TC_BODY(unreachable, tc)
+{
+ require_ndebug();
+
+ do_test(unreachable, false);
+ do_test(unreachable, true);
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, inv);
+ ATF_TP_ADD_TC(tp, pre);
+ ATF_TP_ADD_TC(tp, post);
+ ATF_TP_ADD_TC(tp, unreachable);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/test_helpers.c b/unit/atf-src/atf-c/detail/test_helpers.c
new file mode 100644
index 0000000..827d46e
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/test_helpers.c
@@ -0,0 +1,147 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/test_helpers.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/build.h"
+#include "atf-c/check.h"
+#include "atf-c/detail/dynstr.h"
+#include "atf-c/detail/env.h"
+#include "atf-c/detail/fs.h"
+#include "atf-c/detail/process.h"
+#include "atf-c/error.h"
+
+bool
+build_check_c_o(const char *path)
+{
+ bool success;
+ atf_dynstr_t iflag;
+ const char *optargs[4];
+
+ RE(atf_dynstr_init_fmt(&iflag, "-I%s", atf_env_get_with_default(
+ "ATF_INCLUDEDIR", ATF_INCLUDEDIR)));
+
+ optargs[0] = atf_dynstr_cstring(&iflag);
+ optargs[1] = "-Wall";
+ optargs[2] = "-Werror";
+ optargs[3] = NULL;
+
+ RE(atf_check_build_c_o(path, "test.o", optargs, &success));
+
+ atf_dynstr_fini(&iflag);
+
+ return success;
+}
+
+bool
+build_check_c_o_srcdir(const atf_tc_t *tc, const char *sfile)
+{
+ atf_fs_path_t path;
+
+ RE(atf_fs_path_init_fmt(&path, "%s/%s",
+ atf_tc_get_config_var(tc, "srcdir"), sfile));
+ const bool result = build_check_c_o(atf_fs_path_cstring(&path));
+ atf_fs_path_fini(&path);
+ return result;
+}
+
+void
+header_check(const char *hdrname)
+{
+ FILE *srcfile;
+ char failmsg[128];
+
+ srcfile = fopen("test.c", "w");
+ ATF_REQUIRE(srcfile != NULL);
+ fprintf(srcfile, "#include <%s>\n", hdrname);
+ fclose(srcfile);
+
+ snprintf(failmsg, sizeof(failmsg),
+ "Header check failed; %s is not self-contained", hdrname);
+
+ if (!build_check_c_o("test.c"))
+ atf_tc_fail("%s", failmsg);
+}
+
+void
+get_process_helpers_path(const atf_tc_t *tc, const bool is_detail,
+ atf_fs_path_t *path)
+{
+ RE(atf_fs_path_init_fmt(path, "%s/%sprocess_helpers",
+ atf_tc_get_config_var(tc, "srcdir"),
+ is_detail ? "" : "detail/"));
+}
+
+struct run_h_tc_data {
+ atf_tc_t *m_tc;
+ const char *m_resname;
+};
+
+static
+void
+run_h_tc_child(void *v)
+{
+ struct run_h_tc_data *data = (struct run_h_tc_data *)v;
+
+ RE(atf_tc_run(data->m_tc, data->m_resname));
+}
+
+/* TODO: Investigate if it's worth to add this functionality as part of
+ * the public API. I.e. a function to easily run a test case body in a
+ * subprocess. */
+void
+run_h_tc(atf_tc_t *tc, const char *outname, const char *errname,
+ const char *resname)
+{
+ atf_fs_path_t outpath, errpath;
+ atf_process_stream_t outb, errb;
+ atf_process_child_t child;
+ atf_process_status_t status;
+
+ RE(atf_fs_path_init_fmt(&outpath, outname));
+ RE(atf_fs_path_init_fmt(&errpath, errname));
+
+ struct run_h_tc_data data = { tc, resname };
+
+ RE(atf_process_stream_init_redirect_path(&outb, &outpath));
+ RE(atf_process_stream_init_redirect_path(&errb, &errpath));
+ RE(atf_process_fork(&child, run_h_tc_child, &outb, &errb, &data));
+ atf_process_stream_fini(&errb);
+ atf_process_stream_fini(&outb);
+
+ RE(atf_process_child_wait(&child, &status));
+ ATF_CHECK(atf_process_status_exited(&status));
+ atf_process_status_fini(&status);
+
+ atf_fs_path_fini(&errpath);
+ atf_fs_path_fini(&outpath);
+}
diff --git a/unit/atf-src/atf-c/detail/test_helpers.h b/unit/atf-src/atf-c/detail/test_helpers.h
new file mode 100644
index 0000000..a601c29
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/test_helpers.h
@@ -0,0 +1,75 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if defined(ATF_C_DETAIL_TEST_HELPERS_H)
+# error "Cannot include test_helpers.h more than once."
+#else
+# define ATF_C_DETAIL_TEST_HELPERS_H
+#endif
+
+#include <stdbool.h>
+
+#include <atf-c.h>
+
+#include <atf-c/error_fwd.h>
+#include <atf-c/tc.h>
+
+struct atf_dynstr;
+struct atf_fs_path;
+
+#define CE(stm) ATF_CHECK(!atf_is_error(stm))
+#define RE(stm) ATF_REQUIRE(!atf_is_error(stm))
+
+#define HEADER_TC(name, hdrname) \
+ ATF_TC(name); \
+ ATF_TC_HEAD(name, tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr", "Tests that the " hdrname " file can " \
+ "be included on its own, without any prerequisites"); \
+ } \
+ ATF_TC_BODY(name, tc) \
+ { \
+ header_check(hdrname); \
+ }
+
+#define BUILD_TC(name, sfile, descr, failmsg) \
+ ATF_TC(name); \
+ ATF_TC_HEAD(name, tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr", descr); \
+ } \
+ ATF_TC_BODY(name, tc) \
+ { \
+ if (!build_check_c_o_srcdir(tc, sfile)) \
+ atf_tc_fail("%s", failmsg); \
+ }
+
+bool build_check_c_o(const char *);
+bool build_check_c_o_srcdir(const atf_tc_t *, const char *);
+void header_check(const char *);
+void get_process_helpers_path(const atf_tc_t *, const bool,
+ struct atf_fs_path *);
+bool read_line(int, struct atf_dynstr *);
+void run_h_tc(atf_tc_t *, const char *, const char *, const char *);
diff --git a/unit/atf-src/atf-c/detail/text.c b/unit/atf-src/atf-c/detail/text.c
new file mode 100644
index 0000000..3b1d7e7
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/text.c
@@ -0,0 +1,180 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/text.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "atf-c/detail/dynstr.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/error.h"
+
+atf_error_t
+atf_text_for_each_word(const char *instr, const char *sep,
+ atf_error_t (*func)(const char *, void *),
+ void *data)
+{
+ atf_error_t err;
+ char *str, *str2, *last;
+
+ str = strdup(instr);
+ if (str == NULL) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ err = atf_no_error();
+ str2 = strtok_r(str, sep, &last);
+ while (str2 != NULL && !atf_is_error(err)) {
+ err = func(str2, data);
+ str2 = strtok_r(NULL, sep, &last);
+ }
+
+ free(str);
+out:
+ return err;
+}
+
+atf_error_t
+atf_text_format(char **dest, const char *fmt, ...)
+{
+ atf_error_t err;
+ va_list ap;
+
+ va_start(ap, fmt);
+ err = atf_text_format_ap(dest, fmt, ap);
+ va_end(ap);
+
+ return err;
+}
+
+atf_error_t
+atf_text_format_ap(char **dest, const char *fmt, va_list ap)
+{
+ atf_error_t err;
+ atf_dynstr_t tmp;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ err = atf_dynstr_init_ap(&tmp, fmt, ap2);
+ va_end(ap2);
+ if (!atf_is_error(err))
+ *dest = atf_dynstr_fini_disown(&tmp);
+
+ return err;
+}
+
+atf_error_t
+atf_text_split(const char *str, const char *delim, atf_list_t *words)
+{
+ atf_error_t err;
+ const char *end;
+ const char *iter;
+
+ err = atf_list_init(words);
+ if (atf_is_error(err))
+ goto err;
+
+ end = str + strlen(str);
+ INV(*end == '\0');
+ iter = str;
+ while (iter < end) {
+ const char *ptr;
+
+ INV(iter != NULL);
+ ptr = strstr(iter, delim);
+ if (ptr == NULL)
+ ptr = end;
+
+ INV(ptr >= iter);
+ if (ptr > iter) {
+ atf_dynstr_t word;
+
+ err = atf_dynstr_init_raw(&word, iter, ptr - iter);
+ if (atf_is_error(err))
+ goto err_list;
+
+ err = atf_list_append(words, atf_dynstr_fini_disown(&word), true);
+ if (atf_is_error(err))
+ goto err_list;
+ }
+
+ iter = ptr + strlen(delim);
+ }
+
+ INV(!atf_is_error(err));
+ return err;
+
+err_list:
+ atf_list_fini(words);
+err:
+ return err;
+}
+
+atf_error_t
+atf_text_to_bool(const char *str, bool *b)
+{
+ atf_error_t err;
+
+ if (strcasecmp(str, "yes") == 0 ||
+ strcasecmp(str, "true") == 0) {
+ *b = true;
+ err = atf_no_error();
+ } else if (strcasecmp(str, "no") == 0 ||
+ strcasecmp(str, "false") == 0) {
+ *b = false;
+ err = atf_no_error();
+ } else {
+ /* XXX Not really a libc error. */
+ err = atf_libc_error(EINVAL, "Cannot convert string '%s' "
+ "to boolean", str);
+ }
+
+ return err;
+}
+
+atf_error_t
+atf_text_to_long(const char *str, long *l)
+{
+ atf_error_t err;
+ char *endptr;
+ long tmp;
+
+ errno = 0;
+ tmp = strtol(str, &endptr, 10);
+ if (str[0] == '\0' || *endptr != '\0')
+ err = atf_libc_error(EINVAL, "'%s' is not a number", str);
+ else if (errno == ERANGE || (tmp == LONG_MAX || tmp == LONG_MIN))
+ err = atf_libc_error(ERANGE, "'%s' is out of range", str);
+ else {
+ *l = tmp;
+ err = atf_no_error();
+ }
+
+ return err;
+}
diff --git a/unit/atf-src/atf-c/detail/text.h b/unit/atf-src/atf-c/detail/text.h
new file mode 100644
index 0000000..b2820b6
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/text.h
@@ -0,0 +1,44 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_DETAIL_TEXT_H)
+#define ATF_C_DETAIL_TEXT_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include <atf-c/detail/list.h>
+#include <atf-c/error_fwd.h>
+
+atf_error_t atf_text_for_each_word(const char *, const char *,
+ atf_error_t (*)(const char *, void *),
+ void *);
+atf_error_t atf_text_format(char **, const char *, ...);
+atf_error_t atf_text_format_ap(char **, const char *, va_list);
+atf_error_t atf_text_split(const char *, const char *, atf_list_t *);
+atf_error_t atf_text_to_bool(const char *, bool *);
+atf_error_t atf_text_to_long(const char *, long *);
+
+#endif /* !defined(ATF_C_DETAIL_TEXT_H) */
diff --git a/unit/atf-src/atf-c/detail/text_test.c b/unit/atf-src/atf-c/detail/text_test.c
new file mode 100644
index 0000000..5d6c8e5
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/text_test.c
@@ -0,0 +1,421 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/text.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/sanity.h"
+#include "atf-c/detail/test_helpers.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+#define REQUIRE_ERROR(exp) \
+ do { \
+ atf_error_t err = exp; \
+ ATF_REQUIRE(atf_is_error(err)); \
+ atf_error_free(err); \
+ } while (0)
+
+static
+size_t
+array_size(const char *words[])
+{
+ size_t count;
+ const char **word;
+
+ count = 0;
+ for (word = words; *word != NULL; word++)
+ count++;
+
+ return count;
+}
+
+static
+void
+check_split(const char *str, const char *delim, const char *words[])
+{
+ atf_list_t list;
+ const char **word;
+ size_t i;
+
+ printf("Splitting '%s' with delimiter '%s'\n", str, delim);
+ CE(atf_text_split(str, delim, &list));
+
+ printf("Expecting %zd words\n", array_size(words));
+ ATF_CHECK_EQ(atf_list_size(&list), array_size(words));
+
+ for (word = words, i = 0; *word != NULL; word++, i++) {
+ printf("Word at position %zd should be '%s'\n", i, words[i]);
+ ATF_CHECK_STREQ((const char *)atf_list_index_c(&list, i), words[i]);
+ }
+
+ atf_list_fini(&list);
+}
+
+static
+atf_error_t
+word_acum(const char *word, void *data)
+{
+ char *acum = data;
+
+ strcat(acum, word);
+
+ return atf_no_error();
+}
+
+static
+atf_error_t
+word_count(const char *word ATF_DEFS_ATTRIBUTE_UNUSED, void *data)
+{
+ size_t *counter = data;
+
+ (*counter)++;
+
+ return atf_no_error();
+}
+
+struct fail_at {
+ int failpos;
+ int curpos;
+};
+
+static
+atf_error_t
+word_fail_at(const char *word ATF_DEFS_ATTRIBUTE_UNUSED, void *data)
+{
+ struct fail_at *fa = data;
+ atf_error_t err;
+
+ if (fa->failpos == fa->curpos)
+ err = atf_no_memory_error(); /* Just a random error. */
+ else {
+ fa->curpos++;
+ err = atf_no_error();
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(for_each_word);
+ATF_TC_HEAD(for_each_word, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_text_for_each_word"
+ "function");
+}
+ATF_TC_BODY(for_each_word, tc)
+{
+ size_t cnt;
+ char acum[1024];
+
+ cnt = 0;
+ strcpy(acum, "");
+ RE(atf_text_for_each_word("1 2 3", " ", word_count, &cnt));
+ RE(atf_text_for_each_word("1 2 3", " ", word_acum, acum));
+ ATF_REQUIRE(cnt == 3);
+ ATF_REQUIRE(strcmp(acum, "123") == 0);
+
+ cnt = 0;
+ strcpy(acum, "");
+ RE(atf_text_for_each_word("1 2 3", ".", word_count, &cnt));
+ RE(atf_text_for_each_word("1 2 3", ".", word_acum, acum));
+ ATF_REQUIRE(cnt == 1);
+ ATF_REQUIRE(strcmp(acum, "1 2 3") == 0);
+
+ cnt = 0;
+ strcpy(acum, "");
+ RE(atf_text_for_each_word("1 2 3 4 5", " ", word_count, &cnt));
+ RE(atf_text_for_each_word("1 2 3 4 5", " ", word_acum, acum));
+ ATF_REQUIRE(cnt == 5);
+ ATF_REQUIRE(strcmp(acum, "12345") == 0);
+
+ cnt = 0;
+ strcpy(acum, "");
+ RE(atf_text_for_each_word("1 2.3.4 5", " .", word_count, &cnt));
+ RE(atf_text_for_each_word("1 2.3.4 5", " .", word_acum, acum));
+ ATF_REQUIRE(cnt == 5);
+ ATF_REQUIRE(strcmp(acum, "12345") == 0);
+
+ {
+ struct fail_at fa;
+ fa.failpos = 3;
+ fa.curpos = 0;
+ atf_error_t err = atf_text_for_each_word("a b c d e", " ",
+ word_fail_at, &fa);
+ ATF_REQUIRE(atf_is_error(err));
+ ATF_REQUIRE(atf_error_is(err, "no_memory"));
+ ATF_REQUIRE(fa.curpos == 3);
+ atf_error_free(err);
+ }
+}
+
+ATF_TC(format);
+ATF_TC_HEAD(format, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of free-form "
+ "strings using a variable parameters list");
+}
+ATF_TC_BODY(format, tc)
+{
+ char *str;
+ atf_error_t err;
+
+ err = atf_text_format(&str, "%s %s %d", "Test", "string", 1);
+ ATF_REQUIRE(!atf_is_error(err));
+ ATF_REQUIRE(strcmp(str, "Test string 1") == 0);
+ free(str);
+}
+
+static
+void
+format_ap(char **dest, const char *fmt, ...)
+{
+ va_list ap;
+ atf_error_t err;
+
+ va_start(ap, fmt);
+ err = atf_text_format_ap(dest, fmt, ap);
+ va_end(ap);
+
+ ATF_REQUIRE(!atf_is_error(err));
+}
+
+ATF_TC(format_ap);
+ATF_TC_HEAD(format_ap, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of free-form "
+ "strings using a va_list argument");
+}
+ATF_TC_BODY(format_ap, tc)
+{
+ char *str;
+
+ format_ap(&str, "%s %s %d", "Test", "string", 1);
+ ATF_REQUIRE(strcmp(str, "Test string 1") == 0);
+ free(str);
+}
+
+ATF_TC(split);
+ATF_TC_HEAD(split, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the split function");
+}
+ATF_TC_BODY(split, tc)
+{
+ {
+ const char *words[] = { NULL };
+ check_split("", " ", words);
+ }
+
+ {
+ const char *words[] = { NULL };
+ check_split(" ", " ", words);
+ }
+
+ {
+ const char *words[] = { NULL };
+ check_split(" ", " ", words);
+ }
+
+ {
+ const char *words[] = { "a", "b", NULL };
+ check_split("a b", " ", words);
+ }
+
+ {
+ const char *words[] = { "a", "b", "c", "d", NULL };
+ check_split("a b c d", " ", words);
+ }
+
+ {
+ const char *words[] = { "foo", "bar", NULL };
+ check_split("foo bar", " ", words);
+ }
+
+ {
+ const char *words[] = { "foo", "bar", "baz", "foobar", NULL };
+ check_split("foo bar baz foobar", " ", words);
+ }
+
+ {
+ const char *words[] = { "foo", "bar", NULL };
+ check_split(" foo bar", " ", words);
+ }
+
+ {
+ const char *words[] = { "foo", "bar", NULL };
+ check_split("foo bar", " ", words);
+ }
+
+ {
+ const char *words[] = { "foo", "bar", NULL };
+ check_split("foo bar ", " ", words);
+ }
+
+ {
+ const char *words[] = { "foo", "bar", NULL };
+ check_split(" foo bar ", " ", words);
+ }
+}
+
+ATF_TC(split_delims);
+ATF_TC_HEAD(split_delims, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the split function using "
+ "different delimiters");
+}
+ATF_TC_BODY(split_delims, tc)
+{
+
+ {
+ const char *words[] = { NULL };
+ check_split("", "/", words);
+ }
+
+ {
+ const char *words[] = { " ", NULL };
+ check_split(" ", "/", words);
+ }
+
+ {
+ const char *words[] = { " ", NULL };
+ check_split(" ", "/", words);
+ }
+
+ {
+ const char *words[] = { "a", "b", NULL };
+ check_split("a/b", "/", words);
+ }
+
+ {
+ const char *words[] = { "a", "bcd", "ef", NULL };
+ check_split("aLONGDELIMbcdLONGDELIMef", "LONGDELIM", words);
+ }
+}
+
+ATF_TC(to_bool);
+ATF_TC_HEAD(to_bool, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_text_to_bool function");
+}
+ATF_TC_BODY(to_bool, tc)
+{
+ bool b;
+
+ RE(atf_text_to_bool("true", &b)); ATF_REQUIRE(b);
+ RE(atf_text_to_bool("TRUE", &b)); ATF_REQUIRE(b);
+ RE(atf_text_to_bool("yes", &b)); ATF_REQUIRE(b);
+ RE(atf_text_to_bool("YES", &b)); ATF_REQUIRE(b);
+
+ RE(atf_text_to_bool("false", &b)); ATF_REQUIRE(!b);
+ RE(atf_text_to_bool("FALSE", &b)); ATF_REQUIRE(!b);
+ RE(atf_text_to_bool("no", &b)); ATF_REQUIRE(!b);
+ RE(atf_text_to_bool("NO", &b)); ATF_REQUIRE(!b);
+
+ b = false;
+ REQUIRE_ERROR(atf_text_to_bool("", &b));
+ ATF_REQUIRE(!b);
+ b = true;
+ REQUIRE_ERROR(atf_text_to_bool("", &b));
+ ATF_REQUIRE(b);
+
+ b = false;
+ REQUIRE_ERROR(atf_text_to_bool("tru", &b));
+ ATF_REQUIRE(!b);
+ b = true;
+ REQUIRE_ERROR(atf_text_to_bool("tru", &b));
+ ATF_REQUIRE(b);
+
+ b = false;
+ REQUIRE_ERROR(atf_text_to_bool("true2", &b));
+ ATF_REQUIRE(!b);
+ b = true;
+ REQUIRE_ERROR(atf_text_to_bool("true2", &b));
+ ATF_REQUIRE(b);
+
+ b = false;
+ REQUIRE_ERROR(atf_text_to_bool("fals", &b));
+ ATF_REQUIRE(!b);
+ b = true;
+ REQUIRE_ERROR(atf_text_to_bool("fals", &b));
+ ATF_REQUIRE(b);
+
+ b = false;
+ REQUIRE_ERROR(atf_text_to_bool("false2", &b));
+ ATF_REQUIRE(!b);
+ b = true;
+ REQUIRE_ERROR(atf_text_to_bool("false2", &b));
+ ATF_REQUIRE(b);
+}
+
+ATF_TC(to_long);
+ATF_TC_HEAD(to_long, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the atf_text_to_long function");
+}
+ATF_TC_BODY(to_long, tc)
+{
+ long l;
+
+ RE(atf_text_to_long("0", &l)); ATF_REQUIRE_EQ(l, 0);
+ RE(atf_text_to_long("-5", &l)); ATF_REQUIRE_EQ(l, -5);
+ RE(atf_text_to_long("5", &l)); ATF_REQUIRE_EQ(l, 5);
+ RE(atf_text_to_long("123456789", &l)); ATF_REQUIRE_EQ(l, 123456789);
+
+ l = 1212;
+ REQUIRE_ERROR(atf_text_to_long("", &l));
+ ATF_REQUIRE_EQ(l, 1212);
+ REQUIRE_ERROR(atf_text_to_long("foo", &l));
+ ATF_REQUIRE_EQ(l, 1212);
+ REQUIRE_ERROR(atf_text_to_long("1234x", &l));
+ ATF_REQUIRE_EQ(l, 1212);
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, for_each_word);
+ ATF_TP_ADD_TC(tp, format);
+ ATF_TP_ADD_TC(tp, format_ap);
+ ATF_TP_ADD_TC(tp, split);
+ ATF_TP_ADD_TC(tp, split_delims);
+ ATF_TP_ADD_TC(tp, to_bool);
+ ATF_TP_ADD_TC(tp, to_long);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/tp_main.c b/unit/atf-src/atf-c/detail/tp_main.c
new file mode 100644
index 0000000..229615b
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/tp_main.c
@@ -0,0 +1,615 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atf-c/detail/dynstr.h"
+#include "atf-c/detail/env.h"
+#include "atf-c/detail/fs.h"
+#include "atf-c/detail/map.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/error.h"
+#include "atf-c/tc.h"
+#include "atf-c/tp.h"
+#include "atf-c/utils.h"
+
+#if defined(HAVE_GNU_GETOPT)
+# define GETOPT_POSIX "+"
+#else
+# define GETOPT_POSIX ""
+#endif
+
+static const char *progname = NULL;
+
+/* This prototype is provided by macros.h during instantiation of the test
+ * program, so it can be kept private. Don't know if that's the best idea
+ * though. */
+int atf_tp_main(int, char **, atf_error_t (*)(atf_tp_t *));
+
+enum tc_part {
+ BODY,
+ CLEANUP,
+};
+
+/* ---------------------------------------------------------------------
+ * The "usage" and "user" error types.
+ * --------------------------------------------------------------------- */
+
+#define FREE_FORM_ERROR(name) \
+ struct name ## _error_data { \
+ char m_what[2048]; \
+ }; \
+ \
+ static \
+ void \
+ name ## _format(const atf_error_t err, char *buf, size_t buflen) \
+ { \
+ const struct name ## _error_data *data; \
+ \
+ PRE(atf_error_is(err, #name)); \
+ \
+ data = atf_error_data(err); \
+ snprintf(buf, buflen, "%s", data->m_what); \
+ } \
+ \
+ static \
+ atf_error_t \
+ name ## _error(const char *fmt, ...) \
+ { \
+ atf_error_t err; \
+ struct name ## _error_data data; \
+ va_list ap; \
+ \
+ va_start(ap, fmt); \
+ vsnprintf(data.m_what, sizeof(data.m_what), fmt, ap); \
+ va_end(ap); \
+ \
+ err = atf_error_new(#name, &data, sizeof(data), name ## _format); \
+ \
+ return err; \
+ }
+
+FREE_FORM_ERROR(usage);
+FREE_FORM_ERROR(user);
+
+/* ---------------------------------------------------------------------
+ * Printing functions.
+ * --------------------------------------------------------------------- */
+
+static
+void
+print_error(const atf_error_t err)
+{
+ char buf[4096];
+
+ PRE(atf_is_error(err));
+
+ atf_error_format(err, buf, sizeof(buf));
+ fprintf(stderr, "%s: ERROR: %s\n", progname, buf);
+
+ if (atf_error_is(err, "usage"))
+ fprintf(stderr, "%s: See atf-test-program(1) for usage details.\n",
+ progname);
+}
+
+static
+void
+print_warning(const char *message)
+{
+ fprintf(stderr, "%s: WARNING: %s\n", progname, message);
+}
+
+/* ---------------------------------------------------------------------
+ * Options handling.
+ * --------------------------------------------------------------------- */
+
+struct params {
+ bool m_do_list;
+ atf_fs_path_t m_srcdir;
+ char *m_tcname;
+ enum tc_part m_tcpart;
+ atf_fs_path_t m_resfile;
+ atf_map_t m_config;
+};
+
+static
+atf_error_t
+argv0_to_dir(const char *argv0, atf_fs_path_t *dir)
+{
+ atf_error_t err;
+ atf_fs_path_t temp;
+
+ err = atf_fs_path_init_fmt(&temp, "%s", argv0);
+ if (atf_is_error(err))
+ goto out;
+
+ err = atf_fs_path_branch_path(&temp, dir);
+
+ atf_fs_path_fini(&temp);
+out:
+ return err;
+}
+
+static
+atf_error_t
+params_init(struct params *p, const char *argv0)
+{
+ atf_error_t err;
+
+ p->m_do_list = false;
+ p->m_tcname = NULL;
+ p->m_tcpart = BODY;
+
+ err = argv0_to_dir(argv0, &p->m_srcdir);
+ if (atf_is_error(err))
+ return err;
+
+ err = atf_fs_path_init_fmt(&p->m_resfile, "/dev/stdout");
+ if (atf_is_error(err)) {
+ atf_fs_path_fini(&p->m_srcdir);
+ return err;
+ }
+
+ err = atf_map_init(&p->m_config);
+ if (atf_is_error(err)) {
+ atf_fs_path_fini(&p->m_resfile);
+ atf_fs_path_fini(&p->m_srcdir);
+ return err;
+ }
+
+ return err;
+}
+
+static
+void
+params_fini(struct params *p)
+{
+ atf_map_fini(&p->m_config);
+ atf_fs_path_fini(&p->m_resfile);
+ atf_fs_path_fini(&p->m_srcdir);
+ if (p->m_tcname != NULL)
+ free(p->m_tcname);
+}
+
+static
+atf_error_t
+parse_vflag(char *arg, atf_map_t *config)
+{
+ atf_error_t err;
+ char *split;
+
+ split = strchr(arg, '=');
+ if (split == NULL) {
+ err = usage_error("-v requires an argument of the form var=value");
+ goto out;
+ }
+
+ *split = '\0';
+ split++;
+
+ err = atf_map_insert(config, arg, split, false);
+
+out:
+ return err;
+}
+
+static
+atf_error_t
+replace_path_param(atf_fs_path_t *param, const char *value)
+{
+ atf_error_t err;
+ atf_fs_path_t temp;
+
+ err = atf_fs_path_init_fmt(&temp, "%s", value);
+ if (!atf_is_error(err)) {
+ atf_fs_path_fini(param);
+ *param = temp;
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * Test case listing.
+ * --------------------------------------------------------------------- */
+
+static
+void
+list_tcs(const atf_tp_t *tp)
+{
+ const atf_tc_t *const *tcs;
+ const atf_tc_t *const *tcsptr;
+
+ printf("Content-Type: application/X-atf-tp; version=\"1\"\n\n");
+
+ tcs = atf_tp_get_tcs(tp);
+ INV(tcs != NULL); /* Should be checked. */
+ for (tcsptr = tcs; *tcsptr != NULL; tcsptr++) {
+ const atf_tc_t *tc = *tcsptr;
+ char **vars = atf_tc_get_md_vars(tc);
+ char **ptr;
+
+ INV(vars != NULL); /* Should be checked. */
+
+ if (tcsptr != tcs) /* Not first. */
+ printf("\n");
+
+ for (ptr = vars; *ptr != NULL; ptr += 2) {
+ if (strcmp(*ptr, "ident") == 0) {
+ printf("ident: %s\n", *(ptr + 1));
+ break;
+ }
+ }
+
+ for (ptr = vars; *ptr != NULL; ptr += 2) {
+ if (strcmp(*ptr, "ident") != 0) {
+ printf("%s: %s\n", *ptr, *(ptr + 1));
+ }
+ }
+
+ atf_utils_free_charpp(vars);
+ }
+#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+ free(UNCONST(tcs));
+#undef UNCONST
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+static
+atf_error_t
+handle_tcarg(const char *tcarg, char **tcname, enum tc_part *tcpart)
+{
+ atf_error_t err;
+
+ err = atf_no_error();
+
+ *tcname = strdup(tcarg);
+ if (*tcname == NULL) {
+ err = atf_no_memory_error();
+ goto out;
+ }
+
+ char *delim = strchr(*tcname, ':');
+ if (delim != NULL) {
+ *delim = '\0';
+
+ delim++;
+ if (strcmp(delim, "body") == 0) {
+ *tcpart = BODY;
+ } else if (strcmp(delim, "cleanup") == 0) {
+ *tcpart = CLEANUP;
+ } else {
+ err = usage_error("Invalid test case part `%s'", delim);
+ goto out;
+ }
+ }
+
+out:
+ return err;
+}
+
+static
+atf_error_t
+process_params(int argc, char **argv, struct params *p)
+{
+ atf_error_t err;
+ int ch;
+ int old_opterr;
+
+ err = params_init(p, argv[0]);
+ if (atf_is_error(err))
+ goto out;
+
+ old_opterr = opterr;
+ opterr = 0;
+ while (!atf_is_error(err) &&
+ (ch = getopt(argc, argv, GETOPT_POSIX ":lr:s:v:")) != -1) {
+ switch (ch) {
+ case 'l':
+ p->m_do_list = true;
+ break;
+
+ case 'r':
+ err = replace_path_param(&p->m_resfile, optarg);
+ break;
+
+ case 's':
+ err = replace_path_param(&p->m_srcdir, optarg);
+ break;
+
+ case 'v':
+ err = parse_vflag(optarg, &p->m_config);
+ break;
+
+ case ':':
+ err = usage_error("Option -%c requires an argument.", optopt);
+ break;
+
+ case '?':
+ default:
+ err = usage_error("Unknown option -%c.", optopt);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Clear getopt state just in case the test wants to use it. */
+ opterr = old_opterr;
+ optind = 1;
+#if defined(HAVE_OPTRESET)
+ optreset = 1;
+#endif
+
+ if (!atf_is_error(err)) {
+ if (p->m_do_list) {
+ if (argc > 0)
+ err = usage_error("Cannot provide test case names with -l");
+ } else {
+ if (argc == 0)
+ err = usage_error("Must provide a test case name");
+ else if (argc == 1)
+ err = handle_tcarg(argv[0], &p->m_tcname, &p->m_tcpart);
+ else if (argc > 1) {
+ err = usage_error("Cannot provide more than one test case "
+ "name");
+ }
+ }
+ }
+
+ if (atf_is_error(err))
+ params_fini(p);
+
+out:
+ return err;
+}
+
+static
+atf_error_t
+srcdir_strip_libtool(atf_fs_path_t *srcdir)
+{
+ atf_error_t err;
+ atf_fs_path_t parent;
+
+ err = atf_fs_path_branch_path(srcdir, &parent);
+ if (atf_is_error(err))
+ goto out;
+
+ atf_fs_path_fini(srcdir);
+ *srcdir = parent;
+
+ INV(!atf_is_error(err));
+out:
+ return err;
+}
+
+static
+atf_error_t
+handle_srcdir(struct params *p)
+{
+ atf_error_t err;
+ atf_dynstr_t leafname;
+ atf_fs_path_t exe, srcdir;
+ bool b;
+
+ err = atf_fs_path_copy(&srcdir, &p->m_srcdir);
+ if (atf_is_error(err))
+ goto out;
+
+ if (!atf_fs_path_is_absolute(&srcdir)) {
+ atf_fs_path_t srcdirabs;
+
+ err = atf_fs_path_to_absolute(&srcdir, &srcdirabs);
+ if (atf_is_error(err))
+ goto out_srcdir;
+
+ atf_fs_path_fini(&srcdir);
+ srcdir = srcdirabs;
+ }
+
+ err = atf_fs_path_leaf_name(&srcdir, &leafname);
+ if (atf_is_error(err))
+ goto out_srcdir;
+ else {
+ const bool libs = atf_equal_dynstr_cstring(&leafname, ".libs");
+ atf_dynstr_fini(&leafname);
+
+ if (libs) {
+ err = srcdir_strip_libtool(&srcdir);
+ if (atf_is_error(err))
+ goto out;
+ }
+ }
+
+ err = atf_fs_path_copy(&exe, &srcdir);
+ if (atf_is_error(err))
+ goto out_srcdir;
+
+ err = atf_fs_path_append_fmt(&exe, "%s", progname);
+ if (atf_is_error(err))
+ goto out_exe;
+
+ err = atf_fs_exists(&exe, &b);
+ if (!atf_is_error(err)) {
+ if (b) {
+ err = atf_map_insert(&p->m_config, "srcdir",
+ strdup(atf_fs_path_cstring(&srcdir)), true);
+ } else {
+ err = user_error("Cannot find the test program in the source "
+ "directory `%s'", atf_fs_path_cstring(&srcdir));
+ }
+ }
+
+out_exe:
+ atf_fs_path_fini(&exe);
+out_srcdir:
+ atf_fs_path_fini(&srcdir);
+out:
+ return err;
+}
+
+static
+atf_error_t
+run_tc(const atf_tp_t *tp, struct params *p, int *exitcode)
+{
+ atf_error_t err;
+
+ err = atf_no_error();
+
+ if (!atf_tp_has_tc(tp, p->m_tcname)) {
+ err = usage_error("Unknown test case `%s'", p->m_tcname);
+ goto out;
+ }
+
+ if (!atf_env_has("__RUNNING_INSIDE_ATF_RUN") || strcmp(atf_env_get(
+ "__RUNNING_INSIDE_ATF_RUN"), "internal-yes-value") != 0)
+ {
+ print_warning("Running test cases outside of kyua(1) is unsupported");
+ print_warning("No isolation nor timeout control is being applied; you "
+ "may get unexpected failures; see atf-test-case(4)");
+ }
+
+ switch (p->m_tcpart) {
+ case BODY:
+ err = atf_tp_run(tp, p->m_tcname, atf_fs_path_cstring(&p->m_resfile));
+ if (atf_is_error(err)) {
+ /* TODO: Handle error */
+ *exitcode = EXIT_FAILURE;
+ atf_error_free(err);
+ } else {
+ *exitcode = EXIT_SUCCESS;
+ }
+
+ break;
+
+ case CLEANUP:
+ err = atf_tp_cleanup(tp, p->m_tcname);
+ if (atf_is_error(err)) {
+ /* TODO: Handle error */
+ *exitcode = EXIT_FAILURE;
+ atf_error_free(err);
+ } else {
+ *exitcode = EXIT_SUCCESS;
+ }
+
+ break;
+
+ default:
+ UNREACHABLE;
+ }
+
+ INV(!atf_is_error(err));
+out:
+ return err;
+}
+
+static
+atf_error_t
+controlled_main(int argc, char **argv,
+ atf_error_t (*add_tcs_hook)(atf_tp_t *),
+ int *exitcode)
+{
+ atf_error_t err;
+ struct params p;
+ atf_tp_t tp;
+ char **raw_config;
+
+ err = process_params(argc, argv, &p);
+ if (atf_is_error(err))
+ goto out;
+
+ err = handle_srcdir(&p);
+ if (atf_is_error(err))
+ goto out_p;
+
+ raw_config = atf_map_to_charpp(&p.m_config);
+ if (raw_config == NULL) {
+ err = atf_no_memory_error();
+ goto out_p;
+ }
+ err = atf_tp_init(&tp, (const char* const*)raw_config);
+ atf_utils_free_charpp(raw_config);
+ if (atf_is_error(err))
+ goto out_p;
+
+ err = add_tcs_hook(&tp);
+ if (atf_is_error(err))
+ goto out_tp;
+
+ if (p.m_do_list) {
+ list_tcs(&tp);
+ INV(!atf_is_error(err));
+ *exitcode = EXIT_SUCCESS;
+ } else {
+ err = run_tc(&tp, &p, exitcode);
+ }
+
+out_tp:
+ atf_tp_fini(&tp);
+out_p:
+ params_fini(&p);
+out:
+ return err;
+}
+
+int
+atf_tp_main(int argc, char **argv, atf_error_t (*add_tcs_hook)(atf_tp_t *))
+{
+ atf_error_t err;
+ int exitcode;
+
+ progname = strrchr(argv[0], '/');
+ if (progname == NULL)
+ progname = argv[0];
+ else
+ progname++;
+
+ /* Libtool workaround: if running from within the source tree (binaries
+ * that are not installed yet), skip the "lt-" prefix added to files in
+ * the ".libs" directory to show the real (not temporary) name. */
+ if (strncmp(progname, "lt-", 3) == 0)
+ progname += 3;
+
+ exitcode = EXIT_FAILURE; /* Silence GCC warning. */
+ err = controlled_main(argc, argv, add_tcs_hook, &exitcode);
+ if (atf_is_error(err)) {
+ print_error(err);
+ atf_error_free(err);
+ exitcode = EXIT_FAILURE;
+ }
+
+ return exitcode;
+}
diff --git a/unit/atf-src/atf-c/detail/user.c b/unit/atf-src/atf-c/detail/user.c
new file mode 100644
index 0000000..bbb4ded
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/user.c
@@ -0,0 +1,75 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/user.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <unistd.h>
+
+#include "atf-c/detail/sanity.h"
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+uid_t
+atf_user_euid(void)
+{
+ return geteuid();
+}
+
+bool
+atf_user_is_member_of_group(gid_t gid)
+{
+ static gid_t groups[NGROUPS_MAX];
+ static int ngroups = -1;
+ bool found;
+ int i;
+
+ if (ngroups == -1) {
+ ngroups = getgroups(NGROUPS_MAX, groups);
+ INV(ngroups >= 0);
+ }
+
+ found = false;
+ for (i = 0; !found && i < ngroups; i++)
+ if (groups[i] == gid)
+ found = true;
+ return found;
+}
+
+bool
+atf_user_is_root(void)
+{
+ return geteuid() == 0;
+}
+
+bool
+atf_user_is_unprivileged(void)
+{
+ return geteuid() != 0;
+}
diff --git a/unit/atf-src/atf-c/detail/user.h b/unit/atf-src/atf-c/detail/user.h
new file mode 100644
index 0000000..14a3e96
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/user.h
@@ -0,0 +1,45 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_DETAIL_USER_H)
+#define ATF_C_DETAIL_USER_H
+
+#include <sys/types.h>
+
+#include <stdbool.h>
+
+/* TODO: Would be nice to have an atf_user_t type and transform all of
+ * the functions below to methods. */
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+uid_t atf_user_euid(void);
+bool atf_user_is_member_of_group(gid_t);
+bool atf_user_is_root(void);
+bool atf_user_is_unprivileged(void);
+
+#endif /* !defined(ATF_C_DETAIL_USER_H) */
diff --git a/unit/atf-src/atf-c/detail/user_test.c b/unit/atf-src/atf-c/detail/user_test.c
new file mode 100644
index 0000000..6a0f6e1
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/user_test.c
@@ -0,0 +1,146 @@
+/* Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/detail/user.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+
+/* ---------------------------------------------------------------------
+ * Test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(euid);
+ATF_TC_HEAD(euid, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_user_euid function");
+}
+ATF_TC_BODY(euid, tc)
+{
+ ATF_REQUIRE_EQ(atf_user_euid(), geteuid());
+}
+
+ATF_TC(is_member_of_group);
+ATF_TC_HEAD(is_member_of_group, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_user_is_member_of_group "
+ "function");
+}
+ATF_TC_BODY(is_member_of_group, tc)
+{
+ gid_t gids[NGROUPS_MAX];
+ gid_t g, maxgid;
+ int ngids;
+ const gid_t maxgid_limit = 1 << 16;
+
+ {
+ int i;
+
+ ngids = getgroups(NGROUPS_MAX, gids);
+ if (ngids == -1)
+ atf_tc_fail("Call to getgroups failed");
+ maxgid = 0;
+ for (i = 0; i < ngids; i++) {
+ printf("User group %d is %u\n", i, gids[i]);
+ if (maxgid < gids[i])
+ maxgid = gids[i];
+ }
+ printf("User belongs to %d groups\n", ngids);
+ printf("Last GID is %u\n", maxgid);
+ }
+
+ if (maxgid > maxgid_limit) {
+ printf("Test truncated from %u groups to %u to keep the run time "
+ "reasonable enough\n", maxgid, maxgid_limit);
+ maxgid = maxgid_limit;
+ }
+
+ for (g = 0; g < maxgid; g++) {
+ bool found = false;
+ int i;
+
+ for (i = 0; !found && i < ngids; i++) {
+ if (gids[i] == g)
+ found = true;
+ }
+
+ if (found) {
+ printf("Checking if user belongs to group %d\n", g);
+ ATF_REQUIRE(atf_user_is_member_of_group(g));
+ } else {
+ printf("Checking if user does not belong to group %d\n", g);
+ ATF_REQUIRE(!atf_user_is_member_of_group(g));
+ }
+ }
+}
+
+ATF_TC(is_root);
+ATF_TC_HEAD(is_root, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_user_is_root function");
+}
+ATF_TC_BODY(is_root, tc)
+{
+ if (geteuid() == 0)
+ ATF_REQUIRE(atf_user_is_root());
+ else
+ ATF_REQUIRE(!atf_user_is_root());
+}
+
+ATF_TC(is_unprivileged);
+ATF_TC_HEAD(is_unprivileged, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_user_is_unprivileged "
+ "function");
+}
+ATF_TC_BODY(is_unprivileged, tc)
+{
+ if (geteuid() != 0)
+ ATF_REQUIRE(atf_user_is_unprivileged());
+ else
+ ATF_REQUIRE(!atf_user_is_unprivileged());
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, euid);
+ ATF_TP_ADD_TC(tp, is_member_of_group);
+ ATF_TP_ADD_TC(tp, is_root);
+ ATF_TP_ADD_TC(tp, is_unprivileged);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/detail/version_helper.c b/unit/atf-src/atf-c/detail/version_helper.c
new file mode 100644
index 0000000..fd48466
--- /dev/null
+++ b/unit/atf-src/atf-c/detail/version_helper.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014 Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither the name of Google Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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.
+ */
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(void)
+{
+ printf("%s\n", PACKAGE_VERSION);
+ return EXIT_SUCCESS;
+}
diff --git a/unit/atf-src/atf-c/error.c b/unit/atf-src/atf-c/error.c
new file mode 100644
index 0000000..071daf6
--- /dev/null
+++ b/unit/atf-src/atf-c/error.c
@@ -0,0 +1,263 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/error.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "atf-c/detail/sanity.h"
+
+/* Theoretically, there can only be a single error intance at any given
+ * point in time, because errors are raised at one point and must be
+ * handled immediately. If another error has to be raised during the
+ * handling process, something else has to be done with the previous
+ * error.
+ *
+ * This is per-thread information and will break threaded tests, but we
+ * currently do not have any threading support; therefore, this is fine. */
+static bool error_on_flight = false;
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+void
+error_format(const atf_error_t err, char *buf, size_t buflen)
+{
+ PRE(err != NULL);
+ snprintf(buf, buflen, "Error '%s'", err->m_type);
+}
+
+static
+bool
+error_init(atf_error_t err, const char *type, void *data, size_t datalen,
+ void (*format)(const atf_error_t, char *, size_t))
+{
+ bool ok;
+
+ PRE(data != NULL || datalen == 0);
+ PRE(datalen != 0 || data == NULL);
+
+ err->m_free = false;
+ err->m_type = type;
+ err->m_format = (format == NULL) ? error_format : format;
+
+ ok = true;
+ if (data == NULL) {
+ err->m_data = NULL;
+ } else {
+ err->m_data = malloc(datalen);
+ if (err->m_data == NULL) {
+ ok = false;
+ } else
+ memcpy(err->m_data, data, datalen);
+ }
+
+ return ok;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_error" type.
+ * --------------------------------------------------------------------- */
+
+atf_error_t
+atf_error_new(const char *type, void *data, size_t datalen,
+ void (*format)(const atf_error_t, char *, size_t))
+{
+ atf_error_t err;
+
+ PRE(!error_on_flight);
+ PRE(data != NULL || datalen == 0);
+ PRE(datalen != 0 || data == NULL);
+
+ err = malloc(sizeof(*err));
+ if (err == NULL)
+ err = atf_no_memory_error();
+ else {
+ if (!error_init(err, type, data, datalen, format)) {
+ free(err);
+ err = atf_no_memory_error();
+ } else {
+ err->m_free = true;
+ error_on_flight = true;
+ }
+ }
+
+ INV(err != NULL);
+ POST(error_on_flight);
+ return err;
+}
+
+void
+atf_error_free(atf_error_t err)
+{
+ bool freeit;
+
+ PRE(error_on_flight);
+ PRE(err != NULL);
+
+ freeit = err->m_free;
+
+ if (err->m_data != NULL)
+ free(err->m_data);
+
+ if (freeit)
+ free(err);
+
+ error_on_flight = false;
+}
+
+atf_error_t
+atf_no_error(void)
+{
+ return NULL;
+}
+
+bool
+atf_is_error(const atf_error_t err)
+{
+ return err != NULL;
+}
+
+bool
+atf_error_is(const atf_error_t err, const char *type)
+{
+ PRE(err != NULL);
+
+ return strcmp(err->m_type, type) == 0;
+}
+
+const void *
+atf_error_data(const atf_error_t err)
+{
+ PRE(err != NULL);
+
+ return err->m_data;
+}
+
+void
+atf_error_format(const atf_error_t err, char *buf, size_t buflen)
+{
+ PRE(err != NULL);
+ err->m_format(err, buf, buflen);
+}
+
+/* ---------------------------------------------------------------------
+ * Common error types.
+ * --------------------------------------------------------------------- */
+
+/*
+ * The "libc" error.
+ */
+
+struct atf_libc_error_data {
+ int m_errno;
+ char m_what[4096];
+};
+typedef struct atf_libc_error_data atf_libc_error_data_t;
+
+static
+void
+libc_format(const atf_error_t err, char *buf, size_t buflen)
+{
+ const atf_libc_error_data_t *data;
+
+ PRE(atf_error_is(err, "libc"));
+
+ data = atf_error_data(err);
+ snprintf(buf, buflen, "%s: %s", data->m_what, strerror(data->m_errno));
+}
+
+atf_error_t
+atf_libc_error(int syserrno, const char *fmt, ...)
+{
+ atf_error_t err;
+ atf_libc_error_data_t data;
+ va_list ap;
+
+ data.m_errno = syserrno;
+ va_start(ap, fmt);
+ vsnprintf(data.m_what, sizeof(data.m_what), fmt, ap);
+ va_end(ap);
+
+ err = atf_error_new("libc", &data, sizeof(data), libc_format);
+
+ return err;
+}
+
+int
+atf_libc_error_code(const atf_error_t err)
+{
+ const struct atf_libc_error_data *data;
+
+ PRE(atf_error_is(err, "libc"));
+
+ data = atf_error_data(err);
+
+ return data->m_errno;
+}
+
+const char *
+atf_libc_error_msg(const atf_error_t err)
+{
+ const struct atf_libc_error_data *data;
+
+ PRE(atf_error_is(err, "libc"));
+
+ data = atf_error_data(err);
+
+ return data->m_what;
+}
+
+/*
+ * The "no_memory" error.
+ */
+
+static struct atf_error no_memory_error;
+
+static
+void
+no_memory_format(const atf_error_t err, char *buf, size_t buflen)
+{
+ PRE(atf_error_is(err, "no_memory"));
+
+ snprintf(buf, buflen, "Not enough memory");
+}
+
+atf_error_t
+atf_no_memory_error(void)
+{
+ PRE(!error_on_flight);
+
+ error_init(&no_memory_error, "no_memory", NULL, 0,
+ no_memory_format);
+
+ error_on_flight = true;
+ return &no_memory_error;
+}
diff --git a/unit/atf-src/atf-c/error.h b/unit/atf-src/atf-c/error.h
new file mode 100644
index 0000000..260c0d0
--- /dev/null
+++ b/unit/atf-src/atf-c/error.h
@@ -0,0 +1,67 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_ERROR_H)
+#define ATF_C_ERROR_H
+
+#include <atf-c/error_fwd.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+
+/* ---------------------------------------------------------------------
+ * The "atf_error" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_error {
+ bool m_free;
+ const char *m_type;
+ void *m_data;
+
+ void (*m_format)(struct atf_error *, char *, size_t);
+};
+
+atf_error_t atf_error_new(const char *, void *, size_t,
+ void (*)(const atf_error_t, char *, size_t));
+void atf_error_free(atf_error_t);
+
+atf_error_t atf_no_error(void);
+bool atf_is_error(const atf_error_t);
+
+bool atf_error_is(const atf_error_t, const char *);
+const void *atf_error_data(const atf_error_t);
+void atf_error_format(const atf_error_t, char *, size_t);
+
+/* ---------------------------------------------------------------------
+ * Common error types.
+ * --------------------------------------------------------------------- */
+
+atf_error_t atf_libc_error(int, const char *, ...);
+int atf_libc_error_code(const atf_error_t);
+const char *atf_libc_error_msg(const atf_error_t);
+
+atf_error_t atf_no_memory_error(void);
+
+#endif /* !defined(ATF_C_ERROR_H) */
diff --git a/unit/atf-src/atf-c/error_fwd.h b/unit/atf-src/atf-c/error_fwd.h
new file mode 100644
index 0000000..d73e164
--- /dev/null
+++ b/unit/atf-src/atf-c/error_fwd.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_ERROR_FWD_H)
+#define ATF_C_ERROR_FWD_H
+
+/* ---------------------------------------------------------------------
+ * The "atf_error" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_error;
+typedef struct atf_error *atf_error_t;
+
+#endif /* !defined(ATF_C_ERROR_FWD_H) */
diff --git a/unit/atf-src/atf-c/error_test.c b/unit/atf-src/atf-c/error_test.c
new file mode 100644
index 0000000..8bfa124
--- /dev/null
+++ b/unit/atf-src/atf-c/error_test.c
@@ -0,0 +1,297 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/error.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "atf-c/defs.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+void
+test_format(const atf_error_t err ATF_DEFS_ATTRIBUTE_UNUSED,
+ char *buf, size_t buflen)
+{
+ snprintf(buf, buflen, "Test formatting function");
+}
+
+/* ---------------------------------------------------------------------
+ * Tests for the "atf_error" type.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(error_new);
+ATF_TC_HEAD(error_new, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of an error "
+ "object");
+}
+ATF_TC_BODY(error_new, tc)
+{
+ atf_error_t err;
+ int data;
+
+ err = atf_error_new("test_error", NULL, 0, NULL);
+ ATF_REQUIRE(atf_error_is(err, "test_error"));
+ ATF_REQUIRE(!atf_error_is(err, "unknown_error"));
+ ATF_REQUIRE(atf_error_data(err) == NULL);
+ atf_error_free(err);
+
+ data = 5;
+ err = atf_error_new("test_data_error", &data, sizeof(data), NULL);
+ ATF_REQUIRE(atf_error_is(err, "test_data_error"));
+ ATF_REQUIRE(!atf_error_is(err, "unknown_error"));
+ ATF_REQUIRE(atf_error_data(err) != NULL);
+ ATF_REQUIRE_EQ(*((const int *)atf_error_data(err)), 5);
+ atf_error_free(err);
+}
+
+ATF_TC(error_new_wo_memory);
+ATF_TC_HEAD(error_new_wo_memory, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that an unavailable memory error "
+ "raised when constructing an error object "
+ "is properly converted to the no_memory "
+ "static error type");
+}
+ATF_TC_BODY(error_new_wo_memory, tc)
+{
+ atf_error_t err;
+ void *invalid;
+
+ invalid = (void *)1;
+
+ err = atf_error_new("test_error", invalid, SIZE_MAX, NULL);
+ ATF_REQUIRE(atf_error_is(err, "no_memory"));
+ ATF_REQUIRE(atf_error_data(err) == NULL);
+ atf_error_free(err);
+}
+
+ATF_TC(no_error);
+ATF_TC_HEAD(no_error, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks that constructing a non-error "
+ "object works");
+}
+ATF_TC_BODY(no_error, tc)
+{
+ atf_error_t err;
+
+ err = atf_no_error();
+ ATF_REQUIRE(!atf_is_error(err));
+}
+
+ATF_TC(is_error);
+ATF_TC_HEAD(is_error, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the is_error method to determine "
+ "if an error object holds success or an error");
+}
+ATF_TC_BODY(is_error, tc)
+{
+ atf_error_t err;
+
+ err = atf_no_error();
+ ATF_REQUIRE(!atf_is_error(err));
+
+ err = atf_error_new("test_error", NULL, 0, NULL);
+ ATF_REQUIRE(atf_is_error(err));
+ atf_error_free(err);
+}
+
+ATF_TC(format);
+ATF_TC_HEAD(format, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the default formatting function "
+ "and the ability to change it");
+}
+ATF_TC_BODY(format, tc)
+{
+ atf_error_t err;
+ char buf[1024];
+
+ printf("Testing default formatting function\n");
+ err = atf_error_new("test_error", NULL, 0, NULL);
+ atf_error_format(err, buf, sizeof(buf));
+ printf("Error string is: %s\n", buf);
+ ATF_REQUIRE(strcmp(buf, "Error 'test_error'") == 0);
+ atf_error_free(err);
+
+ printf("Testing custom formatting function\n");
+ err = atf_error_new("test_error", NULL, 0, test_format);
+ atf_error_format(err, buf, sizeof(buf));
+ printf("Error string is: %s\n", buf);
+ ATF_REQUIRE(strcmp(buf, "Test formatting function") == 0);
+ atf_error_free(err);
+}
+
+/* ---------------------------------------------------------------------
+ * Tests for the "libc" error.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(libc_new);
+ATF_TC_HEAD(libc_new, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of libc errors");
+}
+ATF_TC_BODY(libc_new, tc)
+{
+ atf_error_t err;
+
+ err = atf_libc_error(ENOMEM, "Test message 1");
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_REQUIRE_EQ(atf_libc_error_code(err), ENOMEM);
+ ATF_REQUIRE(strcmp(atf_libc_error_msg(err), "Test message 1") == 0);
+ atf_error_free(err);
+
+ err = atf_libc_error(EPERM, "%s message %d", "Test", 2);
+ ATF_REQUIRE(atf_error_is(err, "libc"));
+ ATF_REQUIRE_EQ(atf_libc_error_code(err), EPERM);
+ ATF_REQUIRE(strcmp(atf_libc_error_msg(err), "Test message 2") == 0);
+ atf_error_free(err);
+}
+
+ATF_TC(libc_format);
+ATF_TC_HEAD(libc_format, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the formatting of libc errors");
+}
+ATF_TC_BODY(libc_format, tc)
+{
+ atf_error_t err;
+ char buf[1024];
+
+ err = atf_libc_error(ENOMEM, "Test message 1");
+ atf_error_format(err, buf, sizeof(buf));
+ ATF_REQUIRE(strstr(buf, strerror(ENOMEM)) != NULL);
+ ATF_REQUIRE(strstr(buf, "Test message 1") != NULL);
+ atf_error_free(err);
+
+ err = atf_libc_error(EPERM, "Test message 2");
+ atf_error_format(err, buf, sizeof(buf));
+ ATF_REQUIRE(strstr(buf, strerror(EPERM)) != NULL);
+ ATF_REQUIRE(strstr(buf, "Test message 2") != NULL);
+ atf_error_free(err);
+
+ err = atf_libc_error(EPERM, "%s message %d", "Test", 3);
+ atf_error_format(err, buf, sizeof(buf));
+ ATF_REQUIRE(strstr(buf, strerror(EPERM)) != NULL);
+ ATF_REQUIRE(strstr(buf, "Test message 3") != NULL);
+ atf_error_free(err);
+}
+
+/* ---------------------------------------------------------------------
+ * Tests for the "no_memory" error.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(no_memory_new);
+ATF_TC_HEAD(no_memory_new, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of no_memory "
+ "errors");
+}
+ATF_TC_BODY(no_memory_new, tc)
+{
+ atf_error_t err;
+
+ err = atf_no_memory_error();
+ ATF_REQUIRE(atf_error_is(err, "no_memory"));
+ ATF_REQUIRE(atf_error_data(err) == NULL);
+ atf_error_free(err);
+}
+
+ATF_TC(no_memory_format);
+ATF_TC_HEAD(no_memory_format, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the formatting of no_memory "
+ "errors");
+}
+ATF_TC_BODY(no_memory_format, tc)
+{
+ atf_error_t err;
+ char buf[1024];
+
+ err = atf_no_memory_error();
+ atf_error_format(err, buf, sizeof(buf));
+ ATF_REQUIRE(strcmp(buf, "Not enough memory") == 0);
+ atf_error_free(err);
+}
+
+ATF_TC(no_memory_twice);
+ATF_TC_HEAD(no_memory_twice, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks the construction of no_memory "
+ "errors multiple times, as this error is initialized "
+ "statically");
+}
+ATF_TC_BODY(no_memory_twice, tc)
+{
+ {
+ atf_error_t err = atf_no_memory_error();
+ ATF_REQUIRE(atf_error_is(err, "no_memory"));
+ ATF_REQUIRE(atf_error_data(err) == NULL);
+ atf_error_free(err);
+ }
+
+ {
+ atf_error_t err = atf_no_memory_error();
+ ATF_REQUIRE(atf_error_is(err, "no_memory"));
+ ATF_REQUIRE(atf_error_data(err) == NULL);
+ atf_error_free(err);
+ }
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Add the tests for the "atf_error" type. */
+ ATF_TP_ADD_TC(tp, error_new);
+ ATF_TP_ADD_TC(tp, error_new_wo_memory);
+ ATF_TP_ADD_TC(tp, no_error);
+ ATF_TP_ADD_TC(tp, is_error);
+ ATF_TP_ADD_TC(tp, format);
+
+ /* Add the tests for the "libc" error. */
+ ATF_TP_ADD_TC(tp, libc_new);
+ ATF_TP_ADD_TC(tp, libc_format);
+
+ /* Add the tests for the "no_memory" error. */
+ ATF_TP_ADD_TC(tp, no_memory_new);
+ ATF_TP_ADD_TC(tp, no_memory_format);
+ ATF_TP_ADD_TC(tp, no_memory_twice);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/h_build.h b/unit/atf-src/atf-c/h_build.h
new file mode 100644
index 0000000..c5ad1c7
--- /dev/null
+++ b/unit/atf-src/atf-c/h_build.h
@@ -0,0 +1,410 @@
+/* Copyright (c) 2009 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if defined(ATF_C_H_BUILD_H)
+# error "Cannot include h_build.h more than once."
+#else
+# define ATF_C_H_BUILD_H
+#endif
+
+/* ---------------------------------------------------------------------
+ * Test case data.
+ * --------------------------------------------------------------------- */
+
+static struct c_o_test {
+ const char *msg;
+ const char *cc;
+ const char *cflags;
+ const char *cppflags;
+ const char *sfile;
+ const char *ofile;
+ bool hasoptargs;
+ const char *const optargs[16];
+ const char *const expargv[16];
+} c_o_tests[] = {
+ {
+ "No flags",
+ "cc",
+ "",
+ "",
+ "test.c",
+ "test.o",
+ false,
+ {
+ NULL
+ },
+ {
+ "cc", "-o", "test.o", "-c", "test.c", NULL
+ },
+ },
+
+ {
+ "Multi-word program name",
+ "cc -foo",
+ "",
+ "",
+ "test.c",
+ "test.o",
+ false,
+ {
+ NULL
+ },
+ {
+ "cc", "-foo", "-o", "test.o", "-c", "test.c", NULL
+ },
+ },
+
+ {
+ "Some cflags",
+ "cc",
+ "-f1 -f2 -f3 -f4-f5",
+ "",
+ "test.c",
+ "test.o",
+ false,
+ {
+ NULL
+ },
+ {
+ "cc", "-f1", "-f2", "-f3", "-f4-f5", "-o", "test.o",
+ "-c", "test.c", NULL
+ },
+ },
+
+ {
+ "Some cppflags",
+ "cc",
+ "",
+ "-f1 -f2 -f3 -f4-f5",
+ "test.c",
+ "test.o",
+ false,
+ {
+ NULL
+ },
+ {
+ "cc", "-f1", "-f2", "-f3", "-f4-f5", "-o", "test.o",
+ "-c", "test.c", NULL
+ },
+ },
+
+ {
+ "Some cflags and cppflags",
+ "cc",
+ "-f2",
+ "-f1",
+ "test.c",
+ "test.o",
+ false,
+ {
+ NULL
+ },
+ {
+ "cc", "-f1", "-f2", "-o", "test.o", "-c", "test.c", NULL
+ },
+ },
+
+ {
+ "Some optional arguments",
+ "cc",
+ "",
+ "",
+ "test.c",
+ "test.o",
+ true,
+ {
+ "-o1", "-o2", NULL
+ },
+ {
+ "cc", "-o1", "-o2", "-o", "test.o", "-c", "test.c", NULL
+ },
+ },
+
+ {
+ "Some cflags, cppflags and optional arguments",
+ "cc",
+ "-f2",
+ "-f1",
+ "test.c",
+ "test.o",
+ true,
+ {
+ "-o1", "-o2", NULL
+ },
+ {
+ "cc", "-f1", "-f2", "-o1", "-o2", "-o", "test.o",
+ "-c", "test.c", NULL
+ },
+ },
+
+ {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ false,
+ { NULL },
+ { NULL },
+ },
+};
+
+static struct cpp_test {
+ const char *msg;
+ const char *cpp;
+ const char *cppflags;
+ const char *sfile;
+ const char *ofile;
+ bool hasoptargs;
+ const char *const optargs[16];
+ const char *const expargv[16];
+} cpp_tests[] = {
+ {
+ "No flags",
+ "cpp",
+ "",
+ "test.c",
+ "test.out",
+ false,
+ {
+ NULL
+ },
+ {
+ "cpp", "-o", "test.out", "test.c", NULL
+ },
+ },
+
+ {
+ "Multi-word program name",
+ "cpp -foo",
+ "",
+ "test.c",
+ "test.out",
+ false,
+ {
+ NULL
+ },
+ {
+ "cpp", "-foo", "-o", "test.out", "test.c", NULL
+ },
+ },
+
+ {
+ "Some cppflags",
+ "cpp",
+ "-f1 -f2 -f3 -f4-f5",
+ "test.c",
+ "test.out",
+ false,
+ {
+ NULL
+ },
+ {
+ "cpp", "-f1", "-f2", "-f3", "-f4-f5", "-o", "test.out",
+ "test.c", NULL
+ },
+ },
+
+ {
+ "Some optional arguments",
+ "cpp",
+ "",
+ "test.c",
+ "test.out",
+ true,
+ {
+ "-o1", "-o2", NULL
+ },
+ {
+ "cpp", "-o1", "-o2", "-o", "test.out", "test.c", NULL
+ },
+ },
+
+ {
+ "Some cppflags and optional arguments",
+ "cpp",
+ "-f1",
+ "test.c",
+ "test.out",
+ true,
+ {
+ "-o1", "-o2", NULL
+ },
+ {
+ "cpp", "-f1", "-o1", "-o2", "-o", "test.out", "test.c", NULL
+ },
+ },
+
+ {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ false,
+ { NULL },
+ { NULL },
+ },
+};
+
+static struct cxx_o_test {
+ const char *msg;
+ const char *cxx;
+ const char *cxxflags;
+ const char *cppflags;
+ const char *sfile;
+ const char *ofile;
+ bool hasoptargs;
+ const char *const optargs[16];
+ const char *const expargv[16];
+} cxx_o_tests[] = {
+ {
+ "No flags",
+ "c++",
+ "",
+ "",
+ "test.c",
+ "test.o",
+ false,
+ {
+ NULL
+ },
+ {
+ "c++", "-o", "test.o", "-c", "test.c", NULL
+ },
+ },
+
+ {
+ "Multi-word program name",
+ "c++ -foo",
+ "",
+ "",
+ "test.c",
+ "test.o",
+ false,
+ {
+ NULL
+ },
+ {
+ "c++", "-foo", "-o", "test.o", "-c", "test.c", NULL
+ },
+ },
+
+ {
+ "Some cxxflags",
+ "c++",
+ "-f1 -f2 -f3 -f4-f5",
+ "",
+ "test.c",
+ "test.o",
+ false,
+ {
+ NULL
+ },
+ {
+ "c++", "-f1", "-f2", "-f3", "-f4-f5", "-o", "test.o",
+ "-c", "test.c", NULL
+ },
+ },
+
+ {
+ "Some cppflags",
+ "c++",
+ "",
+ "-f1 -f2 -f3 -f4-f5",
+ "test.c",
+ "test.o",
+ false,
+ {
+ NULL
+ },
+ {
+ "c++", "-f1", "-f2", "-f3", "-f4-f5", "-o", "test.o",
+ "-c", "test.c", NULL
+ },
+ },
+
+ {
+ "Some cxxflags and cppflags",
+ "c++",
+ "-f2",
+ "-f1",
+ "test.c",
+ "test.o",
+ false,
+ {
+ NULL
+ },
+ {
+ "c++", "-f1", "-f2", "-o", "test.o", "-c", "test.c", NULL
+ },
+ },
+
+ {
+ "Some optional arguments",
+ "c++",
+ "",
+ "",
+ "test.c",
+ "test.o",
+ true,
+ {
+ "-o1", "-o2", NULL
+ },
+ {
+ "c++", "-o1", "-o2", "-o", "test.o", "-c", "test.c", NULL
+ },
+ },
+
+ {
+ "Some cxxflags, cppflags and optional arguments",
+ "c++",
+ "-f2",
+ "-f1",
+ "test.c",
+ "test.o",
+ true,
+ {
+ "-o1", "-o2", NULL
+ },
+ {
+ "c++", "-f1", "-f2", "-o1", "-o2", "-o", "test.o",
+ "-c", "test.c", NULL
+ },
+ },
+
+ {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ false,
+ { NULL },
+ { NULL },
+ },
+};
diff --git a/unit/atf-src/atf-c/macros.h b/unit/atf-src/atf-c/macros.h
new file mode 100644
index 0000000..485a159
--- /dev/null
+++ b/unit/atf-src/atf-c/macros.h
@@ -0,0 +1,212 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_MACROS_H)
+#define ATF_C_MACROS_H
+
+#include <string.h>
+
+#include <atf-c/defs.h>
+#include <atf-c/error.h>
+#include <atf-c/tc.h>
+#include <atf-c/tp.h>
+#include <atf-c/utils.h>
+
+#define ATF_TC_NAME(tc) \
+ (atfu_ ## tc ## _tc)
+
+#define ATF_TC_PACK_NAME(tc) \
+ (atfu_ ## tc ## _tc_pack)
+
+#define ATF_TC_WITHOUT_HEAD(tc) \
+ static void atfu_ ## tc ## _body(const atf_tc_t *); \
+ static atf_tc_t atfu_ ## tc ## _tc; \
+ static atf_tc_pack_t atfu_ ## tc ## _tc_pack = { \
+ .m_ident = #tc, \
+ .m_head = NULL, \
+ .m_body = atfu_ ## tc ## _body, \
+ .m_cleanup = NULL, \
+ }
+
+#define ATF_TC(tc) \
+ static void atfu_ ## tc ## _head(atf_tc_t *); \
+ static void atfu_ ## tc ## _body(const atf_tc_t *); \
+ static atf_tc_t atfu_ ## tc ## _tc; \
+ static atf_tc_pack_t atfu_ ## tc ## _tc_pack = { \
+ .m_ident = #tc, \
+ .m_head = atfu_ ## tc ## _head, \
+ .m_body = atfu_ ## tc ## _body, \
+ .m_cleanup = NULL, \
+ }
+
+#define ATF_TC_WITH_CLEANUP(tc) \
+ static void atfu_ ## tc ## _head(atf_tc_t *); \
+ static void atfu_ ## tc ## _body(const atf_tc_t *); \
+ static void atfu_ ## tc ## _cleanup(const atf_tc_t *); \
+ static atf_tc_t atfu_ ## tc ## _tc; \
+ static atf_tc_pack_t atfu_ ## tc ## _tc_pack = { \
+ .m_ident = #tc, \
+ .m_head = atfu_ ## tc ## _head, \
+ .m_body = atfu_ ## tc ## _body, \
+ .m_cleanup = atfu_ ## tc ## _cleanup, \
+ }
+
+#define ATF_TC_HEAD(tc, tcptr) \
+ static \
+ void \
+ atfu_ ## tc ## _head(atf_tc_t *tcptr ATF_DEFS_ATTRIBUTE_UNUSED)
+
+#define ATF_TC_HEAD_NAME(tc) \
+ (atfu_ ## tc ## _head)
+
+#define ATF_TC_BODY(tc, tcptr) \
+ static \
+ void \
+ atfu_ ## tc ## _body(const atf_tc_t *tcptr ATF_DEFS_ATTRIBUTE_UNUSED)
+
+#define ATF_TC_BODY_NAME(tc) \
+ (atfu_ ## tc ## _body)
+
+#define ATF_TC_CLEANUP(tc, tcptr) \
+ static \
+ void \
+ atfu_ ## tc ## _cleanup(const atf_tc_t *tcptr ATF_DEFS_ATTRIBUTE_UNUSED)
+
+#define ATF_TC_CLEANUP_NAME(tc) \
+ (atfu_ ## tc ## _cleanup)
+
+#define ATF_TP_ADD_TCS(tps) \
+ static atf_error_t atfu_tp_add_tcs(atf_tp_t *); \
+ int atf_tp_main(int, char **, atf_error_t (*)(atf_tp_t *)); \
+ \
+ int \
+ main(int argc, char **argv) \
+ { \
+ return atf_tp_main(argc, argv, atfu_tp_add_tcs); \
+ } \
+ static \
+ atf_error_t \
+ atfu_tp_add_tcs(atf_tp_t *tps)
+
+#define ATF_TP_ADD_TC(tp, tc) \
+ do { \
+ atf_error_t atfu_err; \
+ char **atfu_config = atf_tp_get_config(tp); \
+ if (atfu_config == NULL) \
+ return atf_no_memory_error(); \
+ atfu_err = atf_tc_init_pack(&atfu_ ## tc ## _tc, \
+ &atfu_ ## tc ## _tc_pack, \
+ (const char *const *)atfu_config); \
+ atf_utils_free_charpp(atfu_config); \
+ if (atf_is_error(atfu_err)) \
+ return atfu_err; \
+ atfu_err = atf_tp_add_tc(tp, &atfu_ ## tc ## _tc); \
+ if (atf_is_error(atfu_err)) \
+ return atfu_err; \
+ } while (0)
+
+#define ATF_REQUIRE_MSG(expression, fmt, ...) \
+ do { \
+ if (!(expression)) \
+ atf_tc_fail_requirement(__FILE__, __LINE__, fmt, ##__VA_ARGS__); \
+ } while(0)
+
+#define ATF_CHECK_MSG(expression, fmt, ...) \
+ do { \
+ if (!(expression)) \
+ atf_tc_fail_check(__FILE__, __LINE__, fmt, ##__VA_ARGS__); \
+ } while(0)
+
+#define ATF_REQUIRE(expression) \
+ do { \
+ if (!(expression)) \
+ atf_tc_fail_requirement(__FILE__, __LINE__, "%s", \
+ #expression " not met"); \
+ } while(0)
+
+#define ATF_CHECK(expression) \
+ do { \
+ if (!(expression)) \
+ atf_tc_fail_check(__FILE__, __LINE__, "%s", \
+ #expression " not met"); \
+ } while(0)
+
+#define ATF_REQUIRE_EQ(expected, actual) \
+ ATF_REQUIRE_MSG((expected) == (actual), "%s != %s", #expected, #actual)
+
+#define ATF_CHECK_EQ(expected, actual) \
+ ATF_CHECK_MSG((expected) == (actual), "%s != %s", #expected, #actual)
+
+#define ATF_REQUIRE_EQ_MSG(expected, actual, fmt, ...) \
+ ATF_REQUIRE_MSG((expected) == (actual), "%s != %s: " fmt, \
+ #expected, #actual, ##__VA_ARGS__)
+
+#define ATF_CHECK_EQ_MSG(expected, actual, fmt, ...) \
+ ATF_CHECK_MSG((expected) == (actual), "%s != %s: " fmt, \
+ #expected, #actual, ##__VA_ARGS__)
+
+#define ATF_REQUIRE_STREQ(expected, actual) \
+ ATF_REQUIRE_MSG(strcmp(expected, actual) == 0, "%s != %s (%s != %s)", \
+ #expected, #actual, expected, actual)
+
+#define ATF_CHECK_STREQ(expected, actual) \
+ ATF_CHECK_MSG(strcmp(expected, actual) == 0, "%s != %s (%s != %s)", \
+ #expected, #actual, expected, actual)
+
+#define ATF_REQUIRE_STREQ_MSG(expected, actual, fmt, ...) \
+ ATF_REQUIRE_MSG(strcmp(expected, actual) == 0, \
+ "%s != %s (%s != %s): " fmt, \
+ #expected, #actual, expected, actual, ##__VA_ARGS__)
+
+#define ATF_CHECK_STREQ_MSG(expected, actual, fmt, ...) \
+ ATF_CHECK_MSG(strcmp(expected, actual) == 0, \
+ "%s != %s (%s != %s): " fmt, \
+ #expected, #actual, expected, actual, ##__VA_ARGS__)
+
+#define ATF_REQUIRE_MATCH(regexp, string) \
+ ATF_REQUIRE_MSG(atf_utils_grep_string("%s", string, regexp), \
+ "'%s' not matched in '%s'", regexp, string);
+
+#define ATF_CHECK_MATCH(regexp, string) \
+ ATF_CHECK_MSG(atf_utils_grep_string("%s", string, regexp), \
+ "'%s' not matched in '%s'", regexp, string);
+
+#define ATF_REQUIRE_MATCH_MSG(regexp, string, fmt, ...) \
+ ATF_REQUIRE_MSG(atf_utils_grep_string("%s", string, regexp), \
+ "'%s' not matched in '%s': " fmt, regexp, string, \
+ ##__VA_ARGS__);
+
+#define ATF_CHECK_MATCH_MSG(regexp, string, fmt, ...) \
+ ATF_CHECK_MSG(atf_utils_grep_string("%s", string, regexp), \
+ "'%s' not matched in '%s': " fmt, regexp, string, \
+ ##__VA_ARGS__);
+
+#define ATF_CHECK_ERRNO(exp_errno, bool_expr) \
+ atf_tc_check_errno(__FILE__, __LINE__, exp_errno, #bool_expr, bool_expr)
+
+#define ATF_REQUIRE_ERRNO(exp_errno, bool_expr) \
+ atf_tc_require_errno(__FILE__, __LINE__, exp_errno, #bool_expr, bool_expr)
+
+#endif /* !defined(ATF_C_MACROS_H) */
diff --git a/unit/atf-src/atf-c/macros_h_test.c b/unit/atf-src/atf-c/macros_h_test.c
new file mode 100644
index 0000000..9525fca
--- /dev/null
+++ b/unit/atf-src/atf-c/macros_h_test.c
@@ -0,0 +1,102 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <atf-c/macros.h>
+
+void atf_require_inside_if(void);
+void atf_require_equal_inside_if(void);
+void atf_check_errno_semicolons(void);
+void atf_require_errno_semicolons(void);
+
+void
+atf_require_inside_if(void)
+{
+ /* Make sure that ATF_REQUIRE can be used inside an if statement that
+ * does not have braces. Earlier versions of it generated an error
+ * if there was an else clause because they confused the compiler
+ * by defining an unprotected nested if. */
+ if (true)
+ ATF_REQUIRE(true);
+ else
+ ATF_REQUIRE(true);
+}
+
+void
+atf_require_equal_inside_if(void)
+{
+ /* Make sure that ATF_REQUIRE_EQUAL can be used inside an if statement
+ * that does not have braces. Earlier versions of it generated an
+ * error if there was an else clause because they confused the
+ * compiler by defining an unprotected nested if. */
+ if (true)
+ ATF_REQUIRE_EQ(true, true);
+ else
+ ATF_REQUIRE_EQ(true, true);
+}
+
+void
+atf_check_errno_semicolons(void)
+{
+ /* Check that ATF_CHECK_ERRNO does not contain a semicolon that would
+ * cause an empty-statement that confuses some compilers. */
+ ATF_CHECK_ERRNO(1, 1 == 1);
+ ATF_CHECK_ERRNO(2, 2 == 2);
+}
+
+void
+atf_require_errno_semicolons(void)
+{
+ /* Check that ATF_REQUIRE_ERRNO does not contain a semicolon that would
+ * cause an empty-statement that confuses some compilers. */
+ ATF_REQUIRE_ERRNO(1, 1 == 1);
+ ATF_REQUIRE_ERRNO(2, 2 == 2);
+}
+
+/* Test case names should not be expanded during instatiation so that they
+ * can have the exact same name as macros. */
+#define TEST_MACRO_1 invalid + name
+#define TEST_MACRO_2 invalid + name
+#define TEST_MACRO_3 invalid + name
+ATF_TC(TEST_MACRO_1);
+ATF_TC_HEAD(TEST_MACRO_1, tc) { if (tc != NULL) {} }
+ATF_TC_BODY(TEST_MACRO_1, tc) { if (tc != NULL) {} }
+atf_tc_t *test_name_1 = &ATF_TC_NAME(TEST_MACRO_1);
+atf_tc_pack_t *test_pack_1 = &ATF_TC_PACK_NAME(TEST_MACRO_1);
+void (*head_1)(atf_tc_t *) = ATF_TC_HEAD_NAME(TEST_MACRO_1);
+void (*body_1)(const atf_tc_t *) = ATF_TC_BODY_NAME(TEST_MACRO_1);
+ATF_TC_WITH_CLEANUP(TEST_MACRO_2);
+ATF_TC_HEAD(TEST_MACRO_2, tc) { if (tc != NULL) {} }
+ATF_TC_BODY(TEST_MACRO_2, tc) { if (tc != NULL) {} }
+ATF_TC_CLEANUP(TEST_MACRO_2, tc) { if (tc != NULL) {} }
+atf_tc_t *test_name_2 = &ATF_TC_NAME(TEST_MACRO_2);
+atf_tc_pack_t *test_pack_2 = &ATF_TC_PACK_NAME(TEST_MACRO_2);
+void (*head_2)(atf_tc_t *) = ATF_TC_HEAD_NAME(TEST_MACRO_2);
+void (*body_2)(const atf_tc_t *) = ATF_TC_BODY_NAME(TEST_MACRO_2);
+void (*cleanup_2)(const atf_tc_t *) = ATF_TC_CLEANUP_NAME(TEST_MACRO_2);
+ATF_TC_WITHOUT_HEAD(TEST_MACRO_3);
+ATF_TC_BODY(TEST_MACRO_3, tc) { if (tc != NULL) {} }
+atf_tc_t *test_name_3 = &ATF_TC_NAME(TEST_MACRO_3);
+atf_tc_pack_t *test_pack_3 = &ATF_TC_PACK_NAME(TEST_MACRO_3);
+void (*body_3)(const atf_tc_t *) = ATF_TC_BODY_NAME(TEST_MACRO_3);
diff --git a/unit/atf-src/atf-c/macros_test.c b/unit/atf-src/atf-c/macros_test.c
new file mode 100644
index 0000000..83578cd
--- /dev/null
+++ b/unit/atf-src/atf-c/macros_test.c
@@ -0,0 +1,893 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/macros.h"
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/fs.h"
+#include "atf-c/detail/process.h"
+#include "atf-c/detail/test_helpers.h"
+#include "atf-c/detail/text.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+void
+create_ctl_file(const char *name)
+{
+ atf_fs_path_t p;
+
+ RE(atf_fs_path_init_fmt(&p, "%s", name));
+ ATF_REQUIRE(open(atf_fs_path_cstring(&p),
+ O_CREAT | O_WRONLY | O_TRUNC, 0644) != -1);
+ atf_fs_path_fini(&p);
+}
+
+static
+bool
+exists(const char *p)
+{
+ bool b;
+ atf_fs_path_t pp;
+
+ RE(atf_fs_path_init_fmt(&pp, "%s", p));
+ RE(atf_fs_exists(&pp, &b));
+ atf_fs_path_fini(&pp);
+
+ return b;
+}
+
+static
+void
+init_and_run_h_tc(const char *name, void (*head)(atf_tc_t *),
+ void (*body)(const atf_tc_t *))
+{
+ atf_tc_t tc;
+ const char *const config[] = { NULL };
+
+ RE(atf_tc_init(&tc, name, head, body, NULL, config));
+ run_h_tc(&tc, "output", "error", "result");
+ atf_tc_fini(&tc);
+}
+
+/* ---------------------------------------------------------------------
+ * Helper test cases.
+ * --------------------------------------------------------------------- */
+
+#define H_DEF(id, macro) \
+ ATF_TC_HEAD(h_ ## id, tc) \
+ { \
+ atf_tc_set_md_var(tc, "descr", "Helper test case"); \
+ } \
+ ATF_TC_BODY(h_ ## id, tc) \
+ { \
+ create_ctl_file("before"); \
+ macro; \
+ create_ctl_file("after"); \
+ }
+
+#define H_CHECK_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_check_ ## id)
+#define H_CHECK_BODY_NAME(id) ATF_TC_BODY_NAME(h_check_ ## id)
+#define H_CHECK(id, condition) \
+ H_DEF(check_ ## id, ATF_CHECK(condition))
+
+#define H_CHECK_MSG_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_check_msg_ ## id)
+#define H_CHECK_MSG_BODY_NAME(id) ATF_TC_BODY_NAME(h_check_msg_ ## id)
+#define H_CHECK_MSG(id, condition, msg) \
+ H_DEF(check_msg_ ## id, ATF_CHECK_MSG(condition, msg))
+
+#define H_CHECK_EQ_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_check_eq_ ## id)
+#define H_CHECK_EQ_BODY_NAME(id) ATF_TC_BODY_NAME(h_check_eq_ ## id)
+#define H_CHECK_EQ(id, v1, v2) \
+ H_DEF(check_eq_ ## id, ATF_CHECK_EQ(v1, v2))
+
+#define H_CHECK_STREQ_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_check_streq_ ## id)
+#define H_CHECK_STREQ_BODY_NAME(id) ATF_TC_BODY_NAME(h_check_streq_ ## id)
+#define H_CHECK_STREQ(id, v1, v2) \
+ H_DEF(check_streq_ ## id, ATF_CHECK_STREQ(v1, v2))
+
+#define H_CHECK_MATCH_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_check_match_ ## id)
+#define H_CHECK_MATCH_BODY_NAME(id) ATF_TC_BODY_NAME(h_check_match_ ## id)
+#define H_CHECK_MATCH(id, v1, v2) \
+ H_DEF(check_match_ ## id, ATF_CHECK_MATCH(v1, v2))
+
+#define H_CHECK_EQ_MSG_HEAD_NAME(id) \
+ ATF_TC_HEAD_NAME(h_check_eq_msg_ ## id)
+#define H_CHECK_EQ_MSG_BODY_NAME(id) \
+ ATF_TC_BODY_NAME(h_check_eq_msg_ ## id)
+#define H_CHECK_EQ_MSG(id, v1, v2, msg) \
+ H_DEF(check_eq_msg_ ## id, ATF_CHECK_EQ_MSG(v1, v2, msg))
+
+#define H_CHECK_STREQ_MSG_HEAD_NAME(id) \
+ ATF_TC_HEAD_NAME(h_check_streq_msg_ ## id)
+#define H_CHECK_STREQ_MSG_BODY_NAME(id) \
+ ATF_TC_BODY_NAME(h_check_streq_msg_ ## id)
+#define H_CHECK_STREQ_MSG(id, v1, v2, msg) \
+ H_DEF(check_streq_msg_ ## id, ATF_CHECK_STREQ_MSG(v1, v2, msg))
+
+#define H_CHECK_MATCH_MSG_HEAD_NAME(id) \
+ ATF_TC_HEAD_NAME(h_check_match_msg_ ## id)
+#define H_CHECK_MATCH_MSG_BODY_NAME(id) \
+ ATF_TC_BODY_NAME(h_check_match_msg_ ## id)
+#define H_CHECK_MATCH_MSG(id, v1, v2, msg) \
+ H_DEF(check_match_msg_ ## id, ATF_CHECK_MATCH_MSG(v1, v2, msg))
+
+#define H_CHECK_ERRNO_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_check_errno_ ## id)
+#define H_CHECK_ERRNO_BODY_NAME(id) ATF_TC_BODY_NAME(h_check_errno_ ## id)
+#define H_CHECK_ERRNO(id, exp_errno, bool_expr) \
+ H_DEF(check_errno_ ## id, ATF_CHECK_ERRNO(exp_errno, bool_expr))
+
+#define H_REQUIRE_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_require_ ## id)
+#define H_REQUIRE_BODY_NAME(id) ATF_TC_BODY_NAME(h_require_ ## id)
+#define H_REQUIRE(id, condition) \
+ H_DEF(require_ ## id, ATF_REQUIRE(condition))
+
+#define H_REQUIRE_MSG_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_require_msg_ ## id)
+#define H_REQUIRE_MSG_BODY_NAME(id) ATF_TC_BODY_NAME(h_require_msg_ ## id)
+#define H_REQUIRE_MSG(id, condition, msg) \
+ H_DEF(require_msg_ ## id, ATF_REQUIRE_MSG(condition, msg))
+
+#define H_REQUIRE_EQ_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_require_eq_ ## id)
+#define H_REQUIRE_EQ_BODY_NAME(id) ATF_TC_BODY_NAME(h_require_eq_ ## id)
+#define H_REQUIRE_EQ(id, v1, v2) \
+ H_DEF(require_eq_ ## id, ATF_REQUIRE_EQ(v1, v2))
+
+#define H_REQUIRE_STREQ_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_require_streq_ ## id)
+#define H_REQUIRE_STREQ_BODY_NAME(id) ATF_TC_BODY_NAME(h_require_streq_ ## id)
+#define H_REQUIRE_STREQ(id, v1, v2) \
+ H_DEF(require_streq_ ## id, ATF_REQUIRE_STREQ(v1, v2))
+
+#define H_REQUIRE_MATCH_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_require_match_ ## id)
+#define H_REQUIRE_MATCH_BODY_NAME(id) ATF_TC_BODY_NAME(h_require_match_ ## id)
+#define H_REQUIRE_MATCH(id, v1, v2) \
+ H_DEF(require_match_ ## id, ATF_REQUIRE_MATCH(v1, v2))
+
+#define H_REQUIRE_EQ_MSG_HEAD_NAME(id) \
+ ATF_TC_HEAD_NAME(h_require_eq_msg_ ## id)
+#define H_REQUIRE_EQ_MSG_BODY_NAME(id) \
+ ATF_TC_BODY_NAME(h_require_eq_msg_ ## id)
+#define H_REQUIRE_EQ_MSG(id, v1, v2, msg) \
+ H_DEF(require_eq_msg_ ## id, ATF_REQUIRE_EQ_MSG(v1, v2, msg))
+
+#define H_REQUIRE_STREQ_MSG_HEAD_NAME(id) \
+ ATF_TC_HEAD_NAME(h_require_streq_msg_ ## id)
+#define H_REQUIRE_STREQ_MSG_BODY_NAME(id) \
+ ATF_TC_BODY_NAME(h_require_streq_msg_ ## id)
+#define H_REQUIRE_STREQ_MSG(id, v1, v2, msg) \
+ H_DEF(require_streq_msg_ ## id, ATF_REQUIRE_STREQ_MSG(v1, v2, msg))
+
+#define H_REQUIRE_MATCH_MSG_HEAD_NAME(id) \
+ ATF_TC_HEAD_NAME(h_require_match_msg_ ## id)
+#define H_REQUIRE_MATCH_MSG_BODY_NAME(id) \
+ ATF_TC_BODY_NAME(h_require_match_msg_ ## id)
+#define H_REQUIRE_MATCH_MSG(id, v1, v2, msg) \
+ H_DEF(require_match_msg_ ## id, ATF_REQUIRE_MATCH_MSG(v1, v2, msg))
+
+#define H_REQUIRE_ERRNO_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_require_errno_ ## id)
+#define H_REQUIRE_ERRNO_BODY_NAME(id) ATF_TC_BODY_NAME(h_require_errno_ ## id)
+#define H_REQUIRE_ERRNO(id, exp_errno, bool_expr) \
+ H_DEF(require_errno_ ## id, ATF_REQUIRE_ERRNO(exp_errno, bool_expr))
+
+/* ---------------------------------------------------------------------
+ * Test cases for the ATF_{CHECK,REQUIRE}_ERRNO macros.
+ * --------------------------------------------------------------------- */
+
+static int
+errno_fail_stub(const int raised_errno)
+{
+ errno = raised_errno;
+ return -1;
+}
+
+static int
+errno_ok_stub(void)
+{
+ return 0;
+}
+
+H_CHECK_ERRNO(no_error, -1, errno_ok_stub() == -1);
+H_CHECK_ERRNO(errno_ok, 2, errno_fail_stub(2) == -1);
+H_CHECK_ERRNO(errno_fail, 3, errno_fail_stub(4) == -1);
+
+H_REQUIRE_ERRNO(no_error, -1, errno_ok_stub() == -1);
+H_REQUIRE_ERRNO(errno_ok, 2, errno_fail_stub(2) == -1);
+H_REQUIRE_ERRNO(errno_fail, 3, errno_fail_stub(4) == -1);
+
+ATF_TC(check_errno);
+ATF_TC_HEAD(check_errno, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the ATF_CHECK_ERRNO macro");
+}
+ATF_TC_BODY(check_errno, tc)
+{
+ struct test {
+ void (*head)(atf_tc_t *);
+ void (*body)(const atf_tc_t *);
+ bool ok;
+ const char *exp_regex;
+ } *t, tests[] = {
+ { H_CHECK_ERRNO_HEAD_NAME(no_error),
+ H_CHECK_ERRNO_BODY_NAME(no_error),
+ false, "Expected true value in errno_ok_stub\\(\\) == -1" },
+ { H_CHECK_ERRNO_HEAD_NAME(errno_ok),
+ H_CHECK_ERRNO_BODY_NAME(errno_ok),
+ true, NULL },
+ { H_CHECK_ERRNO_HEAD_NAME(errno_fail),
+ H_CHECK_ERRNO_BODY_NAME(errno_fail),
+ false, "Expected errno 3, got 4, in errno_fail_stub\\(4\\) == -1" },
+ { NULL, NULL, false, NULL }
+ };
+
+ for (t = &tests[0]; t->head != NULL; t++) {
+ init_and_run_h_tc("h_check_errno", t->head, t->body);
+
+ ATF_REQUIRE(exists("before"));
+ ATF_REQUIRE(exists("after"));
+
+ if (t->ok) {
+ ATF_REQUIRE(atf_utils_grep_file("^passed", "result"));
+ } else {
+ ATF_REQUIRE(atf_utils_grep_file("^failed", "result"));
+ ATF_REQUIRE(atf_utils_grep_file(
+ "macros_test.c:[0-9]+: %s$", "error", t->exp_regex));
+ }
+
+ ATF_REQUIRE(unlink("before") != -1);
+ ATF_REQUIRE(unlink("after") != -1);
+ }
+}
+
+ATF_TC(require_errno);
+ATF_TC_HEAD(require_errno, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the ATF_REQUIRE_ERRNO macro");
+}
+ATF_TC_BODY(require_errno, tc)
+{
+ struct test {
+ void (*head)(atf_tc_t *);
+ void (*body)(const atf_tc_t *);
+ bool ok;
+ const char *exp_regex;
+ } *t, tests[] = {
+ { H_REQUIRE_ERRNO_HEAD_NAME(no_error),
+ H_REQUIRE_ERRNO_BODY_NAME(no_error),
+ false, "Expected true value in errno_ok_stub\\(\\) == -1" },
+ { H_REQUIRE_ERRNO_HEAD_NAME(errno_ok),
+ H_REQUIRE_ERRNO_BODY_NAME(errno_ok),
+ true, NULL },
+ { H_REQUIRE_ERRNO_HEAD_NAME(errno_fail),
+ H_REQUIRE_ERRNO_BODY_NAME(errno_fail),
+ false, "Expected errno 3, got 4, in errno_fail_stub\\(4\\) == -1" },
+ { NULL, NULL, false, NULL }
+ };
+
+ for (t = &tests[0]; t->head != NULL; t++) {
+ init_and_run_h_tc("h_require_errno", t->head, t->body);
+
+ ATF_REQUIRE(exists("before"));
+ if (t->ok) {
+ ATF_REQUIRE(atf_utils_grep_file("^passed", "result"));
+ ATF_REQUIRE(exists("after"));
+ } else {
+ ATF_REQUIRE(atf_utils_grep_file(
+ "^failed: .*macros_test.c:[0-9]+: %s$", "result",
+ t->exp_regex));
+ ATF_REQUIRE(!exists("after"));
+ }
+
+ ATF_REQUIRE(unlink("before") != -1);
+ if (t->ok)
+ ATF_REQUIRE(unlink("after") != -1);
+ }
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the ATF_CHECK and ATF_CHECK_MSG macros.
+ * --------------------------------------------------------------------- */
+
+H_CHECK(0, 0);
+H_CHECK(1, 1);
+H_CHECK_MSG(0, 0, "expected a false value");
+H_CHECK_MSG(1, 1, "expected a true value");
+
+ATF_TC(check);
+ATF_TC_HEAD(check, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the ATF_CHECK and "
+ "ATF_CHECK_MSG macros");
+}
+ATF_TC_BODY(check, tc)
+{
+ struct test {
+ void (*head)(atf_tc_t *);
+ void (*body)(const atf_tc_t *);
+ bool value;
+ const char *msg;
+ bool ok;
+ } *t, tests[] = {
+ { H_CHECK_HEAD_NAME(0), H_CHECK_BODY_NAME(0), 0,
+ "0 not met", false },
+ { H_CHECK_HEAD_NAME(1), H_CHECK_BODY_NAME(1), 1,
+ "1 not met", true },
+ { H_CHECK_MSG_HEAD_NAME(0), H_CHECK_MSG_BODY_NAME(0), 0,
+ "expected a false value", false },
+ { H_CHECK_MSG_HEAD_NAME(1), H_CHECK_MSG_BODY_NAME(1), 1,
+ "expected a true value", true },
+ { NULL, NULL, false, NULL, false }
+ };
+
+ for (t = &tests[0]; t->head != NULL; t++) {
+ printf("Checking with a %d value\n", t->value);
+
+ init_and_run_h_tc("h_check", t->head, t->body);
+
+ ATF_REQUIRE(exists("before"));
+ ATF_REQUIRE(exists("after"));
+
+ if (t->ok) {
+ ATF_REQUIRE(atf_utils_grep_file("^passed", "result"));
+ } else {
+ ATF_REQUIRE(atf_utils_grep_file("^failed", "result"));
+ ATF_REQUIRE(atf_utils_grep_file("Check failed: .*"
+ "macros_test.c:[0-9]+: %s$", "error", t->msg));
+ }
+
+ ATF_REQUIRE(unlink("before") != -1);
+ ATF_REQUIRE(unlink("after") != -1);
+ }
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the ATF_CHECK_*EQ_ macros.
+ * --------------------------------------------------------------------- */
+
+struct check_eq_test {
+ void (*head)(atf_tc_t *);
+ void (*body)(const atf_tc_t *);
+ const char *v1;
+ const char *v2;
+ const char *msg;
+ bool ok;
+};
+
+static
+void
+do_check_eq_tests(const struct check_eq_test *tests)
+{
+ const struct check_eq_test *t;
+
+ for (t = &tests[0]; t->head != NULL; t++) {
+ printf("Checking with %s, %s and expecting %s\n", t->v1, t->v2,
+ t->ok ? "true" : "false");
+
+ init_and_run_h_tc("h_check", t->head, t->body);
+
+ ATF_CHECK(exists("before"));
+ ATF_CHECK(exists("after"));
+
+ if (t->ok) {
+ ATF_REQUIRE(atf_utils_grep_file("^passed", "result"));
+ } else {
+ ATF_REQUIRE(atf_utils_grep_file("^failed", "result"));
+ ATF_CHECK(atf_utils_grep_file("Check failed: .*"
+ "macros_test.c:[0-9]+: %s$", "error", t->msg));
+ }
+
+ ATF_CHECK(unlink("before") != -1);
+ ATF_CHECK(unlink("after") != -1);
+ }
+}
+
+H_CHECK_EQ(1_1, 1, 1);
+H_CHECK_EQ(1_2, 1, 2);
+H_CHECK_EQ(2_1, 2, 1);
+H_CHECK_EQ(2_2, 2, 2);
+H_CHECK_EQ_MSG(1_1, 1, 1, "1 does not match 1");
+H_CHECK_EQ_MSG(1_2, 1, 2, "1 does not match 2");
+H_CHECK_EQ_MSG(2_1, 2, 1, "2 does not match 1");
+H_CHECK_EQ_MSG(2_2, 2, 2, "2 does not match 2");
+
+ATF_TC(check_eq);
+ATF_TC_HEAD(check_eq, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the ATF_CHECK_EQ and "
+ "ATF_CHECK_EQ_MSG macros");
+}
+ATF_TC_BODY(check_eq, tc)
+{
+ struct check_eq_test tests[] = {
+ { H_CHECK_EQ_HEAD_NAME(1_1), H_CHECK_EQ_BODY_NAME(1_1),
+ "1", "1", "1 != 1", true },
+ { H_CHECK_EQ_HEAD_NAME(1_2), H_CHECK_EQ_BODY_NAME(1_2),
+ "1", "2", "1 != 2", false },
+ { H_CHECK_EQ_HEAD_NAME(2_1), H_CHECK_EQ_BODY_NAME(2_1),
+ "2", "1", "2 != 1", false },
+ { H_CHECK_EQ_HEAD_NAME(2_2), H_CHECK_EQ_BODY_NAME(2_2),
+ "2", "2", "2 != 2", true },
+ { H_CHECK_EQ_MSG_HEAD_NAME(1_1), H_CHECK_EQ_MSG_BODY_NAME(1_1),
+ "1", "1", "1 != 1: 1 does not match 1", true },
+ { H_CHECK_EQ_MSG_HEAD_NAME(1_2), H_CHECK_EQ_MSG_BODY_NAME(1_2),
+ "1", "2", "1 != 2: 1 does not match 2", false },
+ { H_CHECK_EQ_MSG_HEAD_NAME(2_1), H_CHECK_EQ_MSG_BODY_NAME(2_1),
+ "2", "1", "2 != 1: 2 does not match 1", false },
+ { H_CHECK_EQ_MSG_HEAD_NAME(2_2), H_CHECK_EQ_MSG_BODY_NAME(2_2),
+ "2", "2", "2 != 2: 2 does not match 2", true },
+ { NULL, NULL, 0, 0, "", false }
+ };
+ do_check_eq_tests(tests);
+}
+
+H_CHECK_STREQ(1_1, "1", "1");
+H_CHECK_STREQ(1_2, "1", "2");
+H_CHECK_STREQ(2_1, "2", "1");
+H_CHECK_STREQ(2_2, "2", "2");
+H_CHECK_STREQ_MSG(1_1, "1", "1", "1 does not match 1");
+H_CHECK_STREQ_MSG(1_2, "1", "2", "1 does not match 2");
+H_CHECK_STREQ_MSG(2_1, "2", "1", "2 does not match 1");
+H_CHECK_STREQ_MSG(2_2, "2", "2", "2 does not match 2");
+#define CHECK_STREQ_VAR1 "5"
+#define CHECK_STREQ_VAR2 "9"
+const char *check_streq_var1 = CHECK_STREQ_VAR1;
+const char *check_streq_var2 = CHECK_STREQ_VAR2;
+H_CHECK_STREQ(vars, check_streq_var1, check_streq_var2);
+
+ATF_TC(check_streq);
+ATF_TC_HEAD(check_streq, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the ATF_CHECK_STREQ and "
+ "ATF_CHECK_STREQ_MSG macros");
+}
+ATF_TC_BODY(check_streq, tc)
+{
+ struct check_eq_test tests[] = {
+ { H_CHECK_STREQ_HEAD_NAME(1_1), H_CHECK_STREQ_BODY_NAME(1_1),
+ "1", "1", "\"1\" != \"1\" \\(1 != 1\\)", true },
+ { H_CHECK_STREQ_HEAD_NAME(1_2), H_CHECK_STREQ_BODY_NAME(1_2),
+ "1", "2", "\"1\" != \"2\" \\(1 != 2\\)", false },
+ { H_CHECK_STREQ_HEAD_NAME(2_1), H_CHECK_STREQ_BODY_NAME(2_1),
+ "2", "1", "\"2\" != \"1\" \\(2 != 1\\)", false },
+ { H_CHECK_STREQ_HEAD_NAME(2_2), H_CHECK_STREQ_BODY_NAME(2_2),
+ "2", "2", "\"2\" != \"2\" \\(2 != 2\\)", true },
+ { H_CHECK_STREQ_MSG_HEAD_NAME(1_1),
+ H_CHECK_STREQ_MSG_BODY_NAME(1_1),
+ "1", "1", "\"1\" != \"1\" \\(1 != 1\\): 1 does not match 1", true },
+ { H_CHECK_STREQ_MSG_HEAD_NAME(1_2),
+ H_CHECK_STREQ_MSG_BODY_NAME(1_2),
+ "1", "2", "\"1\" != \"2\" \\(1 != 2\\): 1 does not match 2", false },
+ { H_CHECK_STREQ_MSG_HEAD_NAME(2_1),
+ H_CHECK_STREQ_MSG_BODY_NAME(2_1),
+ "2", "1", "\"2\" != \"1\" \\(2 != 1\\): 2 does not match 1", false },
+ { H_CHECK_STREQ_MSG_HEAD_NAME(2_2),
+ H_CHECK_STREQ_MSG_BODY_NAME(2_2),
+ "2", "2", "\"2\" != \"2\" \\(2 != 2\\): 2 does not match 2", true },
+ { H_CHECK_STREQ_HEAD_NAME(vars), H_CHECK_STREQ_BODY_NAME(vars),
+ check_streq_var1, check_streq_var2,
+ "check_streq_var1 != check_streq_var2 \\("
+ CHECK_STREQ_VAR1 " != " CHECK_STREQ_VAR2 "\\)", false },
+ { NULL, NULL, 0, 0, "", false }
+ };
+ do_check_eq_tests(tests);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the ATF_CHECK_MATCH and ATF_CHECK_MATCH_MSG macros.
+ * --------------------------------------------------------------------- */
+
+H_CHECK_MATCH(yes, "hello [a-z]+", "abc hello world");
+H_CHECK_MATCH(no, "hello [a-z]+", "abc hello WORLD");
+H_CHECK_MATCH_MSG(yes, "hello [a-z]+", "abc hello world", "lowercase");
+H_CHECK_MATCH_MSG(no, "hello [a-z]+", "abc hello WORLD", "uppercase");
+
+ATF_TC(check_match);
+ATF_TC_HEAD(check_match, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the ATF_CHECK_MATCH and "
+ "ATF_CHECK_MATCH_MSG macros");
+}
+ATF_TC_BODY(check_match, tc)
+{
+ struct check_eq_test tests[] = {
+ { H_CHECK_MATCH_HEAD_NAME(yes), H_CHECK_MATCH_BODY_NAME(yes),
+ "hello [a-z]+", "abc hello world", "", true },
+ { H_CHECK_MATCH_HEAD_NAME(no), H_CHECK_MATCH_BODY_NAME(no),
+ "hello [a-z]+", "abc hello WORLD",
+ "'hello \\[a-z\\]\\+' not matched in 'abc hello WORLD'", false },
+ { H_CHECK_MATCH_MSG_HEAD_NAME(yes), H_CHECK_MATCH_MSG_BODY_NAME(yes),
+ "hello [a-z]+", "abc hello world", "", true },
+ { H_CHECK_MATCH_MSG_HEAD_NAME(no), H_CHECK_MATCH_MSG_BODY_NAME(no),
+ "hello [a-z]+", "abc hello WORLD",
+ "'hello \\[a-z\\]\\+' not matched in 'abc hello WORLD': uppercase",
+ false },
+ { NULL, NULL, 0, 0, "", false }
+ };
+ do_check_eq_tests(tests);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the ATF_REQUIRE and ATF_REQUIRE_MSG macros.
+ * --------------------------------------------------------------------- */
+
+H_REQUIRE(0, 0);
+H_REQUIRE(1, 1);
+H_REQUIRE_MSG(0, 0, "expected a false value");
+H_REQUIRE_MSG(1, 1, "expected a true value");
+
+ATF_TC(require);
+ATF_TC_HEAD(require, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the ATF_REQUIRE and "
+ "ATF_REQUIRE_MSG macros");
+}
+ATF_TC_BODY(require, tc)
+{
+ struct test {
+ void (*head)(atf_tc_t *);
+ void (*body)(const atf_tc_t *);
+ bool value;
+ const char *msg;
+ bool ok;
+ } *t, tests[] = {
+ { H_REQUIRE_HEAD_NAME(0), H_REQUIRE_BODY_NAME(0), 0,
+ "0 not met", false },
+ { H_REQUIRE_HEAD_NAME(1), H_REQUIRE_BODY_NAME(1), 1,
+ "1 not met", true },
+ { H_REQUIRE_MSG_HEAD_NAME(0), H_REQUIRE_MSG_BODY_NAME(0), 0,
+ "expected a false value", false },
+ { H_REQUIRE_MSG_HEAD_NAME(1), H_REQUIRE_MSG_BODY_NAME(1), 1,
+ "expected a true value", true },
+ { NULL, NULL, false, NULL, false }
+ };
+
+ for (t = &tests[0]; t->head != NULL; t++) {
+ printf("Checking with a %d value\n", t->value);
+
+ init_and_run_h_tc("h_require", t->head, t->body);
+
+ ATF_REQUIRE(exists("before"));
+ if (t->ok) {
+ ATF_REQUIRE(atf_utils_grep_file("^passed", "result"));
+ ATF_REQUIRE(exists("after"));
+ } else {
+ ATF_REQUIRE(atf_utils_grep_file(
+ "^failed: .*macros_test.c:[0-9]+: %s$", "result", t->msg));
+ ATF_REQUIRE(!exists("after"));
+ }
+
+ ATF_REQUIRE(unlink("before") != -1);
+ if (t->ok)
+ ATF_REQUIRE(unlink("after") != -1);
+ }
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the ATF_REQUIRE_*EQ_ macros.
+ * --------------------------------------------------------------------- */
+
+struct require_eq_test {
+ void (*head)(atf_tc_t *);
+ void (*body)(const atf_tc_t *);
+ const char *v1;
+ const char *v2;
+ const char *msg;
+ bool ok;
+};
+
+static
+void
+do_require_eq_tests(const struct require_eq_test *tests)
+{
+ const struct require_eq_test *t;
+
+ for (t = &tests[0]; t->head != NULL; t++) {
+ printf("Checking with %s, %s and expecting %s\n", t->v1, t->v2,
+ t->ok ? "true" : "false");
+
+ init_and_run_h_tc("h_require", t->head, t->body);
+
+ ATF_REQUIRE(exists("before"));
+ if (t->ok) {
+ ATF_REQUIRE(atf_utils_grep_file("^passed", "result"));
+ ATF_REQUIRE(exists("after"));
+ } else {
+ ATF_REQUIRE(atf_utils_grep_file("^failed: .*macros_test.c"
+ ":[0-9]+: %s$", "result", t->msg));
+ ATF_REQUIRE(!exists("after"));
+ }
+
+ ATF_REQUIRE(unlink("before") != -1);
+ if (t->ok)
+ ATF_REQUIRE(unlink("after") != -1);
+ }
+}
+
+H_REQUIRE_EQ(1_1, 1, 1);
+H_REQUIRE_EQ(1_2, 1, 2);
+H_REQUIRE_EQ(2_1, 2, 1);
+H_REQUIRE_EQ(2_2, 2, 2);
+H_REQUIRE_EQ_MSG(1_1, 1, 1, "1 does not match 1");
+H_REQUIRE_EQ_MSG(1_2, 1, 2, "1 does not match 2");
+H_REQUIRE_EQ_MSG(2_1, 2, 1, "2 does not match 1");
+H_REQUIRE_EQ_MSG(2_2, 2, 2, "2 does not match 2");
+
+ATF_TC(require_eq);
+ATF_TC_HEAD(require_eq, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the ATF_REQUIRE_EQ and "
+ "ATF_REQUIRE_EQ_MSG macros");
+}
+ATF_TC_BODY(require_eq, tc)
+{
+ struct require_eq_test tests[] = {
+ { H_REQUIRE_EQ_HEAD_NAME(1_1), H_REQUIRE_EQ_BODY_NAME(1_1),
+ "1", "1", "1 != 1", true },
+ { H_REQUIRE_EQ_HEAD_NAME(1_2), H_REQUIRE_EQ_BODY_NAME(1_2),
+ "1", "2", "1 != 2", false },
+ { H_REQUIRE_EQ_HEAD_NAME(2_1), H_REQUIRE_EQ_BODY_NAME(2_1),
+ "2", "1", "2 != 1", false },
+ { H_REQUIRE_EQ_HEAD_NAME(2_2), H_REQUIRE_EQ_BODY_NAME(2_2),
+ "2", "2", "2 != 2", true },
+ { H_REQUIRE_EQ_MSG_HEAD_NAME(1_1), H_REQUIRE_EQ_MSG_BODY_NAME(1_1),
+ "1", "1", "1 != 1: 1 does not match 1", true },
+ { H_REQUIRE_EQ_MSG_HEAD_NAME(1_2), H_REQUIRE_EQ_MSG_BODY_NAME(1_2),
+ "1", "2", "1 != 2: 1 does not match 2", false },
+ { H_REQUIRE_EQ_MSG_HEAD_NAME(2_1), H_REQUIRE_EQ_MSG_BODY_NAME(2_1),
+ "2", "1", "2 != 1: 2 does not match 1", false },
+ { H_REQUIRE_EQ_MSG_HEAD_NAME(2_2), H_REQUIRE_EQ_MSG_BODY_NAME(2_2),
+ "2", "2", "2 != 2: 2 does not match 2", true },
+ { NULL, NULL, 0, 0, "", false }
+ };
+ do_require_eq_tests(tests);
+}
+
+H_REQUIRE_STREQ(1_1, "1", "1");
+H_REQUIRE_STREQ(1_2, "1", "2");
+H_REQUIRE_STREQ(2_1, "2", "1");
+H_REQUIRE_STREQ(2_2, "2", "2");
+H_REQUIRE_STREQ_MSG(1_1, "1", "1", "1 does not match 1");
+H_REQUIRE_STREQ_MSG(1_2, "1", "2", "1 does not match 2");
+H_REQUIRE_STREQ_MSG(2_1, "2", "1", "2 does not match 1");
+H_REQUIRE_STREQ_MSG(2_2, "2", "2", "2 does not match 2");
+#define REQUIRE_STREQ_VAR1 "5"
+#define REQUIRE_STREQ_VAR2 "9"
+const char *require_streq_var1 = REQUIRE_STREQ_VAR1;
+const char *require_streq_var2 = REQUIRE_STREQ_VAR2;
+H_REQUIRE_STREQ(vars, require_streq_var1, require_streq_var2);
+
+ATF_TC(require_streq);
+ATF_TC_HEAD(require_streq, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the ATF_REQUIRE_STREQ and "
+ "ATF_REQUIRE_STREQ_MSG macros");
+}
+ATF_TC_BODY(require_streq, tc)
+{
+ struct require_eq_test tests[] = {
+ { H_REQUIRE_STREQ_HEAD_NAME(1_1), H_REQUIRE_STREQ_BODY_NAME(1_1),
+ "1", "1", "\"1\" != \"1\" \\(1 != 1\\)", true },
+ { H_REQUIRE_STREQ_HEAD_NAME(1_2), H_REQUIRE_STREQ_BODY_NAME(1_2),
+ "1", "2", "\"1\" != \"2\" \\(1 != 2\\)", false },
+ { H_REQUIRE_STREQ_HEAD_NAME(2_1), H_REQUIRE_STREQ_BODY_NAME(2_1),
+ "2", "1", "\"2\" != \"1\" \\(2 != 1\\)", false },
+ { H_REQUIRE_STREQ_HEAD_NAME(2_2), H_REQUIRE_STREQ_BODY_NAME(2_2),
+ "2", "2", "\"2\" != \"2\" \\(2 != 2\\)", true },
+ { H_REQUIRE_STREQ_MSG_HEAD_NAME(1_1),
+ H_REQUIRE_STREQ_MSG_BODY_NAME(1_1),
+ "1", "1", "\"1\" != \"1\" \\(1 != 1\\): 1 does not match 1", true },
+ { H_REQUIRE_STREQ_MSG_HEAD_NAME(1_2),
+ H_REQUIRE_STREQ_MSG_BODY_NAME(1_2),
+ "1", "2", "\"1\" != \"2\" \\(1 != 2\\): 1 does not match 2", false },
+ { H_REQUIRE_STREQ_MSG_HEAD_NAME(2_1),
+ H_REQUIRE_STREQ_MSG_BODY_NAME(2_1),
+ "2", "1", "\"2\" != \"1\" \\(2 != 1\\): 2 does not match 1", false },
+ { H_REQUIRE_STREQ_MSG_HEAD_NAME(2_2),
+ H_REQUIRE_STREQ_MSG_BODY_NAME(2_2),
+ "2", "2", "\"2\" != \"2\" \\(2 != 2\\): 2 does not match 2", true },
+ { H_REQUIRE_STREQ_HEAD_NAME(vars), H_REQUIRE_STREQ_BODY_NAME(vars),
+ require_streq_var1, require_streq_var2,
+ "require_streq_var1 != require_streq_var2 \\("
+ REQUIRE_STREQ_VAR1 " != " REQUIRE_STREQ_VAR2 "\\)", false },
+ { NULL, NULL, 0, 0, "", false }
+ };
+ do_require_eq_tests(tests);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the ATF_REQUIRE_MATCH and ATF_REQUIRE_MATCH_MSG macros.
+ * --------------------------------------------------------------------- */
+
+H_REQUIRE_MATCH(yes, "hello [a-z]+", "abc hello world");
+H_REQUIRE_MATCH(no, "hello [a-z]+", "abc hello WORLD");
+H_REQUIRE_MATCH_MSG(yes, "hello [a-z]+", "abc hello world", "lowercase");
+H_REQUIRE_MATCH_MSG(no, "hello [a-z]+", "abc hello WORLD", "uppercase");
+
+ATF_TC(require_match);
+ATF_TC_HEAD(require_match, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the ATF_REQUIRE_MATCH and "
+ "ATF_REQUIRE_MATCH_MSG macros");
+}
+ATF_TC_BODY(require_match, tc)
+{
+ struct require_eq_test tests[] = {
+ { H_REQUIRE_MATCH_HEAD_NAME(yes), H_REQUIRE_MATCH_BODY_NAME(yes),
+ "hello [a-z]+", "abc hello world", "", true },
+ { H_REQUIRE_MATCH_HEAD_NAME(no), H_REQUIRE_MATCH_BODY_NAME(no),
+ "hello [a-z]+", "abc hello WORLD",
+ "'hello \\[a-z\\]\\+' not matched in 'abc hello WORLD'", false },
+ { H_REQUIRE_MATCH_MSG_HEAD_NAME(yes),
+ H_REQUIRE_MATCH_MSG_BODY_NAME(yes),
+ "hello [a-z]+", "abc hello world", "", true },
+ { H_REQUIRE_MATCH_MSG_HEAD_NAME(no), H_REQUIRE_MATCH_MSG_BODY_NAME(no),
+ "hello [a-z]+", "abc hello WORLD",
+ "'hello \\[a-z\\]\\+' not matched in 'abc hello WORLD': uppercase",
+ false },
+ { NULL, NULL, 0, 0, "", false }
+ };
+ do_require_eq_tests(tests);
+}
+
+/* ---------------------------------------------------------------------
+ * Miscellaneous test cases covering several macros.
+ * --------------------------------------------------------------------- */
+
+static
+bool
+aux_bool(const char *fmt ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+ return false;
+}
+
+static
+const char *
+aux_str(const char *fmt ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+ return "foo";
+}
+
+H_CHECK(msg, aux_bool("%d"));
+H_REQUIRE(msg, aux_bool("%d"));
+H_CHECK_STREQ(msg, aux_str("%d"), "");
+H_REQUIRE_STREQ(msg, aux_str("%d"), "");
+
+ATF_TC(msg_embedded_fmt);
+ATF_TC_HEAD(msg_embedded_fmt, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests that format strings passed "
+ "as part of the automatically-generated messages "
+ "do not get expanded");
+}
+ATF_TC_BODY(msg_embedded_fmt, tc)
+{
+ struct test {
+ void (*head)(atf_tc_t *);
+ void (*body)(const atf_tc_t *);
+ bool fatal;
+ const char *msg;
+ } *t, tests[] = {
+ { H_CHECK_HEAD_NAME(msg), H_CHECK_BODY_NAME(msg), false,
+ "aux_bool\\(\"%d\"\\) not met" },
+ { H_REQUIRE_HEAD_NAME(msg), H_REQUIRE_BODY_NAME(msg), true,
+ "aux_bool\\(\"%d\"\\) not met" },
+ { H_CHECK_STREQ_HEAD_NAME(msg), H_CHECK_STREQ_BODY_NAME(msg), false,
+ "aux_str\\(\"%d\"\\) != \"\" \\(foo != \\)" },
+ { H_REQUIRE_STREQ_HEAD_NAME(msg), H_REQUIRE_STREQ_BODY_NAME(msg), true,
+ "aux_str\\(\"%d\"\\) != \"\" \\(foo != \\)" },
+ { NULL, NULL, false, NULL }
+ };
+
+ for (t = &tests[0]; t->head != NULL; t++) {
+ printf("Checking with an expected '%s' message\n", t->msg);
+
+ init_and_run_h_tc("h_check", t->head, t->body);
+
+ if (t->fatal) {
+ bool matched =
+ atf_utils_grep_file(
+ "^failed: .*macros_test.c:[0-9]+: %s$", "result", t->msg);
+ ATF_CHECK_MSG(matched, "couldn't find error string in result");
+ } else {
+ bool matched = atf_utils_grep_file("Check failed: .*"
+ "macros_test.c:[0-9]+: %s$", "error", t->msg);
+ ATF_CHECK_MSG(matched, "couldn't find error string in output");
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------
+ * Tests cases for the header file.
+ * --------------------------------------------------------------------- */
+
+BUILD_TC(use, "macros_h_test.c",
+ "Tests that the macros provided by the atf-c/macros.h file "
+ "do not cause syntax errors when used",
+ "Build of macros_h_test.c failed; some macros in atf-c/macros.h "
+ "are broken");
+
+ATF_TC(detect_unused_tests);
+ATF_TC_HEAD(detect_unused_tests, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Tests that defining an unused test case raises a "
+ "warning (and thus an error)");
+}
+ATF_TC_BODY(detect_unused_tests, tc)
+{
+ const char* validate_compiler =
+ "struct test_struct { int dummy; };\n"
+ "#define define_unused static struct test_struct unused\n"
+ "define_unused;\n";
+
+ atf_utils_create_file("compiler_test.c", "%s", validate_compiler);
+ if (build_check_c_o("compiler_test.c"))
+ atf_tc_expect_fail("Compiler does not raise a warning on an unused "
+ "static global variable declared by a macro");
+
+ if (build_check_c_o_srcdir(tc, "unused_test.c"))
+ atf_tc_fail("Build of unused_test.c passed; unused test cases are "
+ "not properly detected");
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, check);
+ ATF_TP_ADD_TC(tp, check_eq);
+ ATF_TP_ADD_TC(tp, check_streq);
+ ATF_TP_ADD_TC(tp, check_errno);
+ ATF_TP_ADD_TC(tp, check_match);
+
+ ATF_TP_ADD_TC(tp, require);
+ ATF_TP_ADD_TC(tp, require_eq);
+ ATF_TP_ADD_TC(tp, require_streq);
+ ATF_TP_ADD_TC(tp, require_errno);
+ ATF_TP_ADD_TC(tp, require_match);
+
+ ATF_TP_ADD_TC(tp, msg_embedded_fmt);
+
+ /* Add the test cases for the header file. */
+ ATF_TP_ADD_TC(tp, use);
+ ATF_TP_ADD_TC(tp, detect_unused_tests);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/pkg_config_test.sh b/unit/atf-src/atf-c/pkg_config_test.sh
new file mode 100644
index 0000000..ddffdb0
--- /dev/null
+++ b/unit/atf-src/atf-c/pkg_config_test.sh
@@ -0,0 +1,143 @@
+# Copyright (c) 2008 The NetBSD Foundation, Inc.
+# 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+
+# The following tests assume that the atf-c.pc file is installed in a
+# directory that is known by pkg-config. Otherwise they will fail,
+# and you will be required to adjust PKG_CONFIG_PATH accordingly.
+#
+# It would be possible to bypass this requirement by setting the path
+# explicitly during the tests, but then this would not do a real check
+# to ensure that the installation is working.
+
+require_pc()
+{
+ pkg-config ${1} || atf_fail "pkg-config could not locate ${1}.pc;" \
+ "maybe need to set PKG_CONFIG_PATH?"
+}
+
+check_version()
+{
+ ver1=$($(atf_get_srcdir)/detail/version_helper)
+ echo "Version reported by builtin PACKAGE_VERSION: ${ver1}"
+
+ atf_check -s eq:0 -o save:stdout -e empty pkg-config --modversion "${1}"
+ ver2=$(cat stdout)
+ echo "Version reported by pkg-config: ${ver2}"
+
+ atf_check_equal ${ver1} ${ver2}
+}
+
+atf_test_case version
+version_head()
+{
+ atf_set "descr" "Checks that the version in atf-c is correct"
+ atf_set "require.progs" "pkg-config"
+}
+version_body()
+{
+ require_pc "atf-c"
+
+ check_version "atf-c"
+}
+
+atf_test_case build
+build_head()
+{
+ atf_set "descr" "Checks that a test program can be built against" \
+ "the C library based on the pkg-config information"
+ atf_set "require.progs" "pkg-config"
+}
+build_body()
+{
+ require_pc "atf-c"
+
+ atf_check -s eq:0 -o save:stdout -e empty pkg-config --variable=cc atf-c
+ cc=$(cat stdout)
+ echo "Compiler is: ${cxx}"
+ atf_require_prog ${cxx}
+
+ cat >tp.c <<EOF
+#include <stdio.h>
+
+#include <atf-c.h>
+
+ATF_TC(tc);
+ATF_TC_HEAD(tc, tc) {
+ atf_tc_set_md_var(tc, "descr", "A test case");
+}
+ATF_TC_BODY(tc, tc) {
+ printf("Running\n");
+}
+
+ATF_TP_ADD_TCS(tp) {
+ ATF_TP_ADD_TC(tp, tc);
+
+ return atf_no_error();
+}
+EOF
+
+ atf_check -s eq:0 -o save:stdout -e empty pkg-config --cflags atf-c
+ cflags=$(cat stdout)
+ echo "CFLAGS are: ${cflags}"
+
+ atf_check -s eq:0 -o save:stdout -e empty \
+ pkg-config --libs-only-L --libs-only-other atf-c
+ ldflags=$(cat stdout)
+ atf_check -s eq:0 -o save:stdout -e empty pkg-config --libs-only-l atf-c
+ libs=$(cat stdout)
+ echo "LDFLAGS are: ${ldflags}"
+ echo "LIBS are: ${libs}"
+
+ atf_check -s eq:0 -o empty -e empty ${cc} ${cflags} -o tp.o -c tp.c
+ atf_check -s eq:0 -o empty -e empty ${cc} ${ldflags} -o tp tp.o ${libs}
+
+ libpath=
+ for f in ${ldflags}; do
+ case ${f} in
+ -L*)
+ dir=$(echo ${f} | sed -e 's,^-L,,')
+ if [ -z "${libpath}" ]; then
+ libpath="${dir}"
+ else
+ libpath="${libpath}:${dir}"
+ fi
+ ;;
+ *)
+ ;;
+ esac
+ done
+
+ atf_check -s eq:0 -o empty -e empty test -x tp
+ atf_check -s eq:0 -o match:'Running' -e empty -x \
+ "LD_LIBRARY_PATH=${libpath} ./tp tc"
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case version
+ atf_add_test_case build
+}
+
+# vim: syntax=sh:expandtab:shiftwidth=4:softtabstop=4
diff --git a/unit/atf-src/atf-c/tc.c b/unit/atf-src/atf-c/tc.c
new file mode 100644
index 0000000..f75d059
--- /dev/null
+++ b/unit/atf-src/atf-c/tc.c
@@ -0,0 +1,1219 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/tc.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atf-c/defs.h"
+#include "atf-c/detail/env.h"
+#include "atf-c/detail/fs.h"
+#include "atf-c/detail/map.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/detail/text.h"
+#include "atf-c/error.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+enum expect_type {
+ EXPECT_PASS,
+ EXPECT_FAIL,
+ EXPECT_EXIT,
+ EXPECT_SIGNAL,
+ EXPECT_DEATH,
+ EXPECT_TIMEOUT,
+};
+
+struct context {
+ const atf_tc_t *tc;
+ const char *resfile;
+ size_t fail_count;
+
+ enum expect_type expect;
+ atf_dynstr_t expect_reason;
+ size_t expect_previous_fail_count;
+ size_t expect_fail_count;
+ int expect_exitcode;
+ int expect_signo;
+};
+
+static void context_init(struct context *, const atf_tc_t *, const char *);
+static void check_fatal_error(atf_error_t);
+static void report_fatal_error(const char *, ...)
+ ATF_DEFS_ATTRIBUTE_NORETURN;
+static atf_error_t write_resfile(const int, const char *, const int,
+ const atf_dynstr_t *);
+static void create_resfile(const char *, const char *, const int,
+ atf_dynstr_t *);
+static void error_in_expect(struct context *, const char *, ...)
+ ATF_DEFS_ATTRIBUTE_NORETURN;
+static void validate_expect(struct context *);
+static void expected_failure(struct context *, atf_dynstr_t *)
+ ATF_DEFS_ATTRIBUTE_NORETURN;
+static void fail_requirement(struct context *, atf_dynstr_t *)
+ ATF_DEFS_ATTRIBUTE_NORETURN;
+static void fail_check(struct context *, atf_dynstr_t *);
+static void pass(struct context *)
+ ATF_DEFS_ATTRIBUTE_NORETURN;
+static void skip(struct context *, atf_dynstr_t *)
+ ATF_DEFS_ATTRIBUTE_NORETURN;
+static void format_reason_ap(atf_dynstr_t *, const char *, const size_t,
+ const char *, va_list);
+static void format_reason_fmt(atf_dynstr_t *, const char *, const size_t,
+ const char *, ...);
+static void errno_test(struct context *, const char *, const size_t,
+ const int, const char *, const bool,
+ void (*)(struct context *, atf_dynstr_t *));
+static atf_error_t check_prog_in_dir(const char *, void *);
+static atf_error_t check_prog(struct context *, const char *);
+
+static void
+context_init(struct context *ctx, const atf_tc_t *tc, const char *resfile)
+{
+ ctx->tc = tc;
+ ctx->resfile = resfile;
+ ctx->fail_count = 0;
+ ctx->expect = EXPECT_PASS;
+ check_fatal_error(atf_dynstr_init(&ctx->expect_reason));
+ ctx->expect_previous_fail_count = 0;
+ ctx->expect_fail_count = 0;
+ ctx->expect_exitcode = 0;
+ ctx->expect_signo = 0;
+}
+
+static void
+check_fatal_error(atf_error_t err)
+{
+ if (atf_is_error(err)) {
+ char buf[1024];
+ atf_error_format(err, buf, sizeof(buf));
+ fprintf(stderr, "FATAL ERROR: %s\n", buf);
+ atf_error_free(err);
+ abort();
+ }
+}
+
+static void
+report_fatal_error(const char *msg, ...)
+{
+ va_list ap;
+ fprintf(stderr, "FATAL ERROR: ");
+
+ va_start(ap, msg);
+ vfprintf(stderr, msg, ap);
+ va_end(ap);
+
+ fprintf(stderr, "\n");
+ abort();
+}
+
+/** Writes to a results file.
+ *
+ * The results file is supposed to be already open.
+ *
+ * This function returns an error code instead of exiting in case of error
+ * because the caller needs to clean up the reason object before terminating.
+ */
+static atf_error_t
+write_resfile(const int fd, const char *result, const int arg,
+ const atf_dynstr_t *reason)
+{
+ static char NL[] = "\n", CS[] = ": ";
+ char buf[64];
+ const char *r;
+ struct iovec iov[5];
+ ssize_t ret;
+ int count = 0;
+
+ INV(arg == -1 || reason != NULL);
+
+#define UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+ iov[count].iov_base = UNCONST(result);
+ iov[count++].iov_len = strlen(result);
+
+ if (reason != NULL) {
+ if (arg != -1) {
+ iov[count].iov_base = buf;
+ iov[count++].iov_len = snprintf(buf, sizeof(buf), "(%d)", arg);
+ }
+
+ iov[count].iov_base = CS;
+ iov[count++].iov_len = sizeof(CS) - 1;
+
+ r = atf_dynstr_cstring(reason);
+ iov[count].iov_base = UNCONST(r);
+ iov[count++].iov_len = strlen(r);
+ }
+#undef UNCONST
+
+ iov[count].iov_base = NL;
+ iov[count++].iov_len = sizeof(NL) - 1;
+
+ while ((ret = writev(fd, iov, count)) == -1 && errno == EINTR)
+ continue; /* Retry. */
+ if (ret != -1)
+ return atf_no_error();
+
+ return atf_libc_error(
+ errno, "Failed to write results file; result %s, reason %s", result,
+ reason == NULL ? "null" : atf_dynstr_cstring(reason));
+}
+
+/** Creates a results file.
+ *
+ * The input reason is released in all cases.
+ *
+ * An error in this function is considered to be fatal, hence why it does
+ * not return any error code.
+ */
+static void
+create_resfile(const char *resfile, const char *result, const int arg,
+ atf_dynstr_t *reason)
+{
+ atf_error_t err;
+
+ if (strcmp("/dev/stdout", resfile) == 0) {
+ err = write_resfile(STDOUT_FILENO, result, arg, reason);
+ } else if (strcmp("/dev/stderr", resfile) == 0) {
+ err = write_resfile(STDERR_FILENO, result, arg, reason);
+ } else {
+ const int fd = open(resfile, O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd == -1) {
+ err = atf_libc_error(errno, "Cannot create results file '%s'",
+ resfile);
+ } else {
+ err = write_resfile(fd, result, arg, reason);
+ close(fd);
+ }
+ }
+
+ if (reason != NULL)
+ atf_dynstr_fini(reason);
+
+ check_fatal_error(err);
+}
+
+/** Fails a test case if validate_expect fails. */
+static void
+error_in_expect(struct context *ctx, const char *fmt, ...)
+{
+ atf_dynstr_t reason;
+ va_list ap;
+
+ va_start(ap, fmt);
+ format_reason_ap(&reason, NULL, 0, fmt, ap);
+ va_end(ap);
+
+ ctx->expect = EXPECT_PASS; /* Ensure fail_requirement really fails. */
+ fail_requirement(ctx, &reason);
+}
+
+/** Ensures that the "expect" state is correct.
+ *
+ * Call this function before modifying the current value of expect.
+ */
+static void
+validate_expect(struct context *ctx)
+{
+ if (ctx->expect == EXPECT_DEATH) {
+ error_in_expect(ctx, "Test case was expected to terminate abruptly "
+ "but it continued execution");
+ } else if (ctx->expect == EXPECT_EXIT) {
+ error_in_expect(ctx, "Test case was expected to exit cleanly but it "
+ "continued execution");
+ } else if (ctx->expect == EXPECT_FAIL) {
+ if (ctx->expect_fail_count == ctx->expect_previous_fail_count)
+ error_in_expect(ctx, "Test case was expecting a failure but none "
+ "were raised");
+ else
+ INV(ctx->expect_fail_count > ctx->expect_previous_fail_count);
+ } else if (ctx->expect == EXPECT_PASS) {
+ /* Nothing to validate. */
+ } else if (ctx->expect == EXPECT_SIGNAL) {
+ error_in_expect(ctx, "Test case was expected to receive a termination "
+ "signal but it continued execution");
+ } else if (ctx->expect == EXPECT_TIMEOUT) {
+ error_in_expect(ctx, "Test case was expected to hang but it continued "
+ "execution");
+ } else
+ UNREACHABLE;
+}
+
+static void
+expected_failure(struct context *ctx, atf_dynstr_t *reason)
+{
+ check_fatal_error(atf_dynstr_prepend_fmt(reason, "%s: ",
+ atf_dynstr_cstring(&ctx->expect_reason)));
+ create_resfile(ctx->resfile, "expected_failure", -1, reason);
+ exit(EXIT_SUCCESS);
+}
+
+static void
+fail_requirement(struct context *ctx, atf_dynstr_t *reason)
+{
+ if (ctx->expect == EXPECT_FAIL) {
+ expected_failure(ctx, reason);
+ } else if (ctx->expect == EXPECT_PASS) {
+ create_resfile(ctx->resfile, "failed", -1, reason);
+ exit(EXIT_FAILURE);
+ } else {
+ error_in_expect(ctx, "Test case raised a failure but was not "
+ "expecting one; reason was %s", atf_dynstr_cstring(reason));
+ }
+ UNREACHABLE;
+}
+
+static void
+fail_check(struct context *ctx, atf_dynstr_t *reason)
+{
+ if (ctx->expect == EXPECT_FAIL) {
+ fprintf(stderr, "*** Expected check failure: %s: %s\n",
+ atf_dynstr_cstring(&ctx->expect_reason),
+ atf_dynstr_cstring(reason));
+ ctx->expect_fail_count++;
+ } else if (ctx->expect == EXPECT_PASS) {
+ fprintf(stderr, "*** Check failed: %s\n", atf_dynstr_cstring(reason));
+ ctx->fail_count++;
+ } else {
+ error_in_expect(ctx, "Test case raised a failure but was not "
+ "expecting one; reason was %s", atf_dynstr_cstring(reason));
+ }
+
+ atf_dynstr_fini(reason);
+}
+
+static void
+pass(struct context *ctx)
+{
+ if (ctx->expect == EXPECT_FAIL) {
+ error_in_expect(ctx, "Test case was expecting a failure but got "
+ "a pass instead");
+ } else if (ctx->expect == EXPECT_PASS) {
+ create_resfile(ctx->resfile, "passed", -1, NULL);
+ exit(EXIT_SUCCESS);
+ } else {
+ error_in_expect(ctx, "Test case asked to explicitly pass but was "
+ "not expecting such condition");
+ }
+ UNREACHABLE;
+}
+
+static void
+skip(struct context *ctx, atf_dynstr_t *reason)
+{
+ if (ctx->expect == EXPECT_PASS) {
+ create_resfile(ctx->resfile, "skipped", -1, reason);
+ exit(EXIT_SUCCESS);
+ } else {
+ error_in_expect(ctx, "Can only skip a test case when running in "
+ "expect pass mode");
+ }
+ UNREACHABLE;
+}
+
+/** Formats a failure/skip reason message.
+ *
+ * The formatted reason is stored in out_reason. out_reason is initialized
+ * in this function and is supposed to be released by the caller. In general,
+ * the reason will eventually be fed to create_resfile, which will release
+ * it.
+ *
+ * Errors in this function are fatal. Rationale being: reasons are used to
+ * create results files; if we can't format the reason correctly, the result
+ * of the test program will be bogus. So it's better to just exit with a
+ * fatal error.
+ */
+static void
+format_reason_ap(atf_dynstr_t *out_reason,
+ const char *source_file, const size_t source_line,
+ const char *reason, va_list ap)
+{
+ atf_error_t err;
+
+ if (source_file != NULL) {
+ err = atf_dynstr_init_fmt(out_reason, "%s:%zd: ", source_file,
+ source_line);
+ } else {
+ PRE(source_line == 0);
+ err = atf_dynstr_init(out_reason);
+ }
+
+ if (!atf_is_error(err)) {
+ va_list ap2;
+ va_copy(ap2, ap);
+ err = atf_dynstr_append_ap(out_reason, reason, ap2);
+ va_end(ap2);
+ }
+
+ check_fatal_error(err);
+}
+
+static void
+format_reason_fmt(atf_dynstr_t *out_reason,
+ const char *source_file, const size_t source_line,
+ const char *reason, ...)
+{
+ va_list ap;
+
+ va_start(ap, reason);
+ format_reason_ap(out_reason, source_file, source_line, reason, ap);
+ va_end(ap);
+}
+
+static void
+errno_test(struct context *ctx, const char *file, const size_t line,
+ const int exp_errno, const char *expr_str,
+ const bool expr_result,
+ void (*fail_func)(struct context *, atf_dynstr_t *))
+{
+ const int actual_errno = errno;
+
+ if (expr_result) {
+ if (exp_errno != actual_errno) {
+ atf_dynstr_t reason;
+
+ format_reason_fmt(&reason, file, line, "Expected errno %d, got %d, "
+ "in %s", exp_errno, actual_errno, expr_str);
+ fail_func(ctx, &reason);
+ }
+ } else {
+ atf_dynstr_t reason;
+
+ format_reason_fmt(&reason, file, line, "Expected true value in %s",
+ expr_str);
+ fail_func(ctx, &reason);
+ }
+}
+
+struct prog_found_pair {
+ const char *prog;
+ bool found;
+};
+
+static atf_error_t
+check_prog_in_dir(const char *dir, void *data)
+{
+ struct prog_found_pair *pf = data;
+ atf_error_t err;
+
+ if (pf->found)
+ err = atf_no_error();
+ else {
+ atf_fs_path_t p;
+
+ err = atf_fs_path_init_fmt(&p, "%s/%s", dir, pf->prog);
+ if (atf_is_error(err))
+ goto out_p;
+
+ err = atf_fs_eaccess(&p, atf_fs_access_x);
+ if (!atf_is_error(err))
+ pf->found = true;
+ else {
+ atf_error_free(err);
+ INV(!pf->found);
+ err = atf_no_error();
+ }
+
+out_p:
+ atf_fs_path_fini(&p);
+ }
+
+ return err;
+}
+
+static atf_error_t
+check_prog(struct context *ctx, const char *prog)
+{
+ atf_error_t err;
+ atf_fs_path_t p;
+
+ err = atf_fs_path_init_fmt(&p, "%s", prog);
+ if (atf_is_error(err))
+ goto out;
+
+ if (atf_fs_path_is_absolute(&p)) {
+ err = atf_fs_eaccess(&p, atf_fs_access_x);
+ if (atf_is_error(err)) {
+ atf_dynstr_t reason;
+
+ atf_error_free(err);
+ atf_fs_path_fini(&p);
+ format_reason_fmt(&reason, NULL, 0, "The required program %s could "
+ "not be found", prog);
+ skip(ctx, &reason);
+ }
+ } else {
+ const char *path = atf_env_get("PATH");
+ struct prog_found_pair pf;
+ atf_fs_path_t bp;
+
+ err = atf_fs_path_branch_path(&p, &bp);
+ if (atf_is_error(err))
+ goto out_p;
+
+ if (strcmp(atf_fs_path_cstring(&bp), ".") != 0) {
+ atf_fs_path_fini(&bp);
+ atf_fs_path_fini(&p);
+
+ report_fatal_error("Relative paths are not allowed when searching "
+ "for a program (%s)", prog);
+ UNREACHABLE;
+ }
+
+ pf.prog = prog;
+ pf.found = false;
+ err = atf_text_for_each_word(path, ":", check_prog_in_dir, &pf);
+ if (atf_is_error(err))
+ goto out_bp;
+
+ if (!pf.found) {
+ atf_dynstr_t reason;
+
+ atf_fs_path_fini(&bp);
+ atf_fs_path_fini(&p);
+ format_reason_fmt(&reason, NULL, 0, "The required program %s could "
+ "not be found in the PATH", prog);
+ fail_requirement(ctx, &reason);
+ }
+
+out_bp:
+ atf_fs_path_fini(&bp);
+ }
+
+out_p:
+ atf_fs_path_fini(&p);
+out:
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_tc" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_tc_impl {
+ const char *m_ident;
+
+ atf_map_t m_vars;
+ atf_map_t m_config;
+
+ atf_tc_head_t m_head;
+ atf_tc_body_t m_body;
+ atf_tc_cleanup_t m_cleanup;
+};
+
+/*
+ * Constructors/destructors.
+ */
+
+atf_error_t
+atf_tc_init(atf_tc_t *tc, const char *ident, atf_tc_head_t head,
+ atf_tc_body_t body, atf_tc_cleanup_t cleanup,
+ const char *const *config)
+{
+ atf_error_t err;
+
+ tc->pimpl = malloc(sizeof(struct atf_tc_impl));
+ if (tc->pimpl == NULL) {
+ err = atf_no_memory_error();
+ goto err;
+ }
+
+ tc->pimpl->m_ident = ident;
+ tc->pimpl->m_head = head;
+ tc->pimpl->m_body = body;
+ tc->pimpl->m_cleanup = cleanup;
+
+ err = atf_map_init_charpp(&tc->pimpl->m_config, config);
+ if (atf_is_error(err))
+ goto err;
+
+ err = atf_map_init(&tc->pimpl->m_vars);
+ if (atf_is_error(err))
+ goto err_vars;
+
+ err = atf_tc_set_md_var(tc, "ident", ident);
+ if (atf_is_error(err))
+ goto err_map;
+
+ if (cleanup != NULL) {
+ err = atf_tc_set_md_var(tc, "has.cleanup", "true");
+ if (atf_is_error(err))
+ goto err_map;
+ }
+
+ /* XXX Should the head be able to return error codes? */
+ if (tc->pimpl->m_head != NULL)
+ tc->pimpl->m_head(tc);
+
+ if (strcmp(atf_tc_get_md_var(tc, "ident"), ident) != 0) {
+ report_fatal_error("Test case head modified the read-only 'ident' "
+ "property");
+ UNREACHABLE;
+ }
+
+ INV(!atf_is_error(err));
+ return err;
+
+err_map:
+ atf_map_fini(&tc->pimpl->m_vars);
+err_vars:
+ atf_map_fini(&tc->pimpl->m_config);
+err:
+ return err;
+}
+
+atf_error_t
+atf_tc_init_pack(atf_tc_t *tc, const atf_tc_pack_t *pack,
+ const char *const *config)
+{
+ return atf_tc_init(tc, pack->m_ident, pack->m_head, pack->m_body,
+ pack->m_cleanup, config);
+}
+
+void
+atf_tc_fini(atf_tc_t *tc)
+{
+ atf_map_fini(&tc->pimpl->m_vars);
+ atf_map_fini(&tc->pimpl->m_config);
+ free(tc->pimpl);
+ tc->pimpl = NULL;
+}
+
+/*
+ * Getters.
+ */
+
+const char *
+atf_tc_get_ident(const atf_tc_t *tc)
+{
+ return tc->pimpl->m_ident;
+}
+
+const char *
+atf_tc_get_config_var(const atf_tc_t *tc, const char *name)
+{
+ const char *val;
+ atf_map_citer_t iter;
+
+ PRE(atf_tc_has_config_var(tc, name));
+ iter = atf_map_find_c(&tc->pimpl->m_config, name);
+ val = atf_map_citer_data(iter);
+ INV(val != NULL);
+
+ return val;
+}
+
+const char *
+atf_tc_get_config_var_wd(const atf_tc_t *tc, const char *name,
+ const char *defval)
+{
+ const char *val;
+
+ if (!atf_tc_has_config_var(tc, name))
+ val = defval;
+ else
+ val = atf_tc_get_config_var(tc, name);
+
+ return val;
+}
+
+bool
+atf_tc_get_config_var_as_bool(const atf_tc_t *tc, const char *name)
+{
+ bool val;
+ const char *strval;
+ atf_error_t err;
+
+ strval = atf_tc_get_config_var(tc, name);
+ err = atf_text_to_bool(strval, &val);
+ if (atf_is_error(err)) {
+ atf_error_free(err);
+ atf_tc_fail("Configuration variable %s does not have a valid "
+ "boolean value; found %s", name, strval);
+ }
+
+ return val;
+}
+
+bool
+atf_tc_get_config_var_as_bool_wd(const atf_tc_t *tc, const char *name,
+ const bool defval)
+{
+ bool val;
+
+ if (!atf_tc_has_config_var(tc, name))
+ val = defval;
+ else
+ val = atf_tc_get_config_var_as_bool(tc, name);
+
+ return val;
+}
+
+long
+atf_tc_get_config_var_as_long(const atf_tc_t *tc, const char *name)
+{
+ long val;
+ const char *strval;
+ atf_error_t err;
+
+ strval = atf_tc_get_config_var(tc, name);
+ err = atf_text_to_long(strval, &val);
+ if (atf_is_error(err)) {
+ atf_error_free(err);
+ atf_tc_fail("Configuration variable %s does not have a valid "
+ "long value; found %s", name, strval);
+ }
+
+ return val;
+}
+
+long
+atf_tc_get_config_var_as_long_wd(const atf_tc_t *tc, const char *name,
+ const long defval)
+{
+ long val;
+
+ if (!atf_tc_has_config_var(tc, name))
+ val = defval;
+ else
+ val = atf_tc_get_config_var_as_long(tc, name);
+
+ return val;
+}
+
+const char *
+atf_tc_get_md_var(const atf_tc_t *tc, const char *name)
+{
+ const char *val;
+ atf_map_citer_t iter;
+
+ PRE(atf_tc_has_md_var(tc, name));
+ iter = atf_map_find_c(&tc->pimpl->m_vars, name);
+ val = atf_map_citer_data(iter);
+ INV(val != NULL);
+
+ return val;
+}
+
+char **
+atf_tc_get_md_vars(const atf_tc_t *tc)
+{
+ return atf_map_to_charpp(&tc->pimpl->m_vars);
+}
+
+bool
+atf_tc_has_config_var(const atf_tc_t *tc, const char *name)
+{
+ atf_map_citer_t end, iter;
+
+ iter = atf_map_find_c(&tc->pimpl->m_config, name);
+ end = atf_map_end_c(&tc->pimpl->m_config);
+ return !atf_equal_map_citer_map_citer(iter, end);
+}
+
+bool
+atf_tc_has_md_var(const atf_tc_t *tc, const char *name)
+{
+ atf_map_citer_t end, iter;
+
+ iter = atf_map_find_c(&tc->pimpl->m_vars, name);
+ end = atf_map_end_c(&tc->pimpl->m_vars);
+ return !atf_equal_map_citer_map_citer(iter, end);
+}
+
+/*
+ * Modifiers.
+ */
+
+atf_error_t
+atf_tc_set_md_var(atf_tc_t *tc, const char *name, const char *fmt, ...)
+{
+ atf_error_t err;
+ char *value;
+ va_list ap;
+
+ va_start(ap, fmt);
+ err = atf_text_format_ap(&value, fmt, ap);
+ va_end(ap);
+
+ if (!atf_is_error(err))
+ err = atf_map_insert(&tc->pimpl->m_vars, name, value, true);
+ else
+ free(value);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * Free functions, as they should be publicly but they can't.
+ * --------------------------------------------------------------------- */
+
+static void _atf_tc_fail(struct context *, const char *, va_list)
+ ATF_DEFS_ATTRIBUTE_NORETURN;
+static void _atf_tc_fail_nonfatal(struct context *, const char *, va_list);
+static void _atf_tc_fail_check(struct context *, const char *, const size_t,
+ const char *, va_list);
+static void _atf_tc_fail_requirement(struct context *, const char *,
+ const size_t, const char *, va_list) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void _atf_tc_pass(struct context *) ATF_DEFS_ATTRIBUTE_NORETURN;
+static void _atf_tc_require_prog(struct context *, const char *);
+static void _atf_tc_skip(struct context *, const char *, va_list)
+ ATF_DEFS_ATTRIBUTE_NORETURN;
+static void _atf_tc_check_errno(struct context *, const char *, const size_t,
+ const int, const char *, const bool);
+static void _atf_tc_require_errno(struct context *, const char *, const size_t,
+ const int, const char *, const bool);
+static void _atf_tc_expect_pass(struct context *);
+static void _atf_tc_expect_fail(struct context *, const char *, va_list);
+static void _atf_tc_expect_exit(struct context *, const int, const char *,
+ va_list);
+static void _atf_tc_expect_signal(struct context *, const int, const char *,
+ va_list);
+static void _atf_tc_expect_death(struct context *, const char *,
+ va_list);
+
+static void
+_atf_tc_fail(struct context *ctx, const char *fmt, va_list ap)
+{
+ va_list ap2;
+ atf_dynstr_t reason;
+
+ va_copy(ap2, ap);
+ format_reason_ap(&reason, NULL, 0, fmt, ap2);
+ va_end(ap2);
+
+ fail_requirement(ctx, &reason);
+ UNREACHABLE;
+}
+
+static void
+_atf_tc_fail_nonfatal(struct context *ctx, const char *fmt, va_list ap)
+{
+ va_list ap2;
+ atf_dynstr_t reason;
+
+ va_copy(ap2, ap);
+ format_reason_ap(&reason, NULL, 0, fmt, ap2);
+ va_end(ap2);
+
+ fail_check(ctx, &reason);
+}
+
+static void
+_atf_tc_fail_check(struct context *ctx, const char *file, const size_t line,
+ const char *fmt, va_list ap)
+{
+ va_list ap2;
+ atf_dynstr_t reason;
+
+ va_copy(ap2, ap);
+ format_reason_ap(&reason, file, line, fmt, ap2);
+ va_end(ap2);
+
+ fail_check(ctx, &reason);
+}
+
+static void
+_atf_tc_fail_requirement(struct context *ctx, const char *file,
+ const size_t line, const char *fmt, va_list ap)
+{
+ va_list ap2;
+ atf_dynstr_t reason;
+
+ va_copy(ap2, ap);
+ format_reason_ap(&reason, file, line, fmt, ap2);
+ va_end(ap2);
+
+ fail_requirement(ctx, &reason);
+ UNREACHABLE;
+}
+
+static void
+_atf_tc_pass(struct context *ctx)
+{
+ pass(ctx);
+ UNREACHABLE;
+}
+
+static void
+_atf_tc_require_prog(struct context *ctx, const char *prog)
+{
+ check_fatal_error(check_prog(ctx, prog));
+}
+
+static void
+_atf_tc_skip(struct context *ctx, const char *fmt, va_list ap)
+{
+ atf_dynstr_t reason;
+ va_list ap2;
+
+ va_copy(ap2, ap);
+ format_reason_ap(&reason, NULL, 0, fmt, ap2);
+ va_end(ap2);
+
+ skip(ctx, &reason);
+}
+
+static void
+_atf_tc_check_errno(struct context *ctx, const char *file, const size_t line,
+ const int exp_errno, const char *expr_str,
+ const bool expr_result)
+{
+ errno_test(ctx, file, line, exp_errno, expr_str, expr_result, fail_check);
+}
+
+static void
+_atf_tc_require_errno(struct context *ctx, const char *file, const size_t line,
+ const int exp_errno, const char *expr_str,
+ const bool expr_result)
+{
+ errno_test(ctx, file, line, exp_errno, expr_str, expr_result,
+ fail_requirement);
+}
+
+static void
+_atf_tc_expect_pass(struct context *ctx)
+{
+ validate_expect(ctx);
+
+ ctx->expect = EXPECT_PASS;
+}
+
+static void
+_atf_tc_expect_fail(struct context *ctx, const char *reason, va_list ap)
+{
+ va_list ap2;
+
+ validate_expect(ctx);
+
+ ctx->expect = EXPECT_FAIL;
+ atf_dynstr_fini(&ctx->expect_reason);
+ va_copy(ap2, ap);
+ check_fatal_error(atf_dynstr_init_ap(&ctx->expect_reason, reason, ap2));
+ va_end(ap2);
+ ctx->expect_previous_fail_count = ctx->expect_fail_count;
+}
+
+static void
+_atf_tc_expect_exit(struct context *ctx, const int exitcode, const char *reason,
+ va_list ap)
+{
+ va_list ap2;
+ atf_dynstr_t formatted;
+
+ validate_expect(ctx);
+
+ ctx->expect = EXPECT_EXIT;
+ va_copy(ap2, ap);
+ check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2));
+ va_end(ap2);
+
+ create_resfile(ctx->resfile, "expected_exit", exitcode, &formatted);
+}
+
+static void
+_atf_tc_expect_signal(struct context *ctx, const int signo, const char *reason,
+ va_list ap)
+{
+ va_list ap2;
+ atf_dynstr_t formatted;
+
+ validate_expect(ctx);
+
+ ctx->expect = EXPECT_SIGNAL;
+ va_copy(ap2, ap);
+ check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2));
+ va_end(ap2);
+
+ create_resfile(ctx->resfile, "expected_signal", signo, &formatted);
+}
+
+static void
+_atf_tc_expect_death(struct context *ctx, const char *reason, va_list ap)
+{
+ va_list ap2;
+ atf_dynstr_t formatted;
+
+ validate_expect(ctx);
+
+ ctx->expect = EXPECT_DEATH;
+ va_copy(ap2, ap);
+ check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2));
+ va_end(ap2);
+
+ create_resfile(ctx->resfile, "expected_death", -1, &formatted);
+}
+
+static void
+_atf_tc_expect_timeout(struct context *ctx, const char *reason, va_list ap)
+{
+ va_list ap2;
+ atf_dynstr_t formatted;
+
+ validate_expect(ctx);
+
+ ctx->expect = EXPECT_TIMEOUT;
+ va_copy(ap2, ap);
+ check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2));
+ va_end(ap2);
+
+ create_resfile(ctx->resfile, "expected_timeout", -1, &formatted);
+}
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+static struct context Current;
+
+atf_error_t
+atf_tc_run(const atf_tc_t *tc, const char *resfile)
+{
+ context_init(&Current, tc, resfile);
+
+ tc->pimpl->m_body(tc);
+
+ validate_expect(&Current);
+
+ if (Current.fail_count > 0) {
+ atf_dynstr_t reason;
+
+ format_reason_fmt(&reason, NULL, 0, "%d checks failed; see output for "
+ "more details", Current.fail_count);
+ fail_requirement(&Current, &reason);
+ } else if (Current.expect_fail_count > 0) {
+ atf_dynstr_t reason;
+
+ format_reason_fmt(&reason, NULL, 0, "%d checks failed as expected; "
+ "see output for more details", Current.expect_fail_count);
+ expected_failure(&Current, &reason);
+ } else {
+ pass(&Current);
+ }
+ UNREACHABLE;
+ return atf_no_error();
+}
+
+atf_error_t
+atf_tc_cleanup(const atf_tc_t *tc)
+{
+ if (tc->pimpl->m_cleanup != NULL)
+ tc->pimpl->m_cleanup(tc);
+ return atf_no_error(); /* XXX */
+}
+
+/* ---------------------------------------------------------------------
+ * Free functions that depend on Current.
+ * --------------------------------------------------------------------- */
+
+/*
+ * All the functions below provide delegates to other internal functions
+ * (prefixed by _) that take the current test case as an argument to
+ * prevent them from accessing global state. This is to keep the side-
+ * effects of the internal functions clearer and easier to understand.
+ *
+ * The public API should never have hid the fact that it needs access to
+ * the current test case (other than maybe in the macros), but changing it
+ * is hard. TODO: Revisit in the future.
+ */
+
+void
+atf_tc_fail(const char *fmt, ...)
+{
+ va_list ap;
+
+ PRE(Current.tc != NULL);
+
+ va_start(ap, fmt);
+ _atf_tc_fail(&Current, fmt, ap);
+ va_end(ap);
+}
+
+void
+atf_tc_fail_nonfatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ PRE(Current.tc != NULL);
+
+ va_start(ap, fmt);
+ _atf_tc_fail_nonfatal(&Current, fmt, ap);
+ va_end(ap);
+}
+
+void
+atf_tc_fail_check(const char *file, const size_t line, const char *fmt, ...)
+{
+ va_list ap;
+
+ PRE(Current.tc != NULL);
+
+ va_start(ap, fmt);
+ _atf_tc_fail_check(&Current, file, line, fmt, ap);
+ va_end(ap);
+}
+
+void
+atf_tc_fail_requirement(const char *file, const size_t line,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ PRE(Current.tc != NULL);
+
+ va_start(ap, fmt);
+ _atf_tc_fail_requirement(&Current, file, line, fmt, ap);
+ va_end(ap);
+}
+
+void
+atf_tc_pass(void)
+{
+ PRE(Current.tc != NULL);
+
+ _atf_tc_pass(&Current);
+}
+
+void
+atf_tc_require_prog(const char *prog)
+{
+ PRE(Current.tc != NULL);
+
+ _atf_tc_require_prog(&Current, prog);
+}
+
+void
+atf_tc_skip(const char *fmt, ...)
+{
+ va_list ap;
+
+ PRE(Current.tc != NULL);
+
+ va_start(ap, fmt);
+ _atf_tc_skip(&Current, fmt, ap);
+ va_end(ap);
+}
+
+void
+atf_tc_check_errno(const char *file, const size_t line, const int exp_errno,
+ const char *expr_str, const bool expr_result)
+{
+ PRE(Current.tc != NULL);
+
+ _atf_tc_check_errno(&Current, file, line, exp_errno, expr_str,
+ expr_result);
+}
+
+void
+atf_tc_require_errno(const char *file, const size_t line, const int exp_errno,
+ const char *expr_str, const bool expr_result)
+{
+ PRE(Current.tc != NULL);
+
+ _atf_tc_require_errno(&Current, file, line, exp_errno, expr_str,
+ expr_result);
+}
+
+void
+atf_tc_expect_pass(void)
+{
+ PRE(Current.tc != NULL);
+
+ _atf_tc_expect_pass(&Current);
+}
+
+void
+atf_tc_expect_fail(const char *reason, ...)
+{
+ va_list ap;
+
+ PRE(Current.tc != NULL);
+
+ va_start(ap, reason);
+ _atf_tc_expect_fail(&Current, reason, ap);
+ va_end(ap);
+}
+
+void
+atf_tc_expect_exit(const int exitcode, const char *reason, ...)
+{
+ va_list ap;
+
+ PRE(Current.tc != NULL);
+
+ va_start(ap, reason);
+ _atf_tc_expect_exit(&Current, exitcode, reason, ap);
+ va_end(ap);
+}
+
+void
+atf_tc_expect_signal(const int signo, const char *reason, ...)
+{
+ va_list ap;
+
+ PRE(Current.tc != NULL);
+
+ va_start(ap, reason);
+ _atf_tc_expect_signal(&Current, signo, reason, ap);
+ va_end(ap);
+}
+
+void
+atf_tc_expect_death(const char *reason, ...)
+{
+ va_list ap;
+
+ PRE(Current.tc != NULL);
+
+ va_start(ap, reason);
+ _atf_tc_expect_death(&Current, reason, ap);
+ va_end(ap);
+}
+
+void
+atf_tc_expect_timeout(const char *reason, ...)
+{
+ va_list ap;
+
+ PRE(Current.tc != NULL);
+
+ va_start(ap, reason);
+ _atf_tc_expect_timeout(&Current, reason, ap);
+ va_end(ap);
+}
diff --git a/unit/atf-src/atf-c/tc.h b/unit/atf-src/atf-c/tc.h
new file mode 100644
index 0000000..3fb6955
--- /dev/null
+++ b/unit/atf-src/atf-c/tc.h
@@ -0,0 +1,136 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_TC_H)
+#define ATF_C_TC_H
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#include <atf-c/defs.h>
+#include <atf-c/error_fwd.h>
+
+struct atf_tc;
+
+typedef void (*atf_tc_head_t)(struct atf_tc *);
+typedef void (*atf_tc_body_t)(const struct atf_tc *);
+typedef void (*atf_tc_cleanup_t)(const struct atf_tc *);
+
+/* ---------------------------------------------------------------------
+ * The "atf_tc_pack" type.
+ * --------------------------------------------------------------------- */
+
+/* For static initialization only. */
+struct atf_tc_pack {
+ const char *m_ident;
+
+ const char *const *m_config;
+
+ atf_tc_head_t m_head;
+ atf_tc_body_t m_body;
+ atf_tc_cleanup_t m_cleanup;
+};
+typedef const struct atf_tc_pack atf_tc_pack_t;
+
+/* ---------------------------------------------------------------------
+ * The "atf_tc" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_tc_impl;
+struct atf_tc {
+ struct atf_tc_impl *pimpl;
+};
+typedef struct atf_tc atf_tc_t;
+
+/* Constructors/destructors. */
+atf_error_t atf_tc_init(atf_tc_t *, const char *, atf_tc_head_t,
+ atf_tc_body_t, atf_tc_cleanup_t,
+ const char *const *);
+atf_error_t atf_tc_init_pack(atf_tc_t *, atf_tc_pack_t *,
+ const char *const *);
+void atf_tc_fini(atf_tc_t *);
+
+/* Getters. */
+const char *atf_tc_get_ident(const atf_tc_t *);
+const char *atf_tc_get_config_var(const atf_tc_t *, const char *);
+const char *atf_tc_get_config_var_wd(const atf_tc_t *, const char *,
+ const char *);
+bool atf_tc_get_config_var_as_bool(const atf_tc_t *, const char *);
+bool atf_tc_get_config_var_as_bool_wd(const atf_tc_t *, const char *,
+ const bool);
+long atf_tc_get_config_var_as_long(const atf_tc_t *, const char *);
+long atf_tc_get_config_var_as_long_wd(const atf_tc_t *, const char *,
+ const long);
+const char *atf_tc_get_md_var(const atf_tc_t *, const char *);
+char **atf_tc_get_md_vars(const atf_tc_t *);
+bool atf_tc_has_config_var(const atf_tc_t *, const char *);
+bool atf_tc_has_md_var(const atf_tc_t *, const char *);
+
+/* Modifiers. */
+atf_error_t atf_tc_set_md_var(atf_tc_t *, const char *, const char *, ...);
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+atf_error_t atf_tc_run(const atf_tc_t *, const char *);
+atf_error_t atf_tc_cleanup(const atf_tc_t *);
+
+/* To be run from test case bodies only. */
+void atf_tc_fail(const char *, ...)
+ ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 2)
+ ATF_DEFS_ATTRIBUTE_NORETURN;
+void atf_tc_fail_nonfatal(const char *, ...)
+ ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 2);
+void atf_tc_pass(void)
+ ATF_DEFS_ATTRIBUTE_NORETURN;
+void atf_tc_require_prog(const char *);
+void atf_tc_skip(const char *, ...)
+ ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 2)
+ ATF_DEFS_ATTRIBUTE_NORETURN;
+void atf_tc_expect_pass(void);
+void atf_tc_expect_fail(const char *, ...)
+ ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 2);
+void atf_tc_expect_exit(const int, const char *, ...)
+ ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(2, 3);
+void atf_tc_expect_signal(const int, const char *, ...)
+ ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(2, 3);
+void atf_tc_expect_death(const char *, ...)
+ ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 2);
+void atf_tc_expect_timeout(const char *, ...)
+ ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 2);
+
+/* To be run from test case bodies only; internal to macros.h. */
+void atf_tc_fail_check(const char *, const size_t, const char *, ...)
+ ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(3, 4);
+void atf_tc_fail_requirement(const char *, const size_t, const char *, ...)
+ ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(3, 4)
+ ATF_DEFS_ATTRIBUTE_NORETURN;
+void atf_tc_check_errno(const char *, const size_t, const int,
+ const char *, const bool);
+void atf_tc_require_errno(const char *, const size_t, const int,
+ const char *, const bool);
+
+#endif /* !defined(ATF_C_TC_H) */
diff --git a/unit/atf-src/atf-c/tc_test.c b/unit/atf-src/atf-c/tc_test.c
new file mode 100644
index 0000000..616c37e
--- /dev/null
+++ b/unit/atf-src/atf-c/tc_test.c
@@ -0,0 +1,183 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/tc.h"
+
+#include <stdbool.h>
+#include <string.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/test_helpers.h"
+
+/* ---------------------------------------------------------------------
+ * Auxiliary test cases.
+ * --------------------------------------------------------------------- */
+
+ATF_TC_HEAD(empty, tc)
+{
+ if (tc != NULL) {}
+}
+ATF_TC_BODY(empty, tc)
+{
+}
+
+ATF_TC_HEAD(test_var, tc)
+{
+ atf_tc_set_md_var(tc, "test-var", "Test text");
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the "atf_tc_t" type.
+ * --------------------------------------------------------------------- */
+
+ATF_TC(init);
+ATF_TC_HEAD(init, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_tc_init function");
+}
+ATF_TC_BODY(init, tcin)
+{
+ atf_tc_t tc;
+
+ RE(atf_tc_init(&tc, "test1", ATF_TC_HEAD_NAME(empty),
+ ATF_TC_BODY_NAME(empty), NULL, NULL));
+ ATF_REQUIRE(strcmp(atf_tc_get_ident(&tc), "test1") == 0);
+ ATF_REQUIRE(!atf_tc_has_md_var(&tc, "test-var"));
+ atf_tc_fini(&tc);
+
+ RE(atf_tc_init(&tc, "test2", ATF_TC_HEAD_NAME(test_var),
+ ATF_TC_BODY_NAME(empty), NULL, NULL));
+ ATF_REQUIRE(strcmp(atf_tc_get_ident(&tc), "test2") == 0);
+ ATF_REQUIRE(atf_tc_has_md_var(&tc, "test-var"));
+ atf_tc_fini(&tc);
+}
+
+ATF_TC(init_pack);
+ATF_TC_HEAD(init_pack, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_tc_init_pack function");
+}
+ATF_TC_BODY(init_pack, tcin)
+{
+ atf_tc_t tc;
+ atf_tc_pack_t tcp1 = {
+ .m_ident = "test1",
+ .m_head = ATF_TC_HEAD_NAME(empty),
+ .m_body = ATF_TC_BODY_NAME(empty),
+ .m_cleanup = NULL,
+ };
+ atf_tc_pack_t tcp2 = {
+ .m_ident = "test2",
+ .m_head = ATF_TC_HEAD_NAME(test_var),
+ .m_body = ATF_TC_BODY_NAME(empty),
+ .m_cleanup = NULL,
+ };
+
+ RE(atf_tc_init_pack(&tc, &tcp1, NULL));
+ ATF_REQUIRE(strcmp(atf_tc_get_ident(&tc), "test1") == 0);
+ ATF_REQUIRE(!atf_tc_has_md_var(&tc, "test-var"));
+ atf_tc_fini(&tc);
+
+ RE(atf_tc_init_pack(&tc, &tcp2, NULL));
+ ATF_REQUIRE(strcmp(atf_tc_get_ident(&tc), "test2") == 0);
+ ATF_REQUIRE(atf_tc_has_md_var(&tc, "test-var"));
+ atf_tc_fini(&tc);
+}
+
+ATF_TC(vars);
+ATF_TC_HEAD(vars, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_tc_get_md_var, "
+ "atf_tc_has_md_var and atf_tc_set_md_var functions");
+}
+ATF_TC_BODY(vars, tcin)
+{
+ atf_tc_t tc;
+
+ RE(atf_tc_init(&tc, "test1", ATF_TC_HEAD_NAME(empty),
+ ATF_TC_BODY_NAME(empty), NULL, NULL));
+ ATF_REQUIRE(!atf_tc_has_md_var(&tc, "test-var"));
+ RE(atf_tc_set_md_var(&tc, "test-var", "Test value"));
+ ATF_REQUIRE(atf_tc_has_md_var(&tc, "test-var"));
+ ATF_REQUIRE(strcmp(atf_tc_get_md_var(&tc, "test-var"), "Test value") == 0);
+ atf_tc_fini(&tc);
+}
+
+ATF_TC(config);
+ATF_TC_HEAD(config, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Tests the atf_tc_get_config_var, "
+ "atf_tc_get_config_var_wd and atf_tc_has_config_var "
+ "functions");
+}
+ATF_TC_BODY(config, tcin)
+{
+ atf_tc_t tc;
+ const char *const config[] = { "test-var", "test-value", NULL };
+
+ RE(atf_tc_init(&tc, "test1", ATF_TC_HEAD_NAME(empty),
+ ATF_TC_BODY_NAME(empty), NULL, NULL));
+ ATF_REQUIRE(!atf_tc_has_config_var(&tc, "test-var"));
+ ATF_REQUIRE(!atf_tc_has_md_var(&tc, "test-var"));
+ atf_tc_fini(&tc);
+
+ RE(atf_tc_init(&tc, "test1", ATF_TC_HEAD_NAME(empty),
+ ATF_TC_BODY_NAME(empty), NULL, config));
+ ATF_REQUIRE(atf_tc_has_config_var(&tc, "test-var"));
+ ATF_REQUIRE(strcmp(atf_tc_get_config_var(&tc, "test-var"),
+ "test-value") == 0);
+ ATF_REQUIRE(!atf_tc_has_md_var(&tc, "test-var"));
+ ATF_REQUIRE(!atf_tc_has_config_var(&tc, "test-var2"));
+ ATF_REQUIRE(strcmp(atf_tc_get_config_var_wd(&tc, "test-var2", "def-value"),
+ "def-value") == 0);
+ atf_tc_fini(&tc);
+}
+
+/* ---------------------------------------------------------------------
+ * Test cases for the free functions.
+ * --------------------------------------------------------------------- */
+
+/* TODO: Add test cases for atf_tc_run. This is going to be very tough,
+ * but good tests here could allow us to avoid much of the indirect
+ * testing done later on. */
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ /* Add the test cases for the "atf_tcr_t" type. */
+ ATF_TP_ADD_TC(tp, init);
+ ATF_TP_ADD_TC(tp, init_pack);
+ ATF_TP_ADD_TC(tp, vars);
+ ATF_TP_ADD_TC(tp, config);
+
+ /* Add the test cases for the free functions. */
+ /* TODO */
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/tp.c b/unit/atf-src/atf-c/tp.c
new file mode 100644
index 0000000..f4f2257
--- /dev/null
+++ b/unit/atf-src/atf-c/tp.c
@@ -0,0 +1,213 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/tp.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "atf-c/detail/fs.h"
+#include "atf-c/detail/map.h"
+#include "atf-c/detail/sanity.h"
+#include "atf-c/error.h"
+#include "atf-c/tc.h"
+
+struct atf_tp_impl {
+ atf_list_t m_tcs;
+ atf_map_t m_config;
+};
+
+/* ---------------------------------------------------------------------
+ * Auxiliary functions.
+ * --------------------------------------------------------------------- */
+
+static
+const atf_tc_t *
+find_tc(const atf_tp_t *tp, const char *ident)
+{
+ const atf_tc_t *tc;
+ atf_list_citer_t iter;
+
+ tc = NULL;
+ atf_list_for_each_c(iter, &tp->pimpl->m_tcs) {
+ const atf_tc_t *tc2;
+ tc2 = atf_list_citer_data(iter);
+ if (strcmp(atf_tc_get_ident(tc2), ident) == 0) {
+ tc = tc2;
+ break;
+ }
+ }
+ return tc;
+}
+
+/* ---------------------------------------------------------------------
+ * The "atf_tp" type.
+ * --------------------------------------------------------------------- */
+
+/*
+ * Constructors/destructors.
+ */
+
+atf_error_t
+atf_tp_init(atf_tp_t *tp, const char *const *config)
+{
+ atf_error_t err;
+
+ PRE(config != NULL);
+
+ tp->pimpl = malloc(sizeof(struct atf_tp_impl));
+ if (tp->pimpl == NULL)
+ return atf_no_memory_error();
+
+ err = atf_list_init(&tp->pimpl->m_tcs);
+ if (atf_is_error(err))
+ goto out;
+
+ err = atf_map_init_charpp(&tp->pimpl->m_config, config);
+ if (atf_is_error(err)) {
+ atf_list_fini(&tp->pimpl->m_tcs);
+ goto out;
+ }
+
+ INV(!atf_is_error(err));
+out:
+ return err;
+}
+
+void
+atf_tp_fini(atf_tp_t *tp)
+{
+ atf_list_iter_t iter;
+
+ atf_map_fini(&tp->pimpl->m_config);
+
+ atf_list_for_each(iter, &tp->pimpl->m_tcs) {
+ atf_tc_t *tc = atf_list_iter_data(iter);
+ atf_tc_fini(tc);
+ }
+ atf_list_fini(&tp->pimpl->m_tcs);
+
+ free(tp->pimpl);
+}
+
+/*
+ * Getters.
+ */
+
+char **
+atf_tp_get_config(const atf_tp_t *tp)
+{
+ return atf_map_to_charpp(&tp->pimpl->m_config);
+}
+
+bool
+atf_tp_has_tc(const atf_tp_t *tp, const char *id)
+{
+ const atf_tc_t *tc = find_tc(tp, id);
+ return tc != NULL;
+}
+
+const atf_tc_t *
+atf_tp_get_tc(const atf_tp_t *tp, const char *id)
+{
+ const atf_tc_t *tc = find_tc(tp, id);
+ PRE(tc != NULL);
+ return tc;
+}
+
+const atf_tc_t *const *
+atf_tp_get_tcs(const atf_tp_t *tp)
+{
+ const atf_tc_t **array;
+ atf_list_citer_t iter;
+ size_t i;
+
+ array = malloc(sizeof(atf_tc_t *) *
+ (atf_list_size(&tp->pimpl->m_tcs) + 1));
+ if (array == NULL)
+ goto out;
+
+ i = 0;
+ atf_list_for_each_c(iter, &tp->pimpl->m_tcs) {
+ array[i] = atf_list_citer_data(iter);
+ if (array[i] == NULL) {
+ free(array);
+ array = NULL;
+ goto out;
+ }
+
+ i++;
+ }
+ array[i] = NULL;
+
+out:
+ return array;
+}
+
+/*
+ * Modifiers.
+ */
+
+atf_error_t
+atf_tp_add_tc(atf_tp_t *tp, atf_tc_t *tc)
+{
+ atf_error_t err;
+
+ PRE(find_tc(tp, atf_tc_get_ident(tc)) == NULL);
+
+ err = atf_list_append(&tp->pimpl->m_tcs, tc, false);
+
+ POST(find_tc(tp, atf_tc_get_ident(tc)) != NULL);
+
+ return err;
+}
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+atf_error_t
+atf_tp_run(const atf_tp_t *tp, const char *tcname, const char *resfile)
+{
+ const atf_tc_t *tc;
+
+ tc = find_tc(tp, tcname);
+ PRE(tc != NULL);
+
+ return atf_tc_run(tc, resfile);
+}
+
+atf_error_t
+atf_tp_cleanup(const atf_tp_t *tp, const char *tcname)
+{
+ const atf_tc_t *tc;
+
+ tc = find_tc(tp, tcname);
+ PRE(tc != NULL);
+
+ return atf_tc_cleanup(tc);
+}
diff --git a/unit/atf-src/atf-c/tp.h b/unit/atf-src/atf-c/tp.h
new file mode 100644
index 0000000..c176f7d
--- /dev/null
+++ b/unit/atf-src/atf-c/tp.h
@@ -0,0 +1,65 @@
+/* Copyright (c) 2008 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_TP_H)
+#define ATF_C_TP_H
+
+#include <stdbool.h>
+
+#include <atf-c/error_fwd.h>
+
+struct atf_tc;
+
+/* ---------------------------------------------------------------------
+ * The "atf_tp" type.
+ * --------------------------------------------------------------------- */
+
+struct atf_tp_impl;
+struct atf_tp {
+ struct atf_tp_impl *pimpl;
+};
+typedef struct atf_tp atf_tp_t;
+
+/* Constructors/destructors. */
+atf_error_t atf_tp_init(atf_tp_t *, const char *const *);
+void atf_tp_fini(atf_tp_t *);
+
+/* Getters. */
+char **atf_tp_get_config(const atf_tp_t *);
+bool atf_tp_has_tc(const atf_tp_t *, const char *);
+const struct atf_tc *atf_tp_get_tc(const atf_tp_t *, const char *);
+const struct atf_tc *const *atf_tp_get_tcs(const atf_tp_t *);
+
+/* Modifiers. */
+atf_error_t atf_tp_add_tc(atf_tp_t *, struct atf_tc *);
+
+/* ---------------------------------------------------------------------
+ * Free functions.
+ * --------------------------------------------------------------------- */
+
+atf_error_t atf_tp_run(const atf_tp_t *, const char *, const char *);
+atf_error_t atf_tp_cleanup(const atf_tp_t *, const char *);
+
+#endif /* !defined(ATF_C_TP_H) */
diff --git a/unit/atf-src/atf-c/tp_test.c b/unit/atf-src/atf-c/tp_test.c
new file mode 100644
index 0000000..de334a9
--- /dev/null
+++ b/unit/atf-src/atf-c/tp_test.c
@@ -0,0 +1,88 @@
+/* Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/tp.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+ATF_TC(getopt);
+ATF_TC_HEAD(getopt, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks if getopt(3) global state is "
+ "reset by the test program driver so that test cases can use "
+ "getopt(3) again");
+}
+ATF_TC_BODY(getopt, tc)
+{
+ /* Provide an option that is unknown to the test program driver and
+ * one that is, together with an argument that would be swallowed by
+ * the test program option if it were recognized. */
+ int argc = 4;
+ char arg1[] = "progname";
+ char arg2[] = "-Z";
+ char arg3[] = "-s";
+ char arg4[] = "foo";
+ char *const argv[] = { arg1, arg2, arg3, arg4, NULL };
+
+ int ch;
+ bool zflag;
+
+ /* Given that this obviously is a test program, and that we used the
+ * same driver to start, we can test getopt(3) right here without doing
+ * any fancy stuff. */
+ zflag = false;
+ while ((ch = getopt(argc, argv, ":Z")) != -1) {
+ switch (ch) {
+ case 'Z':
+ zflag = true;
+ break;
+
+ case '?':
+ default:
+ if (optopt != 's')
+ atf_tc_fail("Unexpected unknown option -%c found", optopt);
+ }
+ }
+
+ ATF_REQUIRE(zflag);
+ ATF_REQUIRE_EQ_MSG(1, argc - optind, "Invalid number of arguments left "
+ "after the call to getopt(3)");
+ ATF_CHECK_STREQ_MSG("foo", argv[optind], "The non-option argument is "
+ "invalid");
+}
+
+/* ---------------------------------------------------------------------
+ * Main.
+ * --------------------------------------------------------------------- */
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, getopt);
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/unused_test.c b/unit/atf-src/atf-c/unused_test.c
new file mode 100644
index 0000000..1c5fda9
--- /dev/null
+++ b/unit/atf-src/atf-c/unused_test.c
@@ -0,0 +1,52 @@
+/* Copyright (c) 2012 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <atf-c.h>
+
+ATF_TC(this_is_used);
+ATF_TC_HEAD(this_is_used, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "A test case that is not referenced");
+}
+ATF_TC_BODY(this_is_used, tc)
+{
+}
+
+ATF_TC(this_is_unused);
+ATF_TC_HEAD(this_is_unused, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "A test case that is referenced");
+}
+ATF_TC_BODY(this_is_unused, tc)
+{
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, this_is_used);
+ /* ATF_TP_ADD_TC(tp, this_is_unused); */
+
+ return atf_no_error();
+}
diff --git a/unit/atf-src/atf-c/utils.c b/unit/atf-src/atf-c/utils.c
new file mode 100644
index 0000000..1e2aac1
--- /dev/null
+++ b/unit/atf-src/atf-c/utils.c
@@ -0,0 +1,456 @@
+/* Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/utils.h"
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/dynstr.h"
+
+/** Allocate a filename to be used by atf_utils_{fork,wait}.
+ *
+ * In case of a failure, marks the calling test as failed when in_parent is
+ * true, else terminates execution.
+ *
+ * \param [out] name String to contain the generated file.
+ * \param pid PID of the process that will write to the file.
+ * \param suffix Either "out" or "err".
+ * \param in_parent If true, fail with atf_tc_fail; else use err(3). */
+static void
+init_out_filename(atf_dynstr_t *name, const pid_t pid, const char *suffix,
+ const bool in_parent)
+{
+ atf_error_t error;
+
+ error = atf_dynstr_init_fmt(name, "atf_utils_fork_%d_%s.txt",
+ (int)pid, suffix);
+ if (atf_is_error(error)) {
+ char buffer[1024];
+ atf_error_format(error, buffer, sizeof(buffer));
+ if (in_parent) {
+ atf_tc_fail("Failed to create output file: %s", buffer);
+ } else {
+ err(EXIT_FAILURE, "Failed to create output file: %s", buffer);
+ }
+ }
+}
+
+/** Searches for a regexp in a string.
+ *
+ * \param regex The regexp to look for.
+ * \param str The string in which to look for the expression.
+ *
+ * \return True if there is a match; false otherwise. */
+static
+bool
+grep_string(const char *regex, const char *str)
+{
+ int res;
+ regex_t preg;
+
+ printf("Looking for '%s' in '%s'\n", regex, str);
+ ATF_REQUIRE(regcomp(&preg, regex, REG_EXTENDED) == 0);
+
+ res = regexec(&preg, str, 0, NULL, 0);
+ ATF_REQUIRE(res == 0 || res == REG_NOMATCH);
+
+ regfree(&preg);
+
+ return res == 0;
+}
+
+/** Prints the contents of a file to stdout.
+ *
+ * \param name The name of the file to be printed.
+ * \param prefix An string to be prepended to every line of the printed
+ * file. */
+void
+atf_utils_cat_file(const char *name, const char *prefix)
+{
+ const int fd = open(name, O_RDONLY);
+ ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", name);
+
+ char buffer[1024];
+ ssize_t count;
+ bool continued = false;
+ while ((count = read(fd, buffer, sizeof(buffer) - 1)) > 0) {
+ buffer[count] = '\0';
+
+ if (!continued)
+ printf("%s", prefix);
+
+ char *iter = buffer;
+ char *end;
+ while ((end = strchr(iter, '\n')) != NULL) {
+ *end = '\0';
+ printf("%s\n", iter);
+
+ iter = end + 1;
+ if (iter != buffer + count)
+ printf("%s", prefix);
+ else
+ continued = false;
+ }
+ if (iter < buffer + count) {
+ printf("%s", iter);
+ continued = true;
+ }
+ }
+ ATF_REQUIRE(count == 0);
+}
+
+/** Compares a file against the given golden contents.
+ *
+ * \param name Name of the file to be compared.
+ * \param contents Expected contents of the file.
+ *
+ * \return True if the file matches the contents; false otherwise. */
+bool
+atf_utils_compare_file(const char *name, const char *contents)
+{
+ const int fd = open(name, O_RDONLY);
+ ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", name);
+
+ const char *pos = contents;
+ ssize_t remaining = strlen(contents);
+
+ char buffer[1024];
+ ssize_t count;
+ while ((count = read(fd, buffer, sizeof(buffer))) > 0 &&
+ count <= remaining) {
+ if (memcmp(pos, buffer, count) != 0) {
+ close(fd);
+ return false;
+ }
+ remaining -= count;
+ pos += count;
+ }
+ close(fd);
+ return count == 0 && remaining == 0;
+}
+
+/** Copies a file.
+ *
+ * \param source Path to the source file.
+ * \param destination Path to the destination file. */
+void
+atf_utils_copy_file(const char *source, const char *destination)
+{
+ const int input = open(source, O_RDONLY);
+ ATF_REQUIRE_MSG(input != -1, "Failed to open source file during "
+ "copy (%s)", source);
+
+ const int output = open(destination, O_WRONLY | O_CREAT | O_TRUNC, 0777);
+ ATF_REQUIRE_MSG(output != -1, "Failed to open destination file during "
+ "copy (%s)", destination);
+
+ char buffer[1024];
+ ssize_t length;
+ while ((length = read(input, buffer, sizeof(buffer))) > 0)
+ ATF_REQUIRE_MSG(write(output, buffer, length) == length,
+ "Failed to write to %s during copy", destination);
+ ATF_REQUIRE_MSG(length != -1, "Failed to read from %s during copy", source);
+
+ struct stat sb;
+ ATF_REQUIRE_MSG(fstat(input, &sb) != -1,
+ "Failed to stat source file %s during copy", source);
+ ATF_REQUIRE_MSG(fchmod(output, sb.st_mode) != -1,
+ "Failed to chmod destination file %s during copy",
+ destination);
+
+ close(output);
+ close(input);
+}
+
+/** Creates a file.
+ *
+ * \param name Name of the file to create.
+ * \param contents Text to write into the created file.
+ * \param ... Positional parameters to the contents. */
+void
+atf_utils_create_file(const char *name, const char *contents, ...)
+{
+ va_list ap;
+ atf_dynstr_t formatted;
+ atf_error_t error;
+
+ va_start(ap, contents);
+ error = atf_dynstr_init_ap(&formatted, contents, ap);
+ va_end(ap);
+ ATF_REQUIRE(!atf_is_error(error));
+
+ const int fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ ATF_REQUIRE_MSG(fd != -1, "Cannot create file %s", name);
+ ATF_REQUIRE(write(fd, atf_dynstr_cstring(&formatted),
+ atf_dynstr_length(&formatted)) != -1);
+ close(fd);
+
+ atf_dynstr_fini(&formatted);
+}
+
+/** Checks if a file exists.
+ *
+ * \param path Location of the file to check for.
+ *
+ * \return True if the file exists, false otherwise. */
+bool
+atf_utils_file_exists(const char *path)
+{
+ const int ret = access(path, F_OK);
+ if (ret == -1) {
+ if (errno != ENOENT)
+ atf_tc_fail("Failed to check the existence of %s: %s", path,
+ strerror(errno));
+ else
+ return false;
+ } else
+ return true;
+}
+
+/** Spawns a subprocess and redirects its output to files.
+ *
+ * Use the atf_utils_wait() function to wait for the completion of the spawned
+ * subprocess and validate its exit conditions.
+ *
+ * \return 0 in the new child; the PID of the new child in the parent. Does
+ * not return in error conditions. */
+pid_t
+atf_utils_fork(void)
+{
+ const pid_t pid = fork();
+ if (pid == -1)
+ atf_tc_fail("fork failed");
+
+ if (pid == 0) {
+ atf_dynstr_t out_name;
+ init_out_filename(&out_name, getpid(), "out", false);
+
+ atf_dynstr_t err_name;
+ init_out_filename(&err_name, getpid(), "err", false);
+
+ atf_utils_redirect(STDOUT_FILENO, atf_dynstr_cstring(&out_name));
+ atf_utils_redirect(STDERR_FILENO, atf_dynstr_cstring(&err_name));
+
+ atf_dynstr_fini(&err_name);
+ atf_dynstr_fini(&out_name);
+ }
+ return pid;
+}
+
+/** Frees an dynamically-allocated "argv" array.
+ *
+ * \param argv A dynamically-allocated array of dynamically-allocated
+ * strings. */
+void
+atf_utils_free_charpp(char **argv)
+{
+ char **ptr;
+
+ for (ptr = argv; *ptr != NULL; ptr++)
+ free(*ptr);
+
+ free(argv);
+}
+
+/** Searches for a regexp in a file.
+ *
+ * \param regex The regexp to look for.
+ * \param file The file in which to look for the expression.
+ * \param ... Positional parameters to the regex.
+ *
+ * \return True if there is a match; false otherwise. */
+bool
+atf_utils_grep_file(const char *regex, const char *file, ...)
+{
+ int fd;
+ va_list ap;
+ atf_dynstr_t formatted;
+ atf_error_t error;
+
+ va_start(ap, file);
+ error = atf_dynstr_init_ap(&formatted, regex, ap);
+ va_end(ap);
+ ATF_REQUIRE(!atf_is_error(error));
+
+ ATF_REQUIRE((fd = open(file, O_RDONLY)) != -1);
+ bool found = false;
+ char *line = NULL;
+ while (!found && (line = atf_utils_readline(fd)) != NULL) {
+ found = grep_string(atf_dynstr_cstring(&formatted), line);
+ free(line);
+ }
+ close(fd);
+
+ atf_dynstr_fini(&formatted);
+
+ return found;
+}
+
+/** Searches for a regexp in a string.
+ *
+ * \param regex The regexp to look for.
+ * \param str The string in which to look for the expression.
+ * \param ... Positional parameters to the regex.
+ *
+ * \return True if there is a match; false otherwise. */
+bool
+atf_utils_grep_string(const char *regex, const char *str, ...)
+{
+ bool res;
+ va_list ap;
+ atf_dynstr_t formatted;
+ atf_error_t error;
+
+ va_start(ap, str);
+ error = atf_dynstr_init_ap(&formatted, regex, ap);
+ va_end(ap);
+ ATF_REQUIRE(!atf_is_error(error));
+
+ res = grep_string(atf_dynstr_cstring(&formatted), str);
+
+ atf_dynstr_fini(&formatted);
+
+ return res;
+}
+
+/** Reads a line of arbitrary length.
+ *
+ * \param fd The descriptor from which to read the line.
+ *
+ * \return A pointer to the read line, which must be released with free(), or
+ * NULL if there was nothing to read from the file. */
+char *
+atf_utils_readline(const int fd)
+{
+ char ch;
+ ssize_t cnt;
+ atf_dynstr_t temp;
+ atf_error_t error;
+
+ error = atf_dynstr_init(&temp);
+ ATF_REQUIRE(!atf_is_error(error));
+
+ while ((cnt = read(fd, &ch, sizeof(ch))) == sizeof(ch) &&
+ ch != '\n') {
+ error = atf_dynstr_append_fmt(&temp, "%c", ch);
+ ATF_REQUIRE(!atf_is_error(error));
+ }
+ ATF_REQUIRE(cnt != -1);
+
+ if (cnt == 0 && atf_dynstr_length(&temp) == 0) {
+ atf_dynstr_fini(&temp);
+ return NULL;
+ } else
+ return atf_dynstr_fini_disown(&temp);
+}
+
+/** Redirects a file descriptor to a file.
+ *
+ * \param target_fd The file descriptor to be replaced.
+ * \param name The name of the file to direct the descriptor to.
+ *
+ * \pre Should only be called from the process spawned by fork_for_testing
+ * because this exits uncontrolledly.
+ * \post Terminates execution if the redirection fails. */
+void
+atf_utils_redirect(const int target_fd, const char *name)
+{
+ if (target_fd == STDOUT_FILENO)
+ fflush(stdout);
+ else if (target_fd == STDERR_FILENO)
+ fflush(stderr);
+
+ const int new_fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (new_fd == -1)
+ err(EXIT_FAILURE, "Cannot create %s", name);
+ if (new_fd != target_fd) {
+ if (dup2(new_fd, target_fd) == -1)
+ err(EXIT_FAILURE, "Cannot redirect to fd %d", target_fd);
+ }
+ close(new_fd);
+}
+
+/** Waits for a subprocess and validates its exit condition.
+ *
+ * \param pid The process to be waited for. Must have been started by
+ * testutils_fork().
+ * \param exitstatus Expected exit status.
+ * \param expout Expected contents of stdout.
+ * \param experr Expected contents of stderr. */
+void
+atf_utils_wait(const pid_t pid, const int exitstatus, const char *expout,
+ const char *experr)
+{
+ int status;
+ ATF_REQUIRE(waitpid(pid, &status, 0) != -1);
+
+ atf_dynstr_t out_name;
+ init_out_filename(&out_name, pid, "out", true);
+
+ atf_dynstr_t err_name;
+ init_out_filename(&err_name, pid, "err", true);
+
+ atf_utils_cat_file(atf_dynstr_cstring(&out_name), "subprocess stdout: ");
+ atf_utils_cat_file(atf_dynstr_cstring(&err_name), "subprocess stderr: ");
+
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(exitstatus, WEXITSTATUS(status));
+
+ const char *save_prefix = "save:";
+ const size_t save_prefix_length = strlen(save_prefix);
+
+ if (strlen(expout) > save_prefix_length &&
+ strncmp(expout, save_prefix, save_prefix_length) == 0) {
+ atf_utils_copy_file(atf_dynstr_cstring(&out_name),
+ expout + save_prefix_length);
+ } else {
+ ATF_REQUIRE(atf_utils_compare_file(atf_dynstr_cstring(&out_name),
+ expout));
+ }
+
+ if (strlen(experr) > save_prefix_length &&
+ strncmp(experr, save_prefix, save_prefix_length) == 0) {
+ atf_utils_copy_file(atf_dynstr_cstring(&err_name),
+ experr + save_prefix_length);
+ } else {
+ ATF_REQUIRE(atf_utils_compare_file(atf_dynstr_cstring(&err_name),
+ experr));
+ }
+
+ ATF_REQUIRE(unlink(atf_dynstr_cstring(&out_name)) != -1);
+ ATF_REQUIRE(unlink(atf_dynstr_cstring(&err_name)) != -1);
+}
diff --git a/unit/atf-src/atf-c/utils.h b/unit/atf-src/atf-c/utils.h
new file mode 100644
index 0000000..e4162b2
--- /dev/null
+++ b/unit/atf-src/atf-c/utils.h
@@ -0,0 +1,50 @@
+/* Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */
+
+#if !defined(ATF_C_UTILS_H)
+#define ATF_C_UTILS_H
+
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <atf-c/defs.h>
+
+void atf_utils_cat_file(const char *, const char *);
+bool atf_utils_compare_file(const char *, const char *);
+void atf_utils_copy_file(const char *, const char *);
+void atf_utils_create_file(const char *, const char *, ...)
+ ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(2, 3);
+bool atf_utils_file_exists(const char *);
+pid_t atf_utils_fork(void);
+void atf_utils_free_charpp(char **);
+bool atf_utils_grep_file(const char *, const char *, ...)
+ ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 3);
+bool atf_utils_grep_string(const char *, const char *, ...)
+ ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 3);
+char *atf_utils_readline(int);
+void atf_utils_redirect(const int, const char *);
+void atf_utils_wait(const pid_t, const int, const char *, const char *);
+
+#endif /* !defined(ATF_C_UTILS_H) */
diff --git a/unit/atf-src/atf-c/utils_test.c b/unit/atf-src/atf-c/utils_test.c
new file mode 100644
index 0000000..fb81cd3
--- /dev/null
+++ b/unit/atf-src/atf-c/utils_test.c
@@ -0,0 +1,570 @@
+/* Copyright (c) 2010 The NetBSD Foundation, Inc.
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 "atf-c/utils.h"
+
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+#include "atf-c/detail/dynstr.h"
+#include "atf-c/detail/test_helpers.h"
+
+/** Reads the contents of a file into a buffer.
+ *
+ * Up to buflen-1 characters are read into buffer. If this function returns,
+ * the contents read into the buffer are guaranteed to be nul-terminated.
+ * Note, however, that if the file contains any nul characters itself,
+ * comparing it "as a string" will not work.
+ *
+ * \param path The file to be read, which must exist.
+ * \param buffer Buffer into which to store the file contents.
+ * \param buflen Size of the target buffer.
+ *
+ * \return The count of bytes read. */
+static ssize_t
+read_file(const char *path, void *const buffer, const size_t buflen)
+{
+ const int fd = open(path, O_RDONLY);
+ ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", path);
+ const ssize_t length = read(fd, buffer, buflen - 1);
+ close(fd);
+ ATF_REQUIRE(length != -1);
+ ((char *)buffer)[length] = '\0';
+ return length;
+}
+
+ATF_TC_WITHOUT_HEAD(cat_file__empty);
+ATF_TC_BODY(cat_file__empty, tc)
+{
+ atf_utils_create_file("file.txt", "%s", "");
+ atf_utils_redirect(STDOUT_FILENO, "captured.txt");
+ atf_utils_cat_file("file.txt", "PREFIX");
+ fflush(stdout);
+ close(STDOUT_FILENO);
+
+ char buffer[1024];
+ read_file("captured.txt", buffer, sizeof(buffer));
+ ATF_REQUIRE_STREQ("", buffer);
+}
+
+ATF_TC_WITHOUT_HEAD(cat_file__one_line);
+ATF_TC_BODY(cat_file__one_line, tc)
+{
+ atf_utils_create_file("file.txt", "This is a single line\n");
+ atf_utils_redirect(STDOUT_FILENO, "captured.txt");
+ atf_utils_cat_file("file.txt", "PREFIX");
+ fflush(stdout);
+ close(STDOUT_FILENO);
+
+ char buffer[1024];
+ read_file("captured.txt", buffer, sizeof(buffer));
+ ATF_REQUIRE_STREQ("PREFIXThis is a single line\n", buffer);
+}
+
+ATF_TC_WITHOUT_HEAD(cat_file__several_lines);
+ATF_TC_BODY(cat_file__several_lines, tc)
+{
+ atf_utils_create_file("file.txt", "First\nSecond line\nAnd third\n");
+ atf_utils_redirect(STDOUT_FILENO, "captured.txt");
+ atf_utils_cat_file("file.txt", ">");
+ fflush(stdout);
+ close(STDOUT_FILENO);
+
+ char buffer[1024];
+ read_file("captured.txt", buffer, sizeof(buffer));
+ ATF_REQUIRE_STREQ(">First\n>Second line\n>And third\n", buffer);
+}
+
+ATF_TC_WITHOUT_HEAD(cat_file__no_newline_eof);
+ATF_TC_BODY(cat_file__no_newline_eof, tc)
+{
+ atf_utils_create_file("file.txt", "Foo\n bar baz");
+ atf_utils_redirect(STDOUT_FILENO, "captured.txt");
+ atf_utils_cat_file("file.txt", "PREFIX");
+ fflush(stdout);
+ close(STDOUT_FILENO);
+
+ char buffer[1024];
+ read_file("captured.txt", buffer, sizeof(buffer));
+ ATF_REQUIRE_STREQ("PREFIXFoo\nPREFIX bar baz", buffer);
+}
+
+ATF_TC_WITHOUT_HEAD(compare_file__empty__match);
+ATF_TC_BODY(compare_file__empty__match, tc)
+{
+ atf_utils_create_file("test.txt", "%s", "");
+ ATF_REQUIRE(atf_utils_compare_file("test.txt", ""));
+}
+
+ATF_TC_WITHOUT_HEAD(compare_file__empty__not_match);
+ATF_TC_BODY(compare_file__empty__not_match, tc)
+{
+ atf_utils_create_file("test.txt", "%s", "");
+ ATF_REQUIRE(!atf_utils_compare_file("test.txt", "\n"));
+ ATF_REQUIRE(!atf_utils_compare_file("test.txt", "foo"));
+ ATF_REQUIRE(!atf_utils_compare_file("test.txt", " "));
+}
+
+ATF_TC_WITHOUT_HEAD(compare_file__short__match);
+ATF_TC_BODY(compare_file__short__match, tc)
+{
+ atf_utils_create_file("test.txt", "this is a short file");
+ ATF_REQUIRE(atf_utils_compare_file("test.txt", "this is a short file"));
+}
+
+ATF_TC_WITHOUT_HEAD(compare_file__short__not_match);
+ATF_TC_BODY(compare_file__short__not_match, tc)
+{
+ atf_utils_create_file("test.txt", "this is a short file");
+ ATF_REQUIRE(!atf_utils_compare_file("test.txt", ""));
+ ATF_REQUIRE(!atf_utils_compare_file("test.txt", "\n"));
+ ATF_REQUIRE(!atf_utils_compare_file("test.txt", "this is a Short file"));
+ ATF_REQUIRE(!atf_utils_compare_file("test.txt", "this is a short fil"));
+ ATF_REQUIRE(!atf_utils_compare_file("test.txt", "this is a short file "));
+}
+
+ATF_TC_WITHOUT_HEAD(compare_file__long__match);
+ATF_TC_BODY(compare_file__long__match, tc)
+{
+ char long_contents[3456];
+ size_t i = 0;
+ for (; i < sizeof(long_contents) - 1; i++)
+ long_contents[i] = '0' + (i % 10);
+ long_contents[i] = '\0';
+ atf_utils_create_file("test.txt", "%s", long_contents);
+
+ ATF_REQUIRE(atf_utils_compare_file("test.txt", long_contents));
+}
+
+ATF_TC_WITHOUT_HEAD(compare_file__long__not_match);
+ATF_TC_BODY(compare_file__long__not_match, tc)
+{
+ char long_contents[3456];
+ size_t i = 0;
+ for (; i < sizeof(long_contents) - 1; i++)
+ long_contents[i] = '0' + (i % 10);
+ long_contents[i] = '\0';
+ atf_utils_create_file("test.txt", "%s", long_contents);
+
+ ATF_REQUIRE(!atf_utils_compare_file("test.txt", ""));
+ ATF_REQUIRE(!atf_utils_compare_file("test.txt", "\n"));
+ ATF_REQUIRE(!atf_utils_compare_file("test.txt", "0123456789"));
+ long_contents[i - 1] = 'Z';
+ ATF_REQUIRE(!atf_utils_compare_file("test.txt", long_contents));
+}
+
+ATF_TC_WITHOUT_HEAD(copy_file__empty);
+ATF_TC_BODY(copy_file__empty, tc)
+{
+ atf_utils_create_file("src.txt", "%s", "");
+ ATF_REQUIRE(chmod("src.txt", 0520) != -1);
+
+ atf_utils_copy_file("src.txt", "dest.txt");
+ ATF_REQUIRE(atf_utils_compare_file("dest.txt", ""));
+ struct stat sb;
+ ATF_REQUIRE(stat("dest.txt", &sb) != -1);
+ ATF_REQUIRE_EQ(0520, sb.st_mode & 0xfff);
+}
+
+ATF_TC_WITHOUT_HEAD(copy_file__some_contents);
+ATF_TC_BODY(copy_file__some_contents, tc)
+{
+ atf_utils_create_file("src.txt", "This is a\ntest file\n");
+ atf_utils_copy_file("src.txt", "dest.txt");
+ ATF_REQUIRE(atf_utils_compare_file("dest.txt", "This is a\ntest file\n"));
+}
+
+ATF_TC_WITHOUT_HEAD(create_file);
+ATF_TC_BODY(create_file, tc)
+{
+ atf_utils_create_file("test.txt", "This is a test with %d", 12345);
+
+ char buffer[128];
+ read_file("test.txt", buffer, sizeof(buffer));
+ ATF_REQUIRE_STREQ("This is a test with 12345", buffer);
+}
+
+ATF_TC_WITHOUT_HEAD(file_exists);
+ATF_TC_BODY(file_exists, tc)
+{
+ atf_utils_create_file("test.txt", "foo");
+
+ ATF_REQUIRE( atf_utils_file_exists("test.txt"));
+ ATF_REQUIRE( atf_utils_file_exists("./test.txt"));
+ ATF_REQUIRE(!atf_utils_file_exists("./test.tx"));
+ ATF_REQUIRE(!atf_utils_file_exists("test.txt2"));
+}
+
+ATF_TC_WITHOUT_HEAD(fork);
+ATF_TC_BODY(fork, tc)
+{
+ fprintf(stdout, "Should not get into child\n");
+ fprintf(stderr, "Should not get into child\n");
+ pid_t pid = atf_utils_fork();
+ if (pid == 0) {
+ fprintf(stdout, "Child stdout\n");
+ fprintf(stderr, "Child stderr\n");
+ exit(EXIT_SUCCESS);
+ }
+
+ int status;
+ ATF_REQUIRE(waitpid(pid, &status, 0) != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+
+ atf_dynstr_t out_name;
+ RE(atf_dynstr_init_fmt(&out_name, "atf_utils_fork_%d_out.txt", (int)pid));
+ atf_dynstr_t err_name;
+ RE(atf_dynstr_init_fmt(&err_name, "atf_utils_fork_%d_err.txt", (int)pid));
+
+ char buffer[1024];
+ read_file(atf_dynstr_cstring(&out_name), buffer, sizeof(buffer));
+ ATF_REQUIRE_STREQ("Child stdout\n", buffer);
+ read_file(atf_dynstr_cstring(&err_name), buffer, sizeof(buffer));
+ ATF_REQUIRE_STREQ("Child stderr\n", buffer);
+
+ atf_dynstr_fini(&err_name);
+ atf_dynstr_fini(&out_name);
+}
+
+ATF_TC_WITHOUT_HEAD(free_charpp__empty);
+ATF_TC_BODY(free_charpp__empty, tc)
+{
+ char **array = malloc(sizeof(char *) * 1);
+ array[0] = NULL;
+
+ atf_utils_free_charpp(array);
+}
+
+ATF_TC_WITHOUT_HEAD(free_charpp__some);
+ATF_TC_BODY(free_charpp__some, tc)
+{
+ char **array = malloc(sizeof(char *) * 4);
+ array[0] = strdup("first");
+ array[1] = strdup("second");
+ array[2] = strdup("third");
+ array[3] = NULL;
+
+ atf_utils_free_charpp(array);
+}
+
+ATF_TC_WITHOUT_HEAD(grep_file);
+ATF_TC_BODY(grep_file, tc)
+{
+ atf_utils_create_file("test.txt", "line1\nthe second line\naaaabbbb\n");
+
+ ATF_CHECK(atf_utils_grep_file("line1", "test.txt"));
+ ATF_CHECK(atf_utils_grep_file("line%d", "test.txt", 1));
+ ATF_CHECK(atf_utils_grep_file("second line", "test.txt"));
+ ATF_CHECK(atf_utils_grep_file("aa.*bb", "test.txt"));
+ ATF_CHECK(!atf_utils_grep_file("foo", "test.txt"));
+ ATF_CHECK(!atf_utils_grep_file("bar", "test.txt"));
+ ATF_CHECK(!atf_utils_grep_file("aaaaa", "test.txt"));
+}
+
+ATF_TC_WITHOUT_HEAD(grep_string);
+ATF_TC_BODY(grep_string, tc)
+{
+ const char *str = "a string - aaaabbbb";
+ ATF_CHECK(atf_utils_grep_string("a string", str));
+ ATF_CHECK(atf_utils_grep_string("^a string", str));
+ ATF_CHECK(atf_utils_grep_string("aaaabbbb$", str));
+ ATF_CHECK(atf_utils_grep_string("a%s*bb", str, "a."));
+ ATF_CHECK(!atf_utils_grep_string("foo", str));
+ ATF_CHECK(!atf_utils_grep_string("bar", str));
+ ATF_CHECK(!atf_utils_grep_string("aaaaa", str));
+}
+
+ATF_TC_WITHOUT_HEAD(readline__none);
+ATF_TC_BODY(readline__none, tc)
+{
+ atf_utils_create_file("empty.txt", "%s", "");
+
+ const int fd = open("empty.txt", O_RDONLY);
+ ATF_REQUIRE(fd != -1);
+ ATF_REQUIRE(atf_utils_readline(fd) == NULL);
+ close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(readline__some);
+ATF_TC_BODY(readline__some, tc)
+{
+ const char *l1 = "First line with % formatting % characters %";
+ const char *l2 = "Second line; much longer than the first one";
+ const char *l3 = "Last line, without terminator";
+
+ atf_utils_create_file("test.txt", "%s\n%s\n%s", l1, l2, l3);
+
+ const int fd = open("test.txt", O_RDONLY);
+ ATF_REQUIRE(fd != -1);
+
+ char *line;
+
+ line = atf_utils_readline(fd);
+ ATF_REQUIRE_STREQ(l1, line);
+ free(line);
+
+ line = atf_utils_readline(fd);
+ ATF_REQUIRE_STREQ(l2, line);
+ free(line);
+
+ line = atf_utils_readline(fd);
+ ATF_REQUIRE_STREQ(l3, line);
+ free(line);
+
+ close(fd);
+}
+
+ATF_TC_WITHOUT_HEAD(redirect__stdout);
+ATF_TC_BODY(redirect__stdout, tc)
+{
+ printf("Buffer this");
+ atf_utils_redirect(STDOUT_FILENO, "captured.txt");
+ printf("The printed message");
+ fflush(stdout);
+
+ char buffer[1024];
+ read_file("captured.txt", buffer, sizeof(buffer));
+ ATF_REQUIRE_STREQ("The printed message", buffer);
+}
+
+ATF_TC_WITHOUT_HEAD(redirect__stderr);
+ATF_TC_BODY(redirect__stderr, tc)
+{
+ fprintf(stderr, "Buffer this");
+ atf_utils_redirect(STDERR_FILENO, "captured.txt");
+ fprintf(stderr, "The printed message");
+ fflush(stderr);
+
+ char buffer[1024];
+ read_file("captured.txt", buffer, sizeof(buffer));
+ ATF_REQUIRE_STREQ("The printed message", buffer);
+}
+
+ATF_TC_WITHOUT_HEAD(redirect__other);
+ATF_TC_BODY(redirect__other, tc)
+{
+ const char *message = "Foo bar\nbaz\n";
+ atf_utils_redirect(15, "captured.txt");
+ ATF_REQUIRE(write(15, message, strlen(message)) != -1);
+ close(15);
+
+ char buffer[1024];
+ read_file("captured.txt", buffer, sizeof(buffer));
+ ATF_REQUIRE_STREQ(message, buffer);
+}
+
+static void
+fork_and_wait(const int exitstatus, const char* expout, const char* experr)
+{
+ const pid_t pid = atf_utils_fork();
+ ATF_REQUIRE(pid != -1);
+ if (pid == 0) {
+ fprintf(stdout, "Some output\n");
+ fprintf(stderr, "Some error\n");
+ exit(123);
+ }
+ atf_utils_wait(pid, exitstatus, expout, experr);
+ exit(EXIT_SUCCESS);
+}
+
+ATF_TC_WITHOUT_HEAD(wait__ok);
+ATF_TC_BODY(wait__ok, tc)
+{
+ const pid_t control = fork();
+ ATF_REQUIRE(control != -1);
+ if (control == 0)
+ fork_and_wait(123, "Some output\n", "Some error\n");
+ else {
+ int status;
+ ATF_REQUIRE(waitpid(control, &status, 0) != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(wait__ok_nested);
+ATF_TC_BODY(wait__ok_nested, tc)
+{
+ const pid_t parent = atf_utils_fork();
+ ATF_REQUIRE(parent != -1);
+ if (parent == 0) {
+ const pid_t child = atf_utils_fork();
+ ATF_REQUIRE(child != -1);
+ if (child == 0) {
+ fflush(stderr);
+ fprintf(stdout, "Child output\n");
+ fflush(stdout);
+ fprintf(stderr, "Child error\n");
+ exit(50);
+ } else {
+ fprintf(stdout, "Parent output\n");
+ fprintf(stderr, "Parent error\n");
+ atf_utils_wait(child, 50, "Child output\n", "Child error\n");
+ exit(40);
+ }
+ } else {
+ atf_utils_wait(parent, 40,
+ "Parent output\n"
+ "subprocess stdout: Child output\n"
+ "subprocess stderr: Child error\n",
+ "Parent error\n");
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(wait__invalid_exitstatus);
+ATF_TC_BODY(wait__invalid_exitstatus, tc)
+{
+ const pid_t control = fork();
+ ATF_REQUIRE(control != -1);
+ if (control == 0)
+ fork_and_wait(120, "Some output\n", "Some error\n");
+ else {
+ int status;
+ ATF_REQUIRE(waitpid(control, &status, 0) != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(EXIT_FAILURE, WEXITSTATUS(status));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(wait__invalid_stdout);
+ATF_TC_BODY(wait__invalid_stdout, tc)
+{
+ const pid_t control = fork();
+ ATF_REQUIRE(control != -1);
+ if (control == 0)
+ fork_and_wait(123, "Some output foo\n", "Some error\n");
+ else {
+ int status;
+ ATF_REQUIRE(waitpid(control, &status, 0) != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(EXIT_FAILURE, WEXITSTATUS(status));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(wait__invalid_stderr);
+ATF_TC_BODY(wait__invalid_stderr, tc)
+{
+ const pid_t control = fork();
+ ATF_REQUIRE(control != -1);
+ if (control == 0)
+ fork_and_wait(123, "Some output\n", "Some error foo\n");
+ else {
+ int status;
+ ATF_REQUIRE(waitpid(control, &status, 0) != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(EXIT_FAILURE, WEXITSTATUS(status));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(wait__save_stdout);
+ATF_TC_BODY(wait__save_stdout, tc)
+{
+ const pid_t control = fork();
+ ATF_REQUIRE(control != -1);
+ if (control == 0)
+ fork_and_wait(123, "save:my-output.txt", "Some error\n");
+ else {
+ int status;
+ ATF_REQUIRE(waitpid(control, &status, 0) != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+
+ ATF_REQUIRE(atf_utils_compare_file("my-output.txt", "Some output\n"));
+ }
+}
+
+ATF_TC_WITHOUT_HEAD(wait__save_stderr);
+ATF_TC_BODY(wait__save_stderr, tc)
+{
+ const pid_t control = fork();
+ ATF_REQUIRE(control != -1);
+ if (control == 0)
+ fork_and_wait(123, "Some output\n", "save:my-output.txt");
+ else {
+ int status;
+ ATF_REQUIRE(waitpid(control, &status, 0) != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+
+ ATF_REQUIRE(atf_utils_compare_file("my-output.txt", "Some error\n"));
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, cat_file__empty);
+ ATF_TP_ADD_TC(tp, cat_file__one_line);
+ ATF_TP_ADD_TC(tp, cat_file__several_lines);
+ ATF_TP_ADD_TC(tp, cat_file__no_newline_eof);
+
+ ATF_TP_ADD_TC(tp, compare_file__empty__match);
+ ATF_TP_ADD_TC(tp, compare_file__empty__not_match);
+ ATF_TP_ADD_TC(tp, compare_file__short__match);
+ ATF_TP_ADD_TC(tp, compare_file__short__not_match);
+ ATF_TP_ADD_TC(tp, compare_file__long__match);
+ ATF_TP_ADD_TC(tp, compare_file__long__not_match);
+
+ ATF_TP_ADD_TC(tp, copy_file__empty);
+ ATF_TP_ADD_TC(tp, copy_file__some_contents);
+
+ ATF_TP_ADD_TC(tp, create_file);
+
+ ATF_TP_ADD_TC(tp, file_exists);
+
+ ATF_TP_ADD_TC(tp, fork);
+
+ ATF_TP_ADD_TC(tp, free_charpp__empty);
+ ATF_TP_ADD_TC(tp, free_charpp__some);
+
+ ATF_TP_ADD_TC(tp, grep_file);
+ ATF_TP_ADD_TC(tp, grep_string);
+
+ ATF_TP_ADD_TC(tp, readline__none);
+ ATF_TP_ADD_TC(tp, readline__some);
+
+ ATF_TP_ADD_TC(tp, redirect__stdout);
+ ATF_TP_ADD_TC(tp, redirect__stderr);
+ ATF_TP_ADD_TC(tp, redirect__other);
+
+ ATF_TP_ADD_TC(tp, wait__ok);
+ ATF_TP_ADD_TC(tp, wait__ok_nested);
+ ATF_TP_ADD_TC(tp, wait__save_stdout);
+ ATF_TP_ADD_TC(tp, wait__save_stderr);
+ ATF_TP_ADD_TC(tp, wait__invalid_exitstatus);
+ ATF_TP_ADD_TC(tp, wait__invalid_stdout);
+ ATF_TP_ADD_TC(tp, wait__invalid_stderr);
+
+ return atf_no_error();
+}