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-upstream.tar.xz
bind9-upstream.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++/Atffile13
-rw-r--r--unit/atf-src/atf-c++/Kyuafile13
-rw-r--r--unit/atf-src/atf-c++/Makefile.am.inc101
-rw-r--r--unit/atf-src/atf-c++/atf-c++.3649
-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_c++_test.cpp44
-rw-r--r--unit/atf-src/atf-c++/build.cpp115
-rw-r--r--unit/atf-src/atf-c++/build.hpp53
-rw-r--r--unit/atf-src/atf-c++/build_test.cpp226
-rw-r--r--unit/atf-src/atf-c++/check.cpp154
-rw-r--r--unit/atf-src/atf-c++/check.hpp131
-rw-r--r--unit/atf-src/atf-c++/check_test.cpp394
-rw-r--r--unit/atf-src/atf-c++/detail/Atffile10
-rw-r--r--unit/atf-src/atf-c++/detail/Kyuafile11
-rw-r--r--unit/atf-src/atf-c++/detail/Makefile.am.inc82
-rw-r--r--unit/atf-src/atf-c++/detail/application.cpp249
-rw-r--r--unit/atf-src/atf-c++/detail/application.hpp107
-rw-r--r--unit/atf-src/atf-c++/detail/application_test.cpp90
-rw-r--r--unit/atf-src/atf-c++/detail/auto_array.hpp175
-rw-r--r--unit/atf-src/atf-c++/detail/auto_array_test.cpp302
-rw-r--r--unit/atf-src/atf-c++/detail/env.cpp75
-rw-r--r--unit/atf-src/atf-c++/detail/env.hpp85
-rw-r--r--unit/atf-src/atf-c++/detail/env_test.cpp101
-rw-r--r--unit/atf-src/atf-c++/detail/exceptions.cpp154
-rw-r--r--unit/atf-src/atf-c++/detail/exceptions.hpp54
-rw-r--r--unit/atf-src/atf-c++/detail/exceptions_test.cpp145
-rw-r--r--unit/atf-src/atf-c++/detail/fs.cpp513
-rw-r--r--unit/atf-src/atf-c++/detail/fs.hpp387
-rw-r--r--unit/atf-src/atf-c++/detail/fs_test.cpp542
-rw-r--r--unit/atf-src/atf-c++/detail/process.cpp346
-rw-r--r--unit/atf-src/atf-c++/detail/process.hpp274
-rw-r--r--unit/atf-src/atf-c++/detail/process_test.cpp354
-rw-r--r--unit/atf-src/atf-c++/detail/sanity.hpp33
-rw-r--r--unit/atf-src/atf-c++/detail/test_helpers.cpp100
-rw-r--r--unit/atf-src/atf-c++/detail/test_helpers.hpp107
-rw-r--r--unit/atf-src/atf-c++/detail/text.cpp156
-rw-r--r--unit/atf-src/atf-c++/detail/text.hpp149
-rw-r--r--unit/atf-src/atf-c++/detail/text_test.cpp386
-rw-r--r--unit/atf-src/atf-c++/detail/version_helper.cpp41
-rw-r--r--unit/atf-src/atf-c++/macros.hpp225
-rw-r--r--unit/atf-src/atf-c++/macros_hpp_test.cpp126
-rw-r--r--unit/atf-src/atf-c++/macros_test.cpp811
-rw-r--r--unit/atf-src/atf-c++/pkg_config_test.sh143
-rw-r--r--unit/atf-src/atf-c++/tests.cpp658
-rw-r--r--unit/atf-src/atf-c++/tests.hpp125
-rw-r--r--unit/atf-src/atf-c++/tests_test.cpp190
-rw-r--r--unit/atf-src/atf-c++/unused_test.cpp48
-rw-r--r--unit/atf-src/atf-c++/utils.cpp100
-rw-r--r--unit/atf-src/atf-c++/utils.hpp64
-rw-r--r--unit/atf-src/atf-c++/utils_test.cpp509
51 files changed, 9975 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..5e1d25f
--- /dev/null
+++ b/unit/atf-src/atf-c++/Atffile
@@ -0,0 +1,13 @@
+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: macros_test
+tp: pkg_config_test
+tp: tests_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..9fd43af
--- /dev/null
+++ b/unit/atf-src/atf-c++/Kyuafile
@@ -0,0 +1,13 @@
+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="macros_test"}
+atf_test_program{name="pkg_config_test"}
+atf_test_program{name="tests_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..58f9945
--- /dev/null
+++ b/unit/atf-src/atf-c++/Makefile.am.inc
@@ -0,0 +1,101 @@
+# 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.
+
+ATF_CXX_LIBS = libatf-c++.la libatf-c.la
+
+lib_LTLIBRARIES += libatf-c++.la
+libatf_c___la_LIBADD = libatf-c.la
+libatf_c___la_SOURCES = atf-c++/build.cpp \
+ atf-c++/build.hpp \
+ atf-c++/check.cpp \
+ atf-c++/check.hpp \
+ atf-c++/macros.hpp \
+ atf-c++/tests.cpp \
+ atf-c++/tests.hpp \
+ atf-c++/utils.cpp \
+ atf-c++/utils.hpp
+libatf_c___la_LDFLAGS = -version-info 2:0:0
+
+include_HEADERS += atf-c++.hpp
+atf_c___HEADERS = atf-c++/build.hpp \
+ atf-c++/check.hpp \
+ atf-c++/macros.hpp \
+ atf-c++/tests.hpp \
+ atf-c++/utils.hpp
+atf_c__dir = $(includedir)/atf-c++
+
+dist_man_MANS += atf-c++/atf-c++.3
+
+atf_aclocal_DATA += atf-c++/atf-c++.m4
+EXTRA_DIST += atf-c++/atf-c++.m4
+
+atf_c__dirpkgconfigdir = $(atf_pkgconfigdir)
+atf_c__dirpkgconfig_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#__CXX__#$(CXX)#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_hpp_test.cpp \
+ atf-c++/unused_test.cpp
+tests_atf_c__dir = $(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.cpp
+atf_c___atf_c___test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS)
+tests_atf_c___PROGRAMS += atf-c++/build_test
+atf_c___build_test_SOURCES = atf-c++/build_test.cpp atf-c/h_build.h
+atf_c___build_test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS)
+tests_atf_c___PROGRAMS += atf-c++/check_test
+atf_c___check_test_SOURCES = atf-c++/check_test.cpp
+atf_c___check_test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS)
+tests_atf_c___PROGRAMS += atf-c++/macros_test
+atf_c___macros_test_SOURCES = atf-c++/macros_test.cpp
+atf_c___macros_test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS)
+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++/tests_test
+atf_c___tests_test_SOURCES = atf-c++/tests_test.cpp
+atf_c___tests_test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS)
+tests_atf_c___PROGRAMS += atf-c++/utils_test
+atf_c___utils_test_SOURCES = atf-c++/utils_test.cpp
+atf_c___utils_test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS)
+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..984ec93
--- /dev/null
+++ b/unit/atf-src/atf-c++/atf-c++.3
@@ -0,0 +1,649 @@
+.\" 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_ADD_TEST_CASE ,
+.Nm ATF_CHECK_ERRNO ,
+.Nm ATF_FAIL ,
+.Nm ATF_INIT_TEST_CASES ,
+.Nm ATF_PASS ,
+.Nm ATF_REQUIRE ,
+.Nm ATF_REQUIRE_EQ ,
+.Nm ATF_REQUIRE_ERRNO ,
+.Nm ATF_REQUIRE_IN ,
+.Nm ATF_REQUIRE_MATCH ,
+.Nm ATF_REQUIRE_NOT_IN ,
+.Nm ATF_REQUIRE_THROW ,
+.Nm ATF_REQUIRE_THROW_RE ,
+.Nm ATF_SKIP ,
+.Nm ATF_TEST_CASE ,
+.Nm ATF_TEST_CASE_BODY ,
+.Nm ATF_TEST_CASE_CLEANUP ,
+.Nm ATF_TEST_CASE_HEAD ,
+.Nm ATF_TEST_CASE_NAME ,
+.Nm ATF_TEST_CASE_USE ,
+.Nm ATF_TEST_CASE_WITH_CLEANUP ,
+.Nm ATF_TEST_CASE_WITHOUT_HEAD ,
+.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::grep_collection ,
+.Nm atf::utils::grep_file ,
+.Nm atf::utils::grep_string ,
+.Nm atf::utils::redirect ,
+.Nm atf::utils::wait
+.Nd C++ API to write ATF-based test programs
+.Sh SYNOPSIS
+.In atf-c++.hpp
+.Fn ATF_ADD_TEST_CASE "tcs" "name"
+.Fn ATF_CHECK_ERRNO "expected_errno" "bool_expression"
+.Fn ATF_FAIL "reason"
+.Fn ATF_INIT_TEST_CASES "tcs"
+.Fn ATF_PASS
+.Fn ATF_REQUIRE "expression"
+.Fn ATF_REQUIRE_EQ "expected_expression" "actual_expression"
+.Fn ATF_REQUIRE_ERRNO "expected_errno" "bool_expression"
+.Fn ATF_REQUIRE_IN "element" "collection"
+.Fn ATF_REQUIRE_MATCH "regexp" "string_expression"
+.Fn ATF_REQUIRE_NOT_IN "element" "collection"
+.Fn ATF_REQUIRE_THROW "expected_exception" "statement"
+.Fn ATF_REQUIRE_THROW_RE "expected_exception" "regexp" "statement"
+.Fn ATF_SKIP "reason"
+.Fn ATF_TEST_CASE "name"
+.Fn ATF_TEST_CASE_BODY "name"
+.Fn ATF_TEST_CASE_CLEANUP "name"
+.Fn ATF_TEST_CASE_HEAD "name"
+.Fn ATF_TEST_CASE_NAME "name"
+.Fn ATF_TEST_CASE_USE "name"
+.Fn ATF_TEST_CASE_WITH_CLEANUP "name"
+.Fn ATF_TEST_CASE_WITHOUT_HEAD "name"
+.Ft void
+.Fo atf::utils::cat_file
+.Fa "const std::string& path"
+.Fa "const std::string& prefix"
+.Fc
+.Ft bool
+.Fo atf::utils::compare_file
+.Fa "const std::string& path"
+.Fa "const std::string& contents"
+.Fc
+.Ft void
+.Fo atf::utils::copy_file
+.Fa "const std::string& source"
+.Fa "const std::string& destination"
+.Fc
+.Ft void
+.Fo atf::utils::create_file
+.Fa "const std::string& path"
+.Fa "const std::string& contents"
+.Fc
+.Ft void
+.Fo atf::utils::file_exists
+.Fa "const std::string& path"
+.Fc
+.Ft pid_t
+.Fo atf::utils::fork
+.Fa "void"
+.Fc
+.Ft bool
+.Fo atf::utils::grep_collection
+.Fa "const std::string& regexp"
+.Fa "const Collection& collection"
+.Fc
+.Ft bool
+.Fo atf::utils::grep_file
+.Fa "const std::string& regexp"
+.Fa "const std::string& path"
+.Fc
+.Ft bool
+.Fo atf::utils::grep_string
+.Fa "const std::string& regexp"
+.Fa "const std::string& path"
+.Fc
+.Ft void
+.Fo atf::utils::redirect
+.Fa "const int fd"
+.Fa "const std::string& path"
+.Fc
+.Ft void
+.Fo atf::utils::wait
+.Fa "const pid_t pid"
+.Fa "const int expected_exit_status"
+.Fa "const std::string& expected_stdout"
+.Fa "const std::string& 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
+extern "C" {
+.Ns ... C-specific includes go here ...
+}
+
+.Ns ... C++-specific includes go here ...
+
+#include <atf-c++.hpp>
+
+ATF_TEST_CASE(tc1);
+ATF_TEST_CASE_HEAD(tc1)
+{
+ ... first test case's header ...
+}
+ATF_TEST_CASE_BODY(tc1)
+{
+ ... first test case's body ...
+}
+
+ATF_TEST_CASE_WITH_CLEANUP(tc2);
+ATF_TEST_CASE_HEAD(tc2)
+{
+ ... second test case's header ...
+}
+ATF_TEST_CASE_BODY(tc2)
+{
+ ... second test case's body ...
+}
+ATF_TEST_CASE_CLEANUP(tc2)
+{
+ ... second test case's cleanup ...
+}
+
+ATF_TEST_CASE(tc3);
+ATF_TEST_CASE_BODY(tc3)
+{
+ ... third test case's body ...
+}
+
+.Ns ... additional test cases ...
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, tc1);
+ ATF_ADD_TEST_CASE(tcs, tc2);
+ ATF_ADD_TEST_CASE(tcs, tc3);
+ ... add additional test cases ...
+}
+.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_TEST_CASE ,
+.Fn ATF_TEST_CASE_WITH_CLEANUP
+or the
+.Fn ATF_TEST_CASE_WITHOUT_HEAD
+macros, which take a single parameter specifiying the test case's
+name.
+.Fn ATF_TEST_CASE ,
+requires to define a head and a body for the test case,
+.Fn ATF_TEST_CASE_WITH_CLEANUP
+requires to define a head, a body and a cleanup for the test case and
+.Fn ATF_TEST_CASE_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 through the
+.Fn ATF_ADD_TEST_CASE
+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_TEST_CASE_HEAD ,
+.Fn ATF_TEST_CASE_BODY
+and
+.Fn ATF_TEST_CASE_CLEANUP
+macros, all of which take the test case's name.
+Following each of these, a block of code is expected, surrounded by the
+opening and closing brackets.
+.Pp
+Additionally, the
+.Fn ATF_TEST_CASE_NAME
+macro can be used to obtain the name of the class corresponding to a
+particular test case, as the name is internally manged by the library to
+prevent clashes with other user identifiers.
+Similarly, the
+.Fn ATF_TEST_CASE_USE
+macro can be executed on a particular test case to mark it as "used" and
+thus prevent compiler warnings regarding unused symbols.
+Note that
+.Em you should never have to use these macros during regular operation.
+.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_INIT_TEST_CASES
+macro, which is passed the name of the list that will hold the test cases.
+This name can be whatever you want as long as it is a valid variable value.
+.Pp
+After the macro, you are supposed to provide the body of a function, which
+should only use the
+.Fn ATF_ADD_TEST_CASE
+macro to register the test cases the test program will execute.
+The first parameter of this macro matches the name you provided in the
+former call.
+.Ss Header definitions
+The test case's header can define the meta-data by using the
+.Fn set_md_var
+method, which takes two parameters: the first one specifies the
+meta-data variable to be set and the second 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 has_config_var
+and the
+.Ft std::string
+.Fn get_config_var
+methods, which can be called in any of the three parts of a test case.
+.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 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 ,
+or at any explicit call to
+.Fn ATF_PASS ,
+.Fn ATF_FAIL
+or
+.Fn ATF_SKIP .
+These three macros 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_PASS
+does not take any parameters.
+.Fn ATF_FAIL
+and
+.Fn ATF_SKIP
+take a single string that describes 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 expect_death "reason"
+Expects the test case to exit prematurely regardless of the nature of the
+exit.
+.It Fn 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 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 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 expect_race "reason"
+Any failure or timeout during the execution of the test case will be
+considered as if a race condition has been triggered and reported as such.
+If no problems arise, the test will continue execution as usual.
+.It Fn 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 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
+automatically call
+.Fn ATF_FAIL
+with an appropriate error message.
+.Pp
+.Fn ATF_REQUIRE
+takes an expression and raises a failure if it evaluates to false.
+.Pp
+.Fn ATF_REQUIRE_EQ
+takes two expressions and raises a failure if the two do not evaluate to
+the same exact value.
+The common style is to put the expected value in the first parameter and the
+actual value in the second parameter.
+.Pp
+.Fn ATF_REQUIRE_IN
+takes an element and a collection and validates that the element is present in
+the collection.
+.Pp
+.Fn ATF_REQUIRE_MATCH
+takes a regular expression and a string and raises a failure if the regular
+expression does not match the string.
+.Pp
+.Fn ATF_REQUIRE_NOT_IN
+takes an element and a collection and validates that the element is not present
+in the collection.
+.Pp
+.Fn ATF_REQUIRE_THROW
+takes the name of an exception and a statement and raises a failure if
+the statement does not throw the specified exception.
+.Fn ATF_REQUIRE_THROW_RE
+takes the name of an exception, a regular expresion and a statement and raises a
+failure if the statement does not throw the specified exception and if the
+message of the exception does not match the regular expression.
+.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 std::string& path"
+.Fa "const std::string& prefix"
+.Fc
+.Bd -ragged -offset indent
+Prints the contents of
+.Fa path
+to the standard output, prefixing every line with the string in
+.Fa prefix .
+.Ed
+.Pp
+.Ft bool
+.Fo atf::utils::compare_file
+.Fa "const std::string& path"
+.Fa "const std::string& contents"
+.Fc
+.Bd -ragged -offset indent
+Returns true if the given
+.Fa path
+matches exactly the expected inlined
+.Fa contents .
+.Ed
+.Pp
+.Ft void
+.Fo atf::utils::copy_file
+.Fa "const std::string& source"
+.Fa "const std::string& 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 std::string& path"
+.Fa "const std::string& contents"
+.Fc
+.Bd -ragged -offset indent
+Creates
+.Fa file
+with the text given in
+.Fa contents .
+.Ed
+.Pp
+.Ft void
+.Fo atf::utils::file_exists
+.Fa "const std::string& path"
+.Fc
+.Bd -ragged -offset indent
+Checks if
+.Fa path
+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 bool
+.Fo atf::utils::grep_collection
+.Fa "const std::string& regexp"
+.Fa "const Collection& collection"
+.Fc
+.Bd -ragged -offset indent
+Searches for the regular expression
+.Fa regexp
+in any of the strings contained in the
+.Fa collection .
+This is a template that accepts any one-dimensional container of strings.
+.Ed
+.Pp
+.Ft bool
+.Fo atf::utils::grep_file
+.Fa "const std::string& regexp"
+.Fa "const std::string& path"
+.Fc
+.Bd -ragged -offset indent
+Searches for the regular expression
+.Fa regexp
+in the file
+.Fa path .
+The variable arguments are used to construct the regular expression.
+.Ed
+.Pp
+.Ft bool
+.Fo atf::utils::grep_string
+.Fa "const std::string& regexp"
+.Fa "const std::string& str"
+.Fc
+.Bd -ragged -offset indent
+Searches for the regular expression
+.Fa regexp
+in the string
+.Fa str .
+.Ed
+.Ft void
+.Fo atf::utils::redirect
+.Fa "const int fd"
+.Fa "const std::string& path"
+.Fc
+.Bd -ragged -offset indent
+Redirects the given file descriptor
+.Fa fd
+to the file
+.Fa path .
+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 std::string& expected_stdout"
+.Fa "const std::string& 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++.hpp>
+
+ATF_TEST_CASE(addition);
+ATF_TEST_CASE_HEAD(addition)
+{
+ set_md_var("descr", "Sample tests for the addition operator");
+}
+ATF_TEST_CASE_BODY(addition)
+{
+ ATF_REQUIRE_EQ(0, 0 + 0);
+ ATF_REQUIRE_EQ(1, 0 + 1);
+ ATF_REQUIRE_EQ(1, 1 + 0);
+
+ ATF_REQUIRE_EQ(2, 1 + 1);
+
+ ATF_REQUIRE_EQ(300, 100 + 200);
+}
+
+ATF_TEST_CASE(open_failure);
+ATF_TEST_CASE_HEAD(open_failure)
+{
+ set_md_var("descr", "Sample tests for the open function");
+}
+ATF_TEST_CASE_BODY(open_failure)
+{
+ ATF_REQUIRE_ERRNO(ENOENT, open("non-existent", O_RDONLY) == -1);
+}
+
+ATF_TEST_CASE(known_bug);
+ATF_TEST_CASE_HEAD(known_bug)
+{
+ set_md_var("descr", "Reproduces a known bug");
+}
+ATF_TEST_CASE_BODY(known_bug)
+{
+ expect_fail("See bug number foo/bar");
+ ATF_REQUIRE_EQ(3, 1 + 1);
+ expect_pass();
+ ATF_REQUIRE_EQ(3, 1 + 2);
+}
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, addition);
+ ATF_ADD_TEST_CASE(tcs, open_failure);
+ ATF_ADD_TEST_CASE(tcs, known_bug);
+}
+.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..e142b56
--- /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_CXX([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_CXX_CFLAGS and ATF_CXX_LIBS with the compiler
+dnl and linker flags need to build against atf-c++.
+AC_DEFUN([ATF_CHECK_CXX], [
+ spec="atf-c++[]m4_default_nblank([ $1], [])"
+ _ATF_CHECK_ARG_WITH(
+ [PKG_CHECK_MODULES([ATF_CXX], [${spec}],
+ [found=yes found_atf_cxx=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..f366bb0
--- /dev/null
+++ b/unit/atf-src/atf-c++/atf-c++.pc.in
@@ -0,0 +1,11 @@
+# ATF pkg-config file
+
+cxx=__CXX__
+includedir=__INCLUDEDIR__
+libdir=__LIBDIR__
+
+Name: atf-c++
+Description: Automated Testing Framework (C++ binding)
+Version: __ATF_VERSION__
+Cflags: -I${includedir}
+Libs: -L${libdir} -latf-c++ -latf-c
diff --git a/unit/atf-src/atf-c++/atf_c++_test.cpp b/unit/atf-src/atf-c++/atf_c++_test.cpp
new file mode 100644
index 0000000..cc70886
--- /dev/null
+++ b/unit/atf-src/atf-c++/atf_c++_test.cpp
@@ -0,0 +1,44 @@
+// 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++.hpp>
+
+#include "atf-c++/detail/test_helpers.hpp"
+
+// ------------------------------------------------------------------------
+// Tests cases for the header file.
+// ------------------------------------------------------------------------
+
+HEADER_TC(include, "atf-c++.hpp");
+
+// ------------------------------------------------------------------------
+// Main.
+// ------------------------------------------------------------------------
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ // Add the test cases for the header file.
+ ATF_ADD_TEST_CASE(tcs, include);
+}
diff --git a/unit/atf-src/atf-c++/build.cpp b/unit/atf-src/atf-c++/build.cpp
new file mode 100644
index 0000000..f2f80f4
--- /dev/null
+++ b/unit/atf-src/atf-c++/build.cpp
@@ -0,0 +1,115 @@
+// 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.hpp"
+
+extern "C" {
+#include "atf-c/build.h"
+#include "atf-c/error.h"
+#include "atf-c/utils.h"
+}
+
+#include "atf-c++/detail/exceptions.hpp"
+#include "atf-c++/detail/process.hpp"
+
+namespace impl = atf::build;
+#define IMPL_NAME "atf::build"
+
+// ------------------------------------------------------------------------
+// Auxiliary functions.
+// ------------------------------------------------------------------------
+
+inline
+atf::process::argv_array
+cargv_to_argv(const atf_list_t* l)
+{
+ std::vector< const char* > aux;
+
+ atf_list_citer_t iter;
+ atf_list_for_each_c(iter, l)
+ aux.push_back(static_cast< const char* >(atf_list_citer_data(iter)));
+
+ return atf::process::argv_array(aux);
+}
+
+inline
+atf::process::argv_array
+cargv_to_argv_and_free(char** l)
+{
+ try {
+ atf::process::argv_array argv((const char* const*)l);
+ atf_utils_free_charpp(l);
+ return argv;
+ } catch (...) {
+ atf_utils_free_charpp(l);
+ throw;
+ }
+}
+
+// ------------------------------------------------------------------------
+// Free functions.
+// ------------------------------------------------------------------------
+
+atf::process::argv_array
+impl::c_o(const std::string& sfile, const std::string& ofile,
+ const atf::process::argv_array& optargs)
+{
+ char** l;
+
+ atf_error_t err = atf_build_c_o(sfile.c_str(), ofile.c_str(),
+ optargs.exec_argv(), &l);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ return cargv_to_argv_and_free(l);
+}
+
+atf::process::argv_array
+impl::cpp(const std::string& sfile, const std::string& ofile,
+ const atf::process::argv_array& optargs)
+{
+ char** l;
+
+ atf_error_t err = atf_build_cpp(sfile.c_str(), ofile.c_str(),
+ optargs.exec_argv(), &l);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ return cargv_to_argv_and_free(l);
+}
+
+atf::process::argv_array
+impl::cxx_o(const std::string& sfile, const std::string& ofile,
+ const atf::process::argv_array& optargs)
+{
+ char** l;
+
+ atf_error_t err = atf_build_cxx_o(sfile.c_str(), ofile.c_str(),
+ optargs.exec_argv(), &l);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ return cargv_to_argv_and_free(l);
+}
diff --git a/unit/atf-src/atf-c++/build.hpp b/unit/atf-src/atf-c++/build.hpp
new file mode 100644
index 0000000..7aa5a97
--- /dev/null
+++ b/unit/atf-src/atf-c++/build.hpp
@@ -0,0 +1,53 @@
+// 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_CXX_BUILD_HPP)
+#define ATF_CXX_BUILD_HPP
+
+#include <string>
+
+namespace atf {
+
+namespace process {
+class argv_array;
+} // namespace process
+
+namespace build {
+
+// ------------------------------------------------------------------------
+// Free functions.
+// ------------------------------------------------------------------------
+
+process::argv_array c_o(const std::string&, const std::string&,
+ const process::argv_array&);
+process::argv_array cpp(const std::string&, const std::string&,
+ const process::argv_array&);
+process::argv_array cxx_o(const std::string&, const std::string&,
+ const process::argv_array&);
+
+} // namespace build
+} // namespace atf
+
+#endif // !defined(ATF_CXX_BUILD_HPP)
diff --git a/unit/atf-src/atf-c++/build_test.cpp b/unit/atf-src/atf-c++/build_test.cpp
new file mode 100644
index 0000000..0a5ab96
--- /dev/null
+++ b/unit/atf-src/atf-c++/build_test.cpp
@@ -0,0 +1,226 @@
+// 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.hpp"
+
+#include <cstring>
+#include <iostream>
+
+#include <atf-c++.hpp>
+
+extern "C" {
+#include "atf-c/h_build.h"
+}
+
+#include "atf-c++/detail/env.hpp"
+#include "atf-c++/detail/process.hpp"
+
+// ------------------------------------------------------------------------
+// Auxiliary functions.
+// ------------------------------------------------------------------------
+
+template< class C >
+void
+print_col(const char* prefix, const C& c)
+{
+ std::cout << prefix << ":";
+ for (typename C::const_iterator iter = c.begin(); iter != c.end();
+ iter++)
+ std::cout << " '" << *iter << "'";
+ std::cout << "\n";
+}
+
+static
+void
+print_array(const char* prefix, const char* const* a)
+{
+ std::cout << prefix << ":";
+ for (; *a != NULL; a++)
+ std::cout << " '" << *a << "'";
+ std::cout << "\n";
+}
+
+static
+void
+verbose_set_env(const char *var, const char *val)
+{
+ std::cout << "Setting " << var << " to '" << val << "'\n";
+ atf::env::set(var, val);
+}
+
+static
+bool
+equal_argvs(const atf::process::argv_array& aa, const char* const* array)
+{
+ bool equal = true;
+
+ atf::process::argv_array::size_type i = 0;
+ while (equal && (i < aa.size() && array[i] != NULL)) {
+ if (std::strcmp(aa[i], array[i]) != 0)
+ equal = false;
+ else
+ i++;
+ }
+
+ if (equal && (i < aa.size() || array[i] != NULL))
+ equal = false;
+
+ return equal;
+}
+
+static
+void
+check_equal_argvs(const atf::process::argv_array& aa, const char* const* array)
+{
+ print_array("Expected arguments", array);
+ print_col("Arguments returned", aa);
+
+ if (!equal_argvs(aa, array))
+ ATF_FAIL("The constructed argv differs from the expected values");
+}
+
+// ------------------------------------------------------------------------
+// Internal test cases.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(equal_argvs);
+ATF_TEST_CASE_HEAD(equal_argvs)
+{
+ set_md_var("descr", "Tests the test case internal equal_argvs function");
+}
+ATF_TEST_CASE_BODY(equal_argvs)
+{
+ {
+ const char* const array[] = { NULL };
+ const char* const argv[] = { NULL };
+
+ ATF_REQUIRE(equal_argvs(atf::process::argv_array(argv), array));
+ }
+
+ {
+ const char* const array[] = { NULL };
+ const char* const argv[] = { "foo", NULL };
+
+ ATF_REQUIRE(!equal_argvs(atf::process::argv_array(argv), array));
+ }
+
+ {
+ const char* const array[] = { "foo", NULL };
+ const char* const argv[] = { NULL };
+
+ ATF_REQUIRE(!equal_argvs(atf::process::argv_array(argv), array));
+ }
+
+ {
+ const char* const array[] = { "foo", NULL };
+ const char* const argv[] = { "foo", NULL };
+
+ ATF_REQUIRE(equal_argvs(atf::process::argv_array(argv), array));
+ }
+}
+
+// ------------------------------------------------------------------------
+// Test cases for the free functions.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(c_o);
+ATF_TEST_CASE_HEAD(c_o)
+{
+ set_md_var("descr", "Tests the c_o function");
+}
+ATF_TEST_CASE_BODY(c_o)
+{
+ for (struct c_o_test* test = c_o_tests; test->expargv[0] != NULL;
+ test++) {
+ std::cout << "> Test: " << test->msg << "\n";
+
+ verbose_set_env("ATF_BUILD_CC", test->cc);
+ verbose_set_env("ATF_BUILD_CFLAGS", test->cflags);
+ verbose_set_env("ATF_BUILD_CPPFLAGS", test->cppflags);
+
+ atf::process::argv_array argv =
+ atf::build::c_o(test->sfile, test->ofile,
+ atf::process::argv_array(test->optargs));
+ check_equal_argvs(argv, test->expargv);
+ }
+}
+
+ATF_TEST_CASE(cpp);
+ATF_TEST_CASE_HEAD(cpp)
+{
+ set_md_var("descr", "Tests the cpp function");
+}
+ATF_TEST_CASE_BODY(cpp)
+{
+ for (struct cpp_test* test = cpp_tests; test->expargv[0] != NULL;
+ test++) {
+ std::cout << "> Test: " << test->msg << "\n";
+
+ verbose_set_env("ATF_BUILD_CPP", test->cpp);
+ verbose_set_env("ATF_BUILD_CPPFLAGS", test->cppflags);
+
+ atf::process::argv_array argv =
+ atf::build::cpp(test->sfile, test->ofile,
+ atf::process::argv_array(test->optargs));
+ check_equal_argvs(argv, test->expargv);
+ }
+}
+
+ATF_TEST_CASE(cxx_o);
+ATF_TEST_CASE_HEAD(cxx_o)
+{
+ set_md_var("descr", "Tests the cxx_o function");
+}
+ATF_TEST_CASE_BODY(cxx_o)
+{
+ for (struct cxx_o_test* test = cxx_o_tests; test->expargv[0] != NULL;
+ test++) {
+ std::cout << "> Test: " << test->msg << "\n";
+
+ verbose_set_env("ATF_BUILD_CXX", test->cxx);
+ verbose_set_env("ATF_BUILD_CXXFLAGS", test->cxxflags);
+ verbose_set_env("ATF_BUILD_CPPFLAGS", test->cppflags);
+
+ atf::process::argv_array argv =
+ atf::build::cxx_o(test->sfile, test->ofile,
+ atf::process::argv_array(test->optargs));
+ check_equal_argvs(argv, test->expargv);
+ }
+}
+
+// ------------------------------------------------------------------------
+// Main.
+// ------------------------------------------------------------------------
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ // Add the internal test cases.
+ ATF_ADD_TEST_CASE(tcs, equal_argvs);
+
+ // Add the test cases for the free functions.
+ ATF_ADD_TEST_CASE(tcs, c_o);
+ ATF_ADD_TEST_CASE(tcs, cpp);
+ ATF_ADD_TEST_CASE(tcs, cxx_o);
+}
diff --git a/unit/atf-src/atf-c++/check.cpp b/unit/atf-src/atf-c++/check.cpp
new file mode 100644
index 0000000..e4d7db4
--- /dev/null
+++ b/unit/atf-src/atf-c++/check.cpp
@@ -0,0 +1,154 @@
+// 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++/check.hpp"
+
+#include <cstring>
+
+extern "C" {
+#include "atf-c/build.h"
+#include "atf-c/error.h"
+}
+
+#include "atf-c++/detail/exceptions.hpp"
+#include "atf-c++/detail/process.hpp"
+#include "atf-c++/detail/sanity.hpp"
+
+namespace impl = atf::check;
+#define IMPL_NAME "atf::check"
+
+// ------------------------------------------------------------------------
+// The "check_result" class.
+// ------------------------------------------------------------------------
+
+impl::check_result::check_result(const atf_check_result_t* result)
+{
+ std::memcpy(&m_result, result, sizeof(m_result));
+}
+
+impl::check_result::~check_result(void)
+{
+ atf_check_result_fini(&m_result);
+}
+
+bool
+impl::check_result::exited(void)
+ const
+{
+ return atf_check_result_exited(&m_result);
+}
+
+int
+impl::check_result::exitcode(void)
+ const
+{
+ PRE(exited());
+ return atf_check_result_exitcode(&m_result);
+}
+
+bool
+impl::check_result::signaled(void)
+ const
+{
+ return atf_check_result_signaled(&m_result);
+}
+
+int
+impl::check_result::termsig(void)
+ const
+{
+ PRE(signaled());
+ return atf_check_result_termsig(&m_result);
+}
+
+const std::string
+impl::check_result::stdout_path(void) const
+{
+ return atf_check_result_stdout(&m_result);
+}
+
+const std::string
+impl::check_result::stderr_path(void) const
+{
+ return atf_check_result_stderr(&m_result);
+}
+
+// ------------------------------------------------------------------------
+// Free functions.
+// ------------------------------------------------------------------------
+
+bool
+impl::build_c_o(const std::string& sfile, const std::string& ofile,
+ const atf::process::argv_array& optargs)
+{
+ bool success;
+
+ atf_error_t err = atf_check_build_c_o(sfile.c_str(), ofile.c_str(),
+ optargs.exec_argv(), &success);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ return success;
+}
+
+bool
+impl::build_cpp(const std::string& sfile, const std::string& ofile,
+ const atf::process::argv_array& optargs)
+{
+ bool success;
+
+ atf_error_t err = atf_check_build_cpp(sfile.c_str(), ofile.c_str(),
+ optargs.exec_argv(), &success);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ return success;
+}
+
+bool
+impl::build_cxx_o(const std::string& sfile, const std::string& ofile,
+ const atf::process::argv_array& optargs)
+{
+ bool success;
+
+ atf_error_t err = atf_check_build_cxx_o(sfile.c_str(), ofile.c_str(),
+ optargs.exec_argv(), &success);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ return success;
+}
+
+std::auto_ptr< impl::check_result >
+impl::exec(const atf::process::argv_array& argva)
+{
+ atf_check_result_t result;
+
+ atf_error_t err = atf_check_exec_array(argva.exec_argv(), &result);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ return std::auto_ptr< impl::check_result >(new impl::check_result(&result));
+}
diff --git a/unit/atf-src/atf-c++/check.hpp b/unit/atf-src/atf-c++/check.hpp
new file mode 100644
index 0000000..0144ded
--- /dev/null
+++ b/unit/atf-src/atf-c++/check.hpp
@@ -0,0 +1,131 @@
+// 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_CXX_CHECK_HPP)
+#define ATF_CXX_CHECK_HPP
+
+extern "C" {
+#include <atf-c/check.h>
+}
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace atf {
+
+namespace process {
+class argv_array;
+} // namespace process
+
+namespace check {
+
+// ------------------------------------------------------------------------
+// The "check_result" class.
+// ------------------------------------------------------------------------
+
+//!
+//! \brief A class that contains results of executed command.
+//!
+//! The check_result class holds information about results
+//! of executing arbitrary command and manages files containing
+//! its output.
+//!
+class check_result {
+ // Non-copyable.
+ check_result(const check_result&);
+ check_result& operator=(const check_result&);
+
+ //!
+ //! \brief Internal representation of a result.
+ //!
+ atf_check_result_t m_result;
+
+ //!
+ //! \brief Constructs a results object and grabs ownership of the
+ //! parameter passed in.
+ //!
+ check_result(const atf_check_result_t* result);
+
+ friend check_result test_constructor(const char* const*);
+ friend std::auto_ptr< check_result > exec(const atf::process::argv_array&);
+
+public:
+ //!
+ //! \brief Destroys object and removes all managed files.
+ //!
+ ~check_result(void);
+
+ //!
+ //! \brief Returns whether the command exited correctly or not.
+ //!
+ bool exited(void) const;
+
+ //!
+ //! \brief Returns command's exit status.
+ //!
+ int exitcode(void) const;
+
+ //!
+ //! \brief Returns whether the command received a signal or not.
+ //!
+ bool signaled(void) const;
+
+ //!
+ //! \brief Returns the signal that terminated the command.
+ //!
+ int termsig(void) const;
+
+ //!
+ //! \brief Returns the path to file contaning command's stdout.
+ //!
+ const std::string stdout_path(void) const;
+
+ //!
+ //! \brief Returns the path to file contaning command's stderr.
+ //!
+ const std::string stderr_path(void) const;
+};
+
+// ------------------------------------------------------------------------
+// Free functions.
+// ------------------------------------------------------------------------
+
+bool build_c_o(const std::string&, const std::string&,
+ const atf::process::argv_array&);
+bool build_cpp(const std::string&, const std::string&,
+ const atf::process::argv_array&);
+bool build_cxx_o(const std::string&, const std::string&,
+ const atf::process::argv_array&);
+std::auto_ptr< check_result > exec(const atf::process::argv_array&);
+
+// Useful for testing only.
+check_result test_constructor(void);
+
+} // namespace check
+} // namespace atf
+
+#endif // !defined(ATF_CXX_CHECK_HPP)
diff --git a/unit/atf-src/atf-c++/check_test.cpp b/unit/atf-src/atf-c++/check_test.cpp
new file mode 100644
index 0000000..7baf3fa
--- /dev/null
+++ b/unit/atf-src/atf-c++/check_test.cpp
@@ -0,0 +1,394 @@
+// 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++/check.hpp"
+
+extern "C" {
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+}
+
+#include <cstdlib>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <list>
+#include <memory>
+#include <vector>
+
+#include <atf-c++.hpp>
+
+#include "atf-c++/detail/fs.hpp"
+#include "atf-c++/detail/process.hpp"
+#include "atf-c++/detail/test_helpers.hpp"
+#include "atf-c++/detail/text.hpp"
+#include "atf-c++/utils.hpp"
+
+// ------------------------------------------------------------------------
+// Auxiliary functions.
+// ------------------------------------------------------------------------
+
+static
+std::auto_ptr< atf::check::check_result >
+do_exec(const atf::tests::tc* tc, const char* helper_name)
+{
+ std::vector< std::string > argv;
+ argv.push_back(get_process_helpers_path(*tc, false).str());
+ argv.push_back(helper_name);
+ std::cout << "Executing " << argv[0] << " " << argv[1] << "\n";
+
+ atf::process::argv_array argva(argv);
+ return atf::check::exec(argva);
+}
+
+static
+std::auto_ptr< atf::check::check_result >
+do_exec(const atf::tests::tc* tc, const char* helper_name, const char *carg2)
+{
+ std::vector< std::string > argv;
+ argv.push_back(get_process_helpers_path(*tc, false).str());
+ argv.push_back(helper_name);
+ argv.push_back(carg2);
+ std::cout << "Executing " << argv[0] << " " << argv[1] << " "
+ << argv[2] << "\n";
+
+ atf::process::argv_array argva(argv);
+ return atf::check::exec(argva);
+}
+
+// ------------------------------------------------------------------------
+// Helper test cases for the free functions.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(h_build_c_o_ok);
+ATF_TEST_CASE_HEAD(h_build_c_o_ok)
+{
+ set_md_var("descr", "Helper test case for build_c_o");
+}
+ATF_TEST_CASE_BODY(h_build_c_o_ok)
+{
+ std::ofstream sfile("test.c");
+ sfile << "#include <stdio.h>\n";
+ sfile.close();
+
+ ATF_REQUIRE(atf::check::build_c_o("test.c", "test.o",
+ atf::process::argv_array()));
+}
+
+ATF_TEST_CASE(h_build_c_o_fail);
+ATF_TEST_CASE_HEAD(h_build_c_o_fail)
+{
+ set_md_var("descr", "Helper test case for build_c_o");
+}
+ATF_TEST_CASE_BODY(h_build_c_o_fail)
+{
+ std::ofstream sfile("test.c");
+ sfile << "void foo(void) { int a = UNDEFINED_SYMBOL; }\n";
+ sfile.close();
+
+ ATF_REQUIRE(!atf::check::build_c_o("test.c", "test.o",
+ atf::process::argv_array()));
+}
+
+ATF_TEST_CASE(h_build_cpp_ok);
+ATF_TEST_CASE_HEAD(h_build_cpp_ok)
+{
+ set_md_var("descr", "Helper test case for build_cpp");
+}
+ATF_TEST_CASE_BODY(h_build_cpp_ok)
+{
+ std::ofstream sfile("test.c");
+ sfile << "#define A foo\n";
+ sfile << "#define B bar\n";
+ sfile << "A B\n";
+ sfile.close();
+
+ ATF_REQUIRE(atf::check::build_cpp("test.c", "test.p",
+ atf::process::argv_array()));
+}
+
+ATF_TEST_CASE(h_build_cpp_fail);
+ATF_TEST_CASE_HEAD(h_build_cpp_fail)
+{
+ set_md_var("descr", "Helper test case for build_cpp");
+}
+ATF_TEST_CASE_BODY(h_build_cpp_fail)
+{
+ std::ofstream sfile("test.c");
+ sfile << "#include \"./non-existent.h\"\n";
+ sfile.close();
+
+ ATF_REQUIRE(!atf::check::build_cpp("test.c", "test.p",
+ atf::process::argv_array()));
+}
+
+ATF_TEST_CASE(h_build_cxx_o_ok);
+ATF_TEST_CASE_HEAD(h_build_cxx_o_ok)
+{
+ set_md_var("descr", "Helper test case for build_cxx_o");
+}
+ATF_TEST_CASE_BODY(h_build_cxx_o_ok)
+{
+ std::ofstream sfile("test.cpp");
+ sfile << "#include <iostream>\n";
+ sfile.close();
+
+ ATF_REQUIRE(atf::check::build_cxx_o("test.cpp", "test.o",
+ atf::process::argv_array()));
+}
+
+ATF_TEST_CASE(h_build_cxx_o_fail);
+ATF_TEST_CASE_HEAD(h_build_cxx_o_fail)
+{
+ set_md_var("descr", "Helper test case for build_cxx_o");
+}
+ATF_TEST_CASE_BODY(h_build_cxx_o_fail)
+{
+ std::ofstream sfile("test.cpp");
+ sfile << "void foo(void) { int a = UNDEFINED_SYMBOL; }\n";
+ sfile.close();
+
+ ATF_REQUIRE(!atf::check::build_cxx_o("test.cpp", "test.o",
+ atf::process::argv_array()));
+}
+
+// ------------------------------------------------------------------------
+// Test cases for the free functions.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(build_c_o);
+ATF_TEST_CASE_HEAD(build_c_o)
+{
+ set_md_var("descr", "Tests the build_c_o function");
+}
+ATF_TEST_CASE_BODY(build_c_o)
+{
+ ATF_TEST_CASE_USE(h_build_c_o_ok);
+ run_h_tc< ATF_TEST_CASE_NAME(h_build_c_o_ok) >();
+ ATF_REQUIRE(atf::utils::grep_file("-o test.o", "stdout"));
+ ATF_REQUIRE(atf::utils::grep_file("-c test.c", "stdout"));
+
+ ATF_TEST_CASE_USE(h_build_c_o_fail);
+ run_h_tc< ATF_TEST_CASE_NAME(h_build_c_o_fail) >();
+ ATF_REQUIRE(atf::utils::grep_file("-o test.o", "stdout"));
+ ATF_REQUIRE(atf::utils::grep_file("-c test.c", "stdout"));
+ ATF_REQUIRE(atf::utils::grep_file("test.c", "stderr"));
+ ATF_REQUIRE(atf::utils::grep_file("UNDEFINED_SYMBOL", "stderr"));
+}
+
+ATF_TEST_CASE(build_cpp);
+ATF_TEST_CASE_HEAD(build_cpp)
+{
+ set_md_var("descr", "Tests the build_cpp function");
+}
+ATF_TEST_CASE_BODY(build_cpp)
+{
+ ATF_TEST_CASE_USE(h_build_cpp_ok);
+ run_h_tc< ATF_TEST_CASE_NAME(h_build_cpp_ok) >();
+ ATF_REQUIRE(atf::utils::grep_file("-o.*test.p", "stdout"));
+ ATF_REQUIRE(atf::utils::grep_file("test.c", "stdout"));
+ ATF_REQUIRE(atf::utils::grep_file("foo bar", "test.p"));
+
+ ATF_TEST_CASE_USE(h_build_cpp_fail);
+ run_h_tc< ATF_TEST_CASE_NAME(h_build_cpp_fail) >();
+ ATF_REQUIRE(atf::utils::grep_file("-o test.p", "stdout"));
+ ATF_REQUIRE(atf::utils::grep_file("test.c", "stdout"));
+ ATF_REQUIRE(atf::utils::grep_file("test.c", "stderr"));
+ ATF_REQUIRE(atf::utils::grep_file("non-existent.h", "stderr"));
+}
+
+ATF_TEST_CASE(build_cxx_o);
+ATF_TEST_CASE_HEAD(build_cxx_o)
+{
+ set_md_var("descr", "Tests the build_cxx_o function");
+}
+ATF_TEST_CASE_BODY(build_cxx_o)
+{
+ ATF_TEST_CASE_USE(h_build_cxx_o_ok);
+ run_h_tc< ATF_TEST_CASE_NAME(h_build_cxx_o_ok) >();
+ ATF_REQUIRE(atf::utils::grep_file("-o test.o", "stdout"));
+ ATF_REQUIRE(atf::utils::grep_file("-c test.cpp", "stdout"));
+
+ ATF_TEST_CASE_USE(h_build_cxx_o_fail);
+ run_h_tc< ATF_TEST_CASE_NAME(h_build_cxx_o_fail) >();
+ ATF_REQUIRE(atf::utils::grep_file("-o test.o", "stdout"));
+ ATF_REQUIRE(atf::utils::grep_file("-c test.cpp", "stdout"));
+ ATF_REQUIRE(atf::utils::grep_file("test.cpp", "stderr"));
+ ATF_REQUIRE(atf::utils::grep_file("UNDEFINED_SYMBOL", "stderr"));
+}
+
+ATF_TEST_CASE(exec_cleanup);
+ATF_TEST_CASE_HEAD(exec_cleanup)
+{
+ set_md_var("descr", "Tests that exec properly cleans up the temporary "
+ "files it creates");
+}
+ATF_TEST_CASE_BODY(exec_cleanup)
+{
+ std::auto_ptr< atf::fs::path > out;
+ std::auto_ptr< atf::fs::path > err;
+
+ {
+ std::auto_ptr< atf::check::check_result > r =
+ do_exec(this, "exit-success");
+ out.reset(new atf::fs::path(r->stdout_path()));
+ err.reset(new atf::fs::path(r->stderr_path()));
+ ATF_REQUIRE(atf::fs::exists(*out.get()));
+ ATF_REQUIRE(atf::fs::exists(*err.get()));
+ }
+ ATF_REQUIRE(!atf::fs::exists(*out.get()));
+ ATF_REQUIRE(!atf::fs::exists(*err.get()));
+}
+
+ATF_TEST_CASE(exec_exitstatus);
+ATF_TEST_CASE_HEAD(exec_exitstatus)
+{
+ set_md_var("descr", "Tests that exec properly captures the exit "
+ "status of the executed command");
+}
+ATF_TEST_CASE_BODY(exec_exitstatus)
+{
+ {
+ std::auto_ptr< atf::check::check_result > r =
+ do_exec(this, "exit-success");
+ ATF_REQUIRE(r->exited());
+ ATF_REQUIRE(!r->signaled());
+ ATF_REQUIRE_EQ(r->exitcode(), EXIT_SUCCESS);
+ }
+
+ {
+ std::auto_ptr< atf::check::check_result > r =
+ do_exec(this, "exit-failure");
+ ATF_REQUIRE(r->exited());
+ ATF_REQUIRE(!r->signaled());
+ ATF_REQUIRE_EQ(r->exitcode(), EXIT_FAILURE);
+ }
+
+ {
+ std::auto_ptr< atf::check::check_result > r =
+ do_exec(this, "exit-signal");
+ ATF_REQUIRE(!r->exited());
+ ATF_REQUIRE(r->signaled());
+ ATF_REQUIRE_EQ(r->termsig(), SIGKILL);
+ }
+}
+
+static
+void
+check_lines(const std::string& path, const char* outname,
+ const char* resname)
+{
+ std::ifstream f(path.c_str());
+ ATF_REQUIRE(f);
+
+ std::string line;
+ std::getline(f, line);
+ ATF_REQUIRE_EQ(line, std::string("Line 1 to ") + outname + " for " +
+ resname);
+ std::getline(f, line);
+ ATF_REQUIRE_EQ(line, std::string("Line 2 to ") + outname + " for " +
+ resname);
+}
+
+ATF_TEST_CASE(exec_stdout_stderr);
+ATF_TEST_CASE_HEAD(exec_stdout_stderr)
+{
+ set_md_var("descr", "Tests that exec properly captures the stdout "
+ "and stderr streams of the child process");
+}
+ATF_TEST_CASE_BODY(exec_stdout_stderr)
+{
+ std::auto_ptr< atf::check::check_result > r1 =
+ do_exec(this, "stdout-stderr", "result1");
+ ATF_REQUIRE(r1->exited());
+ ATF_REQUIRE_EQ(r1->exitcode(), EXIT_SUCCESS);
+
+ std::auto_ptr< atf::check::check_result > r2 =
+ do_exec(this, "stdout-stderr", "result2");
+ ATF_REQUIRE(r2->exited());
+ ATF_REQUIRE_EQ(r2->exitcode(), EXIT_SUCCESS);
+
+ const std::string out1 = r1->stdout_path();
+ const std::string out2 = r2->stdout_path();
+ const std::string err1 = r1->stderr_path();
+ const std::string err2 = r2->stderr_path();
+
+ ATF_REQUIRE(out1.find("check.XXXXXX") == std::string::npos);
+ ATF_REQUIRE(out2.find("check.XXXXXX") == std::string::npos);
+ ATF_REQUIRE(err1.find("check.XXXXXX") == std::string::npos);
+ ATF_REQUIRE(err2.find("check.XXXXXX") == std::string::npos);
+
+ ATF_REQUIRE(out1.find("/check") != std::string::npos);
+ ATF_REQUIRE(out2.find("/check") != std::string::npos);
+ ATF_REQUIRE(err1.find("/check") != std::string::npos);
+ ATF_REQUIRE(err2.find("/check") != std::string::npos);
+
+ ATF_REQUIRE(out1.find("/stdout") != std::string::npos);
+ ATF_REQUIRE(out2.find("/stdout") != std::string::npos);
+ ATF_REQUIRE(err1.find("/stderr") != std::string::npos);
+ ATF_REQUIRE(err2.find("/stderr") != std::string::npos);
+
+ ATF_REQUIRE(out1 != out2);
+ ATF_REQUIRE(err1 != err2);
+
+ check_lines(out1, "stdout", "result1");
+ check_lines(out2, "stdout", "result2");
+ check_lines(err1, "stderr", "result1");
+ check_lines(err2, "stderr", "result2");
+}
+
+ATF_TEST_CASE(exec_unknown);
+ATF_TEST_CASE_HEAD(exec_unknown)
+{
+ set_md_var("descr", "Tests that running a non-existing binary "
+ "is handled correctly");
+}
+ATF_TEST_CASE_BODY(exec_unknown)
+{
+ std::vector< std::string > argv;
+ argv.push_back("/foo/bar/non-existent");
+
+ atf::process::argv_array argva(argv);
+ std::auto_ptr< atf::check::check_result > r = atf::check::exec(argva);
+ ATF_REQUIRE(r->exited());
+ ATF_REQUIRE_EQ(r->exitcode(), 127);
+}
+
+// ------------------------------------------------------------------------
+// Main.
+// ------------------------------------------------------------------------
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ // Add the test cases for the free functions.
+ ATF_ADD_TEST_CASE(tcs, build_c_o);
+ ATF_ADD_TEST_CASE(tcs, build_cpp);
+ ATF_ADD_TEST_CASE(tcs, build_cxx_o);
+ ATF_ADD_TEST_CASE(tcs, exec_cleanup);
+ ATF_ADD_TEST_CASE(tcs, exec_exitstatus);
+ ATF_ADD_TEST_CASE(tcs, exec_stdout_stderr);
+ ATF_ADD_TEST_CASE(tcs, exec_unknown);
+}
diff --git a/unit/atf-src/atf-c++/detail/Atffile b/unit/atf-src/atf-c++/detail/Atffile
new file mode 100644
index 0000000..5cd6c57
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/Atffile
@@ -0,0 +1,10 @@
+Content-Type: application/X-atf-atffile; version="1"
+
+prop: test-suite = atf
+
+tp: application_test
+tp: auto_array_test
+tp: env_test
+tp: exceptions_test
+tp: fs_test
+tp: text_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..fc799e6
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/Kyuafile
@@ -0,0 +1,11 @@
+syntax("kyuafile", 1)
+
+test_suite("atf")
+
+atf_test_program{name="application_test"}
+atf_test_program{name="auto_array_test"}
+atf_test_program{name="env_test"}
+atf_test_program{name="exceptions_test"}
+atf_test_program{name="fs_test"}
+atf_test_program{name="process_test"}
+atf_test_program{name="text_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..5aace70
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/Makefile.am.inc
@@ -0,0 +1,82 @@
+# 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/application.cpp \
+ atf-c++/detail/application.hpp \
+ atf-c++/detail/auto_array.hpp \
+ atf-c++/detail/env.cpp \
+ atf-c++/detail/env.hpp \
+ atf-c++/detail/exceptions.cpp \
+ atf-c++/detail/exceptions.hpp \
+ atf-c++/detail/fs.cpp \
+ atf-c++/detail/fs.hpp \
+ atf-c++/detail/process.cpp \
+ atf-c++/detail/process.hpp \
+ atf-c++/detail/sanity.hpp \
+ atf-c++/detail/text.cpp \
+ atf-c++/detail/text.hpp
+
+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.cpp \
+ atf-c++/detail/test_helpers.hpp
+atf_c___detail_libtest_helpers_la_CPPFLAGS = -DATF_INCLUDEDIR=\"$(includedir)\"
+
+tests_atf_c___detail_PROGRAMS = atf-c++/detail/application_test
+atf_c___detail_application_test_SOURCES = atf-c++/detail/application_test.cpp
+atf_c___detail_application_test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS)
+
+tests_atf_c___detail_PROGRAMS += atf-c++/detail/auto_array_test
+atf_c___detail_auto_array_test_SOURCES = atf-c++/detail/auto_array_test.cpp
+atf_c___detail_auto_array_test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS)
+
+tests_atf_c___detail_PROGRAMS += atf-c++/detail/env_test
+atf_c___detail_env_test_SOURCES = atf-c++/detail/env_test.cpp
+atf_c___detail_env_test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS)
+
+tests_atf_c___detail_PROGRAMS += atf-c++/detail/exceptions_test
+atf_c___detail_exceptions_test_SOURCES = atf-c++/detail/exceptions_test.cpp
+atf_c___detail_exceptions_test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS)
+
+tests_atf_c___detail_PROGRAMS += atf-c++/detail/fs_test
+atf_c___detail_fs_test_SOURCES = atf-c++/detail/fs_test.cpp
+atf_c___detail_fs_test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS)
+
+tests_atf_c___detail_PROGRAMS += atf-c++/detail/process_test
+atf_c___detail_process_test_SOURCES = atf-c++/detail/process_test.cpp
+atf_c___detail_process_test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS)
+
+tests_atf_c___detail_PROGRAMS += atf-c++/detail/text_test
+atf_c___detail_text_test_SOURCES = atf-c++/detail/text_test.cpp
+atf_c___detail_text_test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS)
+
+tests_atf_c___detail_PROGRAMS += atf-c++/detail/version_helper
+atf_c___detail_version_helper_SOURCES = atf-c++/detail/version_helper.cpp
+
+# vim: syntax=make:noexpandtab:shiftwidth=8:softtabstop=8
diff --git a/unit/atf-src/atf-c++/detail/application.cpp b/unit/atf-src/atf-c++/detail/application.cpp
new file mode 100644
index 0000000..37086eb
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/application.cpp
@@ -0,0 +1,249 @@
+// 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/application.hpp"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+extern "C" {
+#include <unistd.h>
+}
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+
+extern "C" {
+#include "atf-c/defs.h"
+}
+
+#include "atf-c++/detail/sanity.hpp"
+
+#if !defined(HAVE_VSNPRINTF_IN_STD)
+namespace std {
+using ::vsnprintf;
+}
+#endif // !defined(HAVE_VSNPRINTF_IN_STD)
+
+namespace impl = atf::application;
+#define IMPL_NAME "atf::application"
+
+// ------------------------------------------------------------------------
+// The "usage_error" class.
+// ------------------------------------------------------------------------
+
+impl::usage_error::usage_error(const char *fmt, ...)
+ throw() :
+ std::runtime_error("usage_error; message unformatted")
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ std::vsnprintf(m_text, sizeof(m_text), fmt, ap);
+ va_end(ap);
+}
+
+impl::usage_error::~usage_error(void)
+ throw()
+{
+}
+
+const char*
+impl::usage_error::what(void)
+ const throw()
+{
+ return m_text;
+}
+
+// ------------------------------------------------------------------------
+// The "application" class.
+// ------------------------------------------------------------------------
+
+impl::option::option(char ch,
+ const std::string& a,
+ const std::string& desc) :
+ m_character(ch),
+ m_argument(a),
+ m_description(desc)
+{
+}
+
+bool
+impl::option::operator<(const impl::option& o)
+ const
+{
+ return m_character < o.m_character;
+}
+
+impl::app::app(const std::string& description,
+ const std::string& manpage) :
+ m_argc(-1),
+ m_argv(NULL),
+ m_prog_name(NULL),
+ m_description(description),
+ m_manpage(manpage)
+{
+}
+
+impl::app::~app(void)
+{
+}
+
+bool
+impl::app::inited(void)
+{
+ return m_argc != -1;
+}
+
+impl::app::options_set
+impl::app::options(void)
+{
+ return specific_options();
+}
+
+std::string
+impl::app::specific_args(void)
+ const
+{
+ return "";
+}
+
+impl::app::options_set
+impl::app::specific_options(void)
+ const
+{
+ return options_set();
+}
+
+void
+impl::app::process_option(int ch ATF_DEFS_ATTRIBUTE_UNUSED,
+ const char* arg ATF_DEFS_ATTRIBUTE_UNUSED)
+{
+}
+
+void
+impl::app::process_options(void)
+{
+ PRE(inited());
+
+ std::string optstr;
+#if defined(HAVE_GNU_GETOPT)
+ optstr += '+'; // Turn on POSIX behavior.
+#endif
+ optstr += ':';
+ {
+ options_set opts = options();
+ for (options_set::const_iterator iter = opts.begin();
+ iter != opts.end(); iter++) {
+ const option& opt = (*iter);
+
+ optstr += opt.m_character;
+ if (!opt.m_argument.empty())
+ optstr += ':';
+ }
+ }
+
+ int ch;
+ const int old_opterr = ::opterr;
+ ::opterr = 0;
+ while ((ch = ::getopt(m_argc, m_argv, optstr.c_str())) != -1) {
+ switch (ch) {
+ case ':':
+ throw usage_error("Option -%c requires an argument.",
+ ::optopt);
+
+ case '?':
+ throw usage_error("Unknown option -%c.", ::optopt);
+
+ default:
+ process_option(ch, ::optarg);
+ }
+ }
+ m_argc -= ::optind;
+ m_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
+}
+
+int
+impl::app::run(int argc, char* const* argv)
+{
+ PRE(argc > 0);
+ PRE(argv != NULL);
+
+ m_argc = argc;
+ m_argv = argv;
+
+ m_argv0 = m_argv[0];
+
+ m_prog_name = std::strrchr(m_argv[0], '/');
+ if (m_prog_name == NULL)
+ m_prog_name = m_argv[0];
+ else
+ m_prog_name++;
+
+ // 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 (std::strncmp(m_prog_name, "lt-", 3) == 0)
+ m_prog_name += 3;
+
+ const std::string bug =
+ std::string("This is probably a bug in ") + m_prog_name +
+ " or one of the libraries it uses. Please report this problem to "
+ PACKAGE_BUGREPORT " and provide as many details as possible "
+ "describing how you got to this condition.";
+
+ int errcode;
+ try {
+ process_options();
+ errcode = main();
+ } catch (const usage_error& e) {
+ std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n";
+ std::cerr << m_prog_name << ": See " << m_manpage << " for usage "
+ "details.\n";
+ errcode = EXIT_FAILURE;
+ } catch (const std::runtime_error& e) {
+ std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n";
+ errcode = EXIT_FAILURE;
+ } catch (const std::exception& e) {
+ std::cerr << m_prog_name << ": ERROR: Caught unexpected error: "
+ << e.what() << "\n";
+ errcode = EXIT_FAILURE;
+ } catch (...) {
+ std::cerr << m_prog_name << ": ERROR: Caught unknown error\n";
+ errcode = EXIT_FAILURE;
+ }
+ return errcode;
+}
diff --git a/unit/atf-src/atf-c++/detail/application.hpp b/unit/atf-src/atf-c++/detail/application.hpp
new file mode 100644
index 0000000..cdb4073
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/application.hpp
@@ -0,0 +1,107 @@
+// 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_CXX_DETAIL_APPLICATION_HPP)
+#define ATF_CXX_DETAIL_APPLICATION_HPP
+
+#include <ostream>
+#include <set>
+#include <stdexcept>
+#include <string>
+
+namespace atf {
+namespace application {
+
+// ------------------------------------------------------------------------
+// The "usage_error" class.
+// ------------------------------------------------------------------------
+
+class usage_error : public std::runtime_error {
+ char m_text[4096];
+
+public:
+ usage_error(const char*, ...) throw();
+ ~usage_error(void) throw();
+
+ const char* what(void) const throw();
+};
+
+// ------------------------------------------------------------------------
+// The "option" class.
+// ------------------------------------------------------------------------
+
+class option {
+ char m_character;
+ std::string m_argument;
+ std::string m_description;
+
+ friend class app;
+
+public:
+ option(char, const std::string&, const std::string&);
+
+ bool operator<(const option&) const;
+};
+
+// ------------------------------------------------------------------------
+// The "app" class.
+// ------------------------------------------------------------------------
+
+class app {
+ void process_options(void);
+ void usage(std::ostream&);
+
+ bool inited(void);
+
+protected:
+ typedef std::set< option > options_set;
+
+ int m_argc;
+ char* const* m_argv;
+
+ const char* m_argv0;
+ const char* m_prog_name;
+ std::string m_description;
+ std::string m_manpage;
+
+ options_set options(void);
+
+ // To be redefined.
+ virtual std::string specific_args(void) const;
+ virtual options_set specific_options(void) const;
+ virtual void process_option(int, const char*);
+ virtual int main(void) = 0;
+
+public:
+ app(const std::string&, const std::string&);
+ virtual ~app(void);
+
+ int run(int, char* const*);
+};
+
+} // namespace application
+} // namespace atf
+
+#endif // !defined(ATF_CXX_DETAIL_APPLICATION_HPP)
diff --git a/unit/atf-src/atf-c++/detail/application_test.cpp b/unit/atf-src/atf-c++/detail/application_test.cpp
new file mode 100644
index 0000000..28b7a2f
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/application_test.cpp
@@ -0,0 +1,90 @@
+// 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++/detail/application.hpp"
+
+extern "C" {
+#include <unistd.h>
+}
+
+#include <atf-c++.hpp>
+
+class getopt_app : public atf::application::app {
+public:
+ getopt_app(void) : app("description", "manpage") {}
+
+ int main(void)
+ {
+ // Provide an option that is unknown to the application 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 an application, 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_FAIL("Unexpected unknown option found");
+ }
+ }
+
+ ATF_REQUIRE(zflag);
+ ATF_REQUIRE_EQ(1, argc - optind);
+ ATF_REQUIRE_EQ(std::string("foo"), argv[optind]);
+
+ return 0;
+ }
+};
+
+ATF_TEST_CASE_WITHOUT_HEAD(getopt);
+ATF_TEST_CASE_BODY(getopt)
+{
+ int argc = 1;
+ char arg1[] = "progname";
+ char *const argv[] = { arg1, NULL };
+ ATF_REQUIRE_EQ(0, getopt_app().run(argc, argv));
+}
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, getopt);
+}
diff --git a/unit/atf-src/atf-c++/detail/auto_array.hpp b/unit/atf-src/atf-c++/detail/auto_array.hpp
new file mode 100644
index 0000000..b434a58
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/auto_array.hpp
@@ -0,0 +1,175 @@
+// 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_CXX_DETAIL_AUTO_ARRAY_HPP)
+#define ATF_CXX_DETAIL_AUTO_ARRAY_HPP
+
+#include <cstddef>
+
+namespace atf {
+
+// ------------------------------------------------------------------------
+// The "auto_array" class.
+// ------------------------------------------------------------------------
+
+template< class T >
+struct auto_array_ref {
+ T* m_ptr;
+
+ explicit auto_array_ref(T*);
+};
+
+template< class T >
+auto_array_ref< T >::auto_array_ref(T* ptr) :
+ m_ptr(ptr)
+{
+}
+
+template< class T >
+class auto_array {
+ T* m_ptr;
+
+public:
+ auto_array(T* = NULL) throw();
+ auto_array(auto_array< T >&) throw();
+ auto_array(auto_array_ref< T >) throw();
+ ~auto_array(void) throw();
+
+ T* get(void) throw();
+ const T* get(void) const throw();
+ T* release(void) throw();
+ void reset(T* = NULL) throw();
+
+ auto_array< T >& operator=(auto_array< T >&) throw();
+ auto_array< T >& operator=(auto_array_ref< T >) throw();
+
+ T& operator[](int) throw();
+ operator auto_array_ref< T >(void) throw();
+};
+
+template< class T >
+auto_array< T >::auto_array(T* ptr)
+ throw() :
+ m_ptr(ptr)
+{
+}
+
+template< class T >
+auto_array< T >::auto_array(auto_array< T >& ptr)
+ throw() :
+ m_ptr(ptr.release())
+{
+}
+
+template< class T >
+auto_array< T >::auto_array(auto_array_ref< T > ref)
+ throw() :
+ m_ptr(ref.m_ptr)
+{
+}
+
+template< class T >
+auto_array< T >::~auto_array(void)
+ throw()
+{
+ if (m_ptr != NULL)
+ delete [] m_ptr;
+}
+
+template< class T >
+T*
+auto_array< T >::get(void)
+ throw()
+{
+ return m_ptr;
+}
+
+template< class T >
+const T*
+auto_array< T >::get(void)
+ const throw()
+{
+ return m_ptr;
+}
+
+template< class T >
+T*
+auto_array< T >::release(void)
+ throw()
+{
+ T* ptr = m_ptr;
+ m_ptr = NULL;
+ return ptr;
+}
+
+template< class T >
+void
+auto_array< T >::reset(T* ptr)
+ throw()
+{
+ if (m_ptr != NULL)
+ delete [] m_ptr;
+ m_ptr = ptr;
+}
+
+template< class T >
+auto_array< T >&
+auto_array< T >::operator=(auto_array< T >& ptr)
+ throw()
+{
+ reset(ptr.release());
+ return *this;
+}
+
+template< class T >
+auto_array< T >&
+auto_array< T >::operator=(auto_array_ref< T > ref)
+ throw()
+{
+ if (m_ptr != ref.m_ptr) {
+ delete [] m_ptr;
+ m_ptr = ref.m_ptr;
+ }
+ return *this;
+}
+
+template< class T >
+T&
+auto_array< T >::operator[](int pos)
+ throw()
+{
+ return m_ptr[pos];
+}
+
+template< class T >
+auto_array< T >::operator auto_array_ref< T >(void)
+ throw()
+{
+ return auto_array_ref< T >(release());
+}
+
+} // namespace atf
+
+#endif // !defined(ATF_CXX_DETAIL_AUTO_ARRAY_HPP)
diff --git a/unit/atf-src/atf-c++/detail/auto_array_test.cpp b/unit/atf-src/atf-c++/detail/auto_array_test.cpp
new file mode 100644
index 0000000..aaad90e
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/auto_array_test.cpp
@@ -0,0 +1,302 @@
+// 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/auto_array.hpp"
+
+extern "C" {
+#include <sys/types.h>
+}
+
+#include <iostream>
+
+#include <atf-c++.hpp>
+
+extern "C" {
+#include "atf-c/defs.h"
+}
+
+// ------------------------------------------------------------------------
+// Tests for the "auto_array" class.
+// ------------------------------------------------------------------------
+
+class test_array {
+public:
+ int m_value;
+
+ static ssize_t m_nblocks;
+
+ static
+ atf::auto_array< test_array >
+ do_copy(atf::auto_array< test_array >& ta)
+ {
+ return atf::auto_array< test_array >(ta);
+ }
+
+ void* operator new(size_t size ATF_DEFS_ATTRIBUTE_UNUSED)
+ {
+ ATF_FAIL("New called but should have been new[]");
+ return new int(5);
+ }
+
+ void* operator new[](size_t size)
+ {
+ m_nblocks++;
+ void* mem = ::operator new(size);
+ std::cout << "Allocated 'test_array' object " << mem << "\n";
+ return mem;
+ }
+
+ void operator delete(void* mem ATF_DEFS_ATTRIBUTE_UNUSED)
+ {
+ ATF_FAIL("Delete called but should have been delete[]");
+ }
+
+ void operator delete[](void* mem)
+ {
+ std::cout << "Releasing 'test_array' object " << mem << "\n";
+ if (m_nblocks == 0)
+ ATF_FAIL("Unbalanced delete[]");
+ m_nblocks--;
+ ::operator delete(mem);
+ }
+};
+
+ssize_t test_array::m_nblocks = 0;
+
+ATF_TEST_CASE(auto_array_scope);
+ATF_TEST_CASE_HEAD(auto_array_scope)
+{
+ set_md_var("descr", "Tests the automatic scope handling in the "
+ "auto_array smart pointer class");
+}
+ATF_TEST_CASE_BODY(auto_array_scope)
+{
+ using atf::auto_array;
+
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+ {
+ auto_array< test_array > t(new test_array[10]);
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 1);
+ }
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+}
+
+ATF_TEST_CASE(auto_array_copy);
+ATF_TEST_CASE_HEAD(auto_array_copy)
+{
+ set_md_var("descr", "Tests the auto_array smart pointer class' copy "
+ "constructor");
+}
+ATF_TEST_CASE_BODY(auto_array_copy)
+{
+ using atf::auto_array;
+
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+ {
+ auto_array< test_array > t1(new test_array[10]);
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 1);
+
+ {
+ auto_array< test_array > t2(t1);
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 1);
+ }
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+ }
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+}
+
+ATF_TEST_CASE(auto_array_copy_ref);
+ATF_TEST_CASE_HEAD(auto_array_copy_ref)
+{
+ set_md_var("descr", "Tests the auto_array smart pointer class' copy "
+ "constructor through the auxiliary auto_array_ref object");
+}
+ATF_TEST_CASE_BODY(auto_array_copy_ref)
+{
+ using atf::auto_array;
+
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+ {
+ auto_array< test_array > t1(new test_array[10]);
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 1);
+
+ {
+ auto_array< test_array > t2 = test_array::do_copy(t1);
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 1);
+ }
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+ }
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+}
+
+ATF_TEST_CASE(auto_array_get);
+ATF_TEST_CASE_HEAD(auto_array_get)
+{
+ set_md_var("descr", "Tests the auto_array smart pointer class' get "
+ "method");
+}
+ATF_TEST_CASE_BODY(auto_array_get)
+{
+ using atf::auto_array;
+
+ test_array* ta = new test_array[10];
+ auto_array< test_array > t(ta);
+ ATF_REQUIRE_EQ(t.get(), ta);
+}
+
+ATF_TEST_CASE(auto_array_release);
+ATF_TEST_CASE_HEAD(auto_array_release)
+{
+ set_md_var("descr", "Tests the auto_array smart pointer class' release "
+ "method");
+}
+ATF_TEST_CASE_BODY(auto_array_release)
+{
+ using atf::auto_array;
+
+ test_array* ta1 = new test_array[10];
+ {
+ auto_array< test_array > t(ta1);
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 1);
+ test_array* ta2 = t.release();
+ ATF_REQUIRE_EQ(ta2, ta1);
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 1);
+ }
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 1);
+ delete [] ta1;
+}
+
+ATF_TEST_CASE(auto_array_reset);
+ATF_TEST_CASE_HEAD(auto_array_reset)
+{
+ set_md_var("descr", "Tests the auto_array smart pointer class' reset "
+ "method");
+}
+ATF_TEST_CASE_BODY(auto_array_reset)
+{
+ using atf::auto_array;
+
+ test_array* ta1 = new test_array[10];
+ test_array* ta2 = new test_array[10];
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 2);
+
+ {
+ auto_array< test_array > t(ta1);
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 2);
+ t.reset(ta2);
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 1);
+ t.reset();
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+ }
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+}
+
+ATF_TEST_CASE(auto_array_assign);
+ATF_TEST_CASE_HEAD(auto_array_assign)
+{
+ set_md_var("descr", "Tests the auto_array smart pointer class' "
+ "assignment operator");
+}
+ATF_TEST_CASE_BODY(auto_array_assign)
+{
+ using atf::auto_array;
+
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+ {
+ auto_array< test_array > t1(new test_array[10]);
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 1);
+
+ {
+ auto_array< test_array > t2;
+ t2 = t1;
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 1);
+ }
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+ }
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+}
+
+ATF_TEST_CASE(auto_array_assign_ref);
+ATF_TEST_CASE_HEAD(auto_array_assign_ref)
+{
+ set_md_var("descr", "Tests the auto_array smart pointer class' "
+ "assignment operator through the auxiliary auto_array_ref "
+ "object");
+}
+ATF_TEST_CASE_BODY(auto_array_assign_ref)
+{
+ using atf::auto_array;
+
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+ {
+ auto_array< test_array > t1(new test_array[10]);
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 1);
+
+ {
+ auto_array< test_array > t2;
+ t2 = test_array::do_copy(t1);
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 1);
+ }
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+ }
+ ATF_REQUIRE_EQ(test_array::m_nblocks, 0);
+}
+
+ATF_TEST_CASE(auto_array_access);
+ATF_TEST_CASE_HEAD(auto_array_access)
+{
+ set_md_var("descr", "Tests the auto_array smart pointer class' access "
+ "operator");
+}
+ATF_TEST_CASE_BODY(auto_array_access)
+{
+ using atf::auto_array;
+
+ auto_array< test_array > t(new test_array[10]);
+
+ for (int i = 0; i < 10; i++)
+ t[i].m_value = i * 2;
+
+ for (int i = 0; i < 10; i++)
+ ATF_REQUIRE_EQ(t[i].m_value, i * 2);
+}
+
+// ------------------------------------------------------------------------
+// Main.
+// ------------------------------------------------------------------------
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ // Add the test for the "auto_array" class.
+ ATF_ADD_TEST_CASE(tcs, auto_array_scope);
+ ATF_ADD_TEST_CASE(tcs, auto_array_copy);
+ ATF_ADD_TEST_CASE(tcs, auto_array_copy_ref);
+ ATF_ADD_TEST_CASE(tcs, auto_array_get);
+ ATF_ADD_TEST_CASE(tcs, auto_array_release);
+ ATF_ADD_TEST_CASE(tcs, auto_array_reset);
+ ATF_ADD_TEST_CASE(tcs, auto_array_assign);
+ ATF_ADD_TEST_CASE(tcs, auto_array_assign_ref);
+ ATF_ADD_TEST_CASE(tcs, auto_array_access);
+}
diff --git a/unit/atf-src/atf-c++/detail/env.cpp b/unit/atf-src/atf-c++/detail/env.cpp
new file mode 100644
index 0000000..8855dcb
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/env.cpp
@@ -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/env.hpp"
+
+extern "C" {
+#include "atf-c/detail/env.h"
+#include "atf-c/error.h"
+}
+
+#include "atf-c++/detail/exceptions.hpp"
+#include "atf-c++/detail/sanity.hpp"
+
+namespace impl = atf::env;
+#define IMPL_NAME "atf::env"
+
+// ------------------------------------------------------------------------
+// Free functions.
+// ------------------------------------------------------------------------
+
+std::string
+impl::get(const std::string& name)
+{
+ return atf_env_get(name.c_str());
+}
+
+std::string
+impl::get(const std::string& name, const std::string& default_value)
+{
+ return atf_env_get_with_default(name.c_str(), default_value.c_str());
+}
+
+bool
+impl::has(const std::string& name)
+{
+ return atf_env_has(name.c_str());
+}
+
+void
+impl::set(const std::string& name, const std::string& val)
+{
+ atf_error_t err = atf_env_set(name.c_str(), val.c_str());
+ if (atf_is_error(err))
+ throw_atf_error(err);
+}
+
+void
+impl::unset(const std::string& name)
+{
+ atf_error_t err = atf_env_unset(name.c_str());
+ if (atf_is_error(err))
+ throw_atf_error(err);
+}
diff --git a/unit/atf-src/atf-c++/detail/env.hpp b/unit/atf-src/atf-c++/detail/env.hpp
new file mode 100644
index 0000000..e1dcbc0
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/env.hpp
@@ -0,0 +1,85 @@
+// 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_CXX_DETAIL_ENV_HPP)
+#define ATF_CXX_DETAIL_ENV_HPP
+
+#include <string>
+
+namespace atf {
+namespace env {
+
+// ------------------------------------------------------------------------
+// Free functions.
+// ------------------------------------------------------------------------
+
+//!
+//! \brief Returns the value of an environment variable.
+//!
+//! Returns the value of the specified environment variable. The variable
+//! must be defined.
+//!
+std::string get(const std::string&);
+
+//!
+//! \brief Returns the value of an environment variable with a default.
+//!
+std::string get(const std::string&, const std::string&);
+
+//!
+//! \brief Checks if the environment has a variable.
+//!
+//! Checks if the environment has a given variable.
+//!
+bool has(const std::string&);
+
+//!
+//! \brief Sets an environment variable to a given value.
+//!
+//! Sets the specified environment variable to the given value. Note that
+//! variables set to the empty string are different to undefined ones.
+//!
+//! Be aware that this alters the program's global status, which in general
+//! is a bad thing to do due to the side-effects it may have. There are
+//! some legitimate usages for this function, though.
+//!
+void set(const std::string&, const std::string&);
+
+//!
+//! \brief Unsets an environment variable.
+//!
+//! Unsets the specified environment variable Note that undefined
+//! variables are different to those defined but set to an empty value.
+//!
+//! Be aware that this alters the program's global status, which in general
+//! is a bad thing to do due to the side-effects it may have. There are
+//! some legitimate usages for this function, though.
+//!
+void unset(const std::string&);
+
+} // namespace env
+} // namespace atf
+
+#endif // !defined(ATF_CXX_DETAIL_ENV_HPP)
diff --git a/unit/atf-src/atf-c++/detail/env_test.cpp b/unit/atf-src/atf-c++/detail/env_test.cpp
new file mode 100644
index 0000000..91616f3
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/env_test.cpp
@@ -0,0 +1,101 @@
+// 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.hpp"
+
+#include <atf-c++.hpp>
+
+// ------------------------------------------------------------------------
+// Test cases for the free functions.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(has_get);
+ATF_TEST_CASE_HEAD(has_get)
+{
+ set_md_var("descr", "Tests the has and get functions");
+}
+ATF_TEST_CASE_BODY(has_get)
+{
+ ATF_REQUIRE(atf::env::has("PATH"));
+ ATF_REQUIRE(!atf::env::get("PATH").empty());
+
+ ATF_REQUIRE(!atf::env::has("_UNDEFINED_VARIABLE_"));
+}
+
+ATF_TEST_CASE(get_with_default);
+ATF_TEST_CASE_HEAD(get_with_default)
+{
+ set_md_var("descr", "Tests the get function with a default value");
+}
+ATF_TEST_CASE_BODY(get_with_default)
+{
+ ATF_REQUIRE(atf::env::has("PATH"));
+ ATF_REQUIRE(atf::env::get("PATH", "default value") != "default value");
+
+ ATF_REQUIRE_EQ(atf::env::get("_UNDEFINED_VARIABLE_", "foo bar"), "foo bar");
+}
+
+ATF_TEST_CASE(set);
+ATF_TEST_CASE_HEAD(set)
+{
+ set_md_var("descr", "Tests the set function");
+}
+ATF_TEST_CASE_BODY(set)
+{
+ ATF_REQUIRE(atf::env::has("PATH"));
+ const std::string& oldval = atf::env::get("PATH");
+ atf::env::set("PATH", "foo-bar");
+ ATF_REQUIRE(atf::env::get("PATH") != oldval);
+ ATF_REQUIRE_EQ(atf::env::get("PATH"), "foo-bar");
+
+ ATF_REQUIRE(!atf::env::has("_UNDEFINED_VARIABLE_"));
+ atf::env::set("_UNDEFINED_VARIABLE_", "foo2-bar2");
+ ATF_REQUIRE_EQ(atf::env::get("_UNDEFINED_VARIABLE_"), "foo2-bar2");
+}
+
+ATF_TEST_CASE(unset);
+ATF_TEST_CASE_HEAD(unset)
+{
+ set_md_var("descr", "Tests the unset function");
+}
+ATF_TEST_CASE_BODY(unset)
+{
+ ATF_REQUIRE(atf::env::has("PATH"));
+ atf::env::unset("PATH");
+ ATF_REQUIRE(!atf::env::has("PATH"));
+}
+
+// ------------------------------------------------------------------------
+// Main.
+// ------------------------------------------------------------------------
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ // Add the test cases for the free functions.
+ ATF_ADD_TEST_CASE(tcs, has_get);
+ ATF_ADD_TEST_CASE(tcs, get_with_default);
+ ATF_ADD_TEST_CASE(tcs, set);
+ ATF_ADD_TEST_CASE(tcs, unset);
+}
diff --git a/unit/atf-src/atf-c++/detail/exceptions.cpp b/unit/atf-src/atf-c++/detail/exceptions.cpp
new file mode 100644
index 0000000..a5b74a3
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/exceptions.cpp
@@ -0,0 +1,154 @@
+// 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/exceptions.hpp"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstring>
+#include <new>
+
+extern "C" {
+#include "atf-c/error.h"
+}
+
+#include "atf-c++/detail/sanity.hpp"
+
+// ------------------------------------------------------------------------
+// The "system_error" type.
+// ------------------------------------------------------------------------
+
+atf::system_error::system_error(const std::string& who,
+ const std::string& message,
+ int sys_err) :
+ std::runtime_error(who + ": " + message),
+ m_sys_err(sys_err)
+{
+}
+
+atf::system_error::~system_error(void)
+ throw()
+{
+}
+
+int
+atf::system_error::code(void)
+ const
+ throw()
+{
+ return m_sys_err;
+}
+
+const char*
+atf::system_error::what(void)
+ const
+ throw()
+{
+ try {
+ if (m_message.length() == 0) {
+ m_message = std::string(std::runtime_error::what()) + ": ";
+ m_message += ::strerror(m_sys_err);
+ }
+
+ return m_message.c_str();
+ } catch (...) {
+ return "Unable to format system_error message";
+ }
+}
+
+// ------------------------------------------------------------------------
+// Free functions.
+// ------------------------------------------------------------------------
+
+static
+void
+throw_libc_error(atf_error_t err)
+{
+ PRE(atf_error_is(err, "libc"));
+
+ const int ecode = atf_libc_error_code(err);
+ const std::string msg = atf_libc_error_msg(err);
+ atf_error_free(err);
+ throw atf::system_error("XXX", msg, ecode);
+}
+
+static
+void
+throw_no_memory_error(atf_error_t err)
+{
+ PRE(atf_error_is(err, "no_memory"));
+
+ atf_error_free(err);
+ throw std::bad_alloc();
+}
+
+static
+void
+throw_unknown_error(atf_error_t err)
+{
+ PRE(atf_is_error(err));
+
+ static char buf[4096];
+ atf_error_format(err, buf, sizeof(buf));
+ atf_error_free(err);
+ throw std::runtime_error(buf);
+}
+
+void
+atf::throw_atf_error(atf_error_t err)
+{
+ static struct handler {
+ const char* m_name;
+ void (*m_func)(atf_error_t);
+ } handlers[] = {
+ { "libc", throw_libc_error },
+ { "no_memory", throw_no_memory_error },
+ { NULL, throw_unknown_error },
+ };
+
+ PRE(atf_is_error(err));
+
+ handler* h = handlers;
+ while (h->m_name != NULL) {
+ if (atf_error_is(err, h->m_name)) {
+ h->m_func(err);
+ UNREACHABLE;
+ } else
+ h++;
+ }
+ // XXX: I'm not sure that raising an "unknown" error is a wise thing
+ // to do here. The C++ binding is supposed to have feature parity
+ // with the C one, so all possible errors raised by the C library
+ // should have their counterpart in the C++ library. Still, removing
+ // this will require some code auditing that I can't afford at the
+ // moment.
+ INV(h->m_name == NULL && h->m_func != NULL);
+ h->m_func(err);
+ UNREACHABLE;
+}
diff --git a/unit/atf-src/atf-c++/detail/exceptions.hpp b/unit/atf-src/atf-c++/detail/exceptions.hpp
new file mode 100644
index 0000000..0728ad1
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/exceptions.hpp
@@ -0,0 +1,54 @@
+// 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_CXX_DETAIL_EXCEPTIONS_HPP)
+#define ATF_CXX_DETAIL_EXCEPTIONS_HPP
+
+#include <stdexcept>
+#include <string>
+
+extern "C" {
+struct atf_error;
+}
+
+namespace atf {
+
+class system_error : public std::runtime_error {
+ int m_sys_err;
+ mutable std::string m_message;
+
+public:
+ system_error(const std::string&, const std::string&, int);
+ ~system_error(void) throw();
+
+ int code(void) const throw();
+ const char* what(void) const throw();
+};
+
+void throw_atf_error(struct atf_error *);
+
+} // namespace atf
+
+#endif // !defined(ATF_CXX_DETAIL_EXCEPTIONS_HPP)
diff --git a/unit/atf-src/atf-c++/detail/exceptions_test.cpp b/unit/atf-src/atf-c++/detail/exceptions_test.cpp
new file mode 100644
index 0000000..c0bf4fc
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/exceptions_test.cpp
@@ -0,0 +1,145 @@
+// 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++/detail/exceptions.hpp"
+
+extern "C" {
+#include "atf-c/error.h"
+}
+
+#include <cstdio>
+#include <new>
+
+#include <atf-c++.hpp>
+
+#include "atf-c++/detail/sanity.hpp"
+
+// ------------------------------------------------------------------------
+// The "test" error.
+// ------------------------------------------------------------------------
+
+extern "C" {
+
+struct test_error_data {
+ const char* m_msg;
+};
+typedef struct test_error_data test_error_data_t;
+
+static
+void
+test_format(const atf_error_t err, char *buf, size_t buflen)
+{
+ const test_error_data_t* data;
+
+ PRE(atf_error_is(err, "test"));
+
+ data = static_cast< const test_error_data_t * >(atf_error_data(err));
+ snprintf(buf, buflen, "Message: %s", data->m_msg);
+}
+
+static
+atf_error_t
+test_error(const char* msg)
+{
+ atf_error_t err;
+ test_error_data_t data;
+
+ data.m_msg = msg;
+
+ err = atf_error_new("test", &data, sizeof(data), test_format);
+
+ return err;
+}
+
+} // extern
+
+// ------------------------------------------------------------------------
+// Tests cases for the free functions.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(throw_atf_error_libc);
+ATF_TEST_CASE_HEAD(throw_atf_error_libc)
+{
+ set_md_var("descr", "Tests the throw_atf_error function when raising "
+ "a libc error");
+}
+ATF_TEST_CASE_BODY(throw_atf_error_libc)
+{
+ try {
+ atf::throw_atf_error(atf_libc_error(1, "System error 1"));
+ } catch (const atf::system_error& e) {
+ ATF_REQUIRE(e.code() == 1);
+ ATF_REQUIRE(std::string(e.what()).find("System error 1") !=
+ std::string::npos);
+ } catch (const std::exception& e) {
+ ATF_FAIL(std::string("Got unexpected exception: ") + e.what());
+ }
+}
+
+ATF_TEST_CASE(throw_atf_error_no_memory);
+ATF_TEST_CASE_HEAD(throw_atf_error_no_memory)
+{
+ set_md_var("descr", "Tests the throw_atf_error function when raising "
+ "a no_memory error");
+}
+ATF_TEST_CASE_BODY(throw_atf_error_no_memory)
+{
+ try {
+ atf::throw_atf_error(atf_no_memory_error());
+ } catch (const std::bad_alloc&) {
+ } catch (const std::exception& e) {
+ ATF_FAIL(std::string("Got unexpected exception: ") + e.what());
+ }
+}
+
+ATF_TEST_CASE(throw_atf_error_unknown);
+ATF_TEST_CASE_HEAD(throw_atf_error_unknown)
+{
+ set_md_var("descr", "Tests the throw_atf_error function when raising "
+ "an unknown error");
+}
+ATF_TEST_CASE_BODY(throw_atf_error_unknown)
+{
+ try {
+ atf::throw_atf_error(test_error("The message"));
+ } catch (const std::runtime_error& e) {
+ const std::string msg = e.what();
+ ATF_REQUIRE(msg.find("The message") != std::string::npos);
+ } catch (const std::exception& e) {
+ ATF_FAIL(std::string("Got unexpected exception: ") + e.what());
+ }
+}
+
+// ------------------------------------------------------------------------
+// Main.
+// ------------------------------------------------------------------------
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ // Add the test cases for the free functions.
+ ATF_ADD_TEST_CASE(tcs, throw_atf_error_libc);
+ ATF_ADD_TEST_CASE(tcs, throw_atf_error_no_memory);
+ ATF_ADD_TEST_CASE(tcs, throw_atf_error_unknown);
+}
diff --git a/unit/atf-src/atf-c++/detail/fs.cpp b/unit/atf-src/atf-c++/detail/fs.cpp
new file mode 100644
index 0000000..bcef920
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/fs.cpp
@@ -0,0 +1,513 @@
+// 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.hpp"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+extern "C" {
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <libgen.h>
+#include <unistd.h>
+}
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+
+extern "C" {
+#include "atf-c/error.h"
+}
+
+#include "atf-c++/detail/env.hpp"
+#include "atf-c++/detail/exceptions.hpp"
+#include "atf-c++/detail/process.hpp"
+#include "atf-c++/detail/sanity.hpp"
+#include "atf-c++/detail/text.hpp"
+#include "atf-c++/utils.hpp"
+
+namespace impl = atf::fs;
+#define IMPL_NAME "atf::fs"
+
+// ------------------------------------------------------------------------
+// Auxiliary functions.
+// ------------------------------------------------------------------------
+
+static bool safe_access(const impl::path&, int, int);
+
+//!
+//! \brief A controlled version of access(2).
+//!
+//! This function reimplements the standard access(2) system call to
+//! safely control its exit status and raise an exception in case of
+//! failure.
+//!
+static
+bool
+safe_access(const impl::path& p, int mode, int experr)
+{
+ bool ok;
+
+ atf_error_t err = atf_fs_eaccess(p.c_path(), mode);
+ if (atf_is_error(err)) {
+ if (atf_error_is(err, "libc")) {
+ if (atf_libc_error_code(err) == experr) {
+ atf_error_free(err);
+ ok = false;
+ } else {
+ atf::throw_atf_error(err);
+ // XXX Silence warning; maybe throw_atf_error should be
+ // an exception and not a function.
+ ok = false;
+ }
+ } else {
+ atf::throw_atf_error(err);
+ // XXX Silence warning; maybe throw_atf_error should be
+ // an exception and not a function.
+ ok = false;
+ }
+ } else
+ ok = true;
+
+ return ok;
+}
+
+// ------------------------------------------------------------------------
+// The "path" class.
+// ------------------------------------------------------------------------
+
+impl::path::path(const std::string& s)
+{
+ atf_error_t err = atf_fs_path_init_fmt(&m_path, "%s", s.c_str());
+ if (atf_is_error(err))
+ throw_atf_error(err);
+}
+
+impl::path::path(const path& p)
+{
+ atf_error_t err = atf_fs_path_copy(&m_path, &p.m_path);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+}
+
+impl::path::path(const atf_fs_path_t *p)
+{
+ atf_error_t err = atf_fs_path_copy(&m_path, p);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+}
+
+impl::path::~path(void)
+{
+ atf_fs_path_fini(&m_path);
+}
+
+const char*
+impl::path::c_str(void)
+ const
+{
+ return atf_fs_path_cstring(&m_path);
+}
+
+const atf_fs_path_t*
+impl::path::c_path(void)
+ const
+{
+ return &m_path;
+}
+
+std::string
+impl::path::str(void)
+ const
+{
+ return c_str();
+}
+
+bool
+impl::path::is_absolute(void)
+ const
+{
+ return atf_fs_path_is_absolute(&m_path);
+}
+
+bool
+impl::path::is_root(void)
+ const
+{
+ return atf_fs_path_is_root(&m_path);
+}
+
+impl::path
+impl::path::branch_path(void)
+ const
+{
+ atf_fs_path_t bp;
+ atf_error_t err;
+
+ err = atf_fs_path_branch_path(&m_path, &bp);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ path p(atf_fs_path_cstring(&bp));
+ atf_fs_path_fini(&bp);
+ return p;
+}
+
+std::string
+impl::path::leaf_name(void)
+ const
+{
+ atf_dynstr_t ln;
+ atf_error_t err;
+
+ err = atf_fs_path_leaf_name(&m_path, &ln);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ std::string s(atf_dynstr_cstring(&ln));
+ atf_dynstr_fini(&ln);
+ return s;
+}
+
+impl::path
+impl::path::to_absolute(void)
+ const
+{
+ atf_fs_path_t pa;
+
+ atf_error_t err = atf_fs_path_to_absolute(&m_path, &pa);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ path p(atf_fs_path_cstring(&pa));
+ atf_fs_path_fini(&pa);
+ return p;
+}
+
+impl::path&
+impl::path::operator=(const path& p)
+{
+ atf_fs_path_t tmp;
+
+ atf_error_t err = atf_fs_path_init_fmt(&tmp, "%s", p.c_str());
+ if (atf_is_error(err))
+ throw_atf_error(err);
+ else {
+ atf_fs_path_fini(&m_path);
+ m_path = tmp;
+ }
+
+ return *this;
+}
+
+bool
+impl::path::operator==(const path& p)
+ const
+{
+ return atf_equal_fs_path_fs_path(&m_path, &p.m_path);
+}
+
+bool
+impl::path::operator!=(const path& p)
+ const
+{
+ return !atf_equal_fs_path_fs_path(&m_path, &p.m_path);
+}
+
+impl::path
+impl::path::operator/(const std::string& p)
+ const
+{
+ path p2 = *this;
+
+ atf_error_t err = atf_fs_path_append_fmt(&p2.m_path, "%s", p.c_str());
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ return p2;
+}
+
+impl::path
+impl::path::operator/(const path& p)
+ const
+{
+ path p2 = *this;
+
+ atf_error_t err = atf_fs_path_append_fmt(&p2.m_path, "%s",
+ atf_fs_path_cstring(&p.m_path));
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ return p2;
+}
+
+bool
+impl::path::operator<(const path& p)
+ const
+{
+ const char *s1 = atf_fs_path_cstring(&m_path);
+ const char *s2 = atf_fs_path_cstring(&p.m_path);
+ return std::strcmp(s1, s2) < 0;
+}
+
+// ------------------------------------------------------------------------
+// The "file_info" class.
+// ------------------------------------------------------------------------
+
+const int impl::file_info::blk_type = atf_fs_stat_blk_type;
+const int impl::file_info::chr_type = atf_fs_stat_chr_type;
+const int impl::file_info::dir_type = atf_fs_stat_dir_type;
+const int impl::file_info::fifo_type = atf_fs_stat_fifo_type;
+const int impl::file_info::lnk_type = atf_fs_stat_lnk_type;
+const int impl::file_info::reg_type = atf_fs_stat_reg_type;
+const int impl::file_info::sock_type = atf_fs_stat_sock_type;
+const int impl::file_info::wht_type = atf_fs_stat_wht_type;
+
+impl::file_info::file_info(const path& p)
+{
+ atf_error_t err;
+
+ err = atf_fs_stat_init(&m_stat, p.c_path());
+ if (atf_is_error(err))
+ throw_atf_error(err);
+}
+
+impl::file_info::file_info(const file_info& fi)
+{
+ atf_fs_stat_copy(&m_stat, &fi.m_stat);
+}
+
+impl::file_info::~file_info(void)
+{
+ atf_fs_stat_fini(&m_stat);
+}
+
+dev_t
+impl::file_info::get_device(void)
+ const
+{
+ return atf_fs_stat_get_device(&m_stat);
+}
+
+ino_t
+impl::file_info::get_inode(void)
+ const
+{
+ return atf_fs_stat_get_inode(&m_stat);
+}
+
+mode_t
+impl::file_info::get_mode(void)
+ const
+{
+ return atf_fs_stat_get_mode(&m_stat);
+}
+
+off_t
+impl::file_info::get_size(void)
+ const
+{
+ return atf_fs_stat_get_size(&m_stat);
+}
+
+int
+impl::file_info::get_type(void)
+ const
+{
+ return atf_fs_stat_get_type(&m_stat);
+}
+
+bool
+impl::file_info::is_owner_readable(void)
+ const
+{
+ return atf_fs_stat_is_owner_readable(&m_stat);
+}
+
+bool
+impl::file_info::is_owner_writable(void)
+ const
+{
+ return atf_fs_stat_is_owner_writable(&m_stat);
+}
+
+bool
+impl::file_info::is_owner_executable(void)
+ const
+{
+ return atf_fs_stat_is_owner_executable(&m_stat);
+}
+
+bool
+impl::file_info::is_group_readable(void)
+ const
+{
+ return atf_fs_stat_is_group_readable(&m_stat);
+}
+
+bool
+impl::file_info::is_group_writable(void)
+ const
+{
+ return atf_fs_stat_is_group_writable(&m_stat);
+}
+
+bool
+impl::file_info::is_group_executable(void)
+ const
+{
+ return atf_fs_stat_is_group_executable(&m_stat);
+}
+
+bool
+impl::file_info::is_other_readable(void)
+ const
+{
+ return atf_fs_stat_is_other_readable(&m_stat);
+}
+
+bool
+impl::file_info::is_other_writable(void)
+ const
+{
+ return atf_fs_stat_is_other_writable(&m_stat);
+}
+
+bool
+impl::file_info::is_other_executable(void)
+ const
+{
+ return atf_fs_stat_is_other_executable(&m_stat);
+}
+
+// ------------------------------------------------------------------------
+// The "directory" class.
+// ------------------------------------------------------------------------
+
+impl::directory::directory(const path& p)
+{
+ DIR* dp = ::opendir(p.c_str());
+ if (dp == NULL)
+ throw system_error(IMPL_NAME "::directory::directory(" +
+ p.str() + ")", "opendir(3) failed", errno);
+
+ struct dirent* dep;
+ while ((dep = ::readdir(dp)) != NULL) {
+ path entryp = p / dep->d_name;
+ insert(value_type(dep->d_name, file_info(entryp)));
+ }
+
+ if (::closedir(dp) == -1)
+ throw system_error(IMPL_NAME "::directory::directory(" +
+ p.str() + ")", "closedir(3) failed", errno);
+}
+
+std::set< std::string >
+impl::directory::names(void)
+ const
+{
+ std::set< std::string > ns;
+
+ for (const_iterator iter = begin(); iter != end(); iter++)
+ ns.insert((*iter).first);
+
+ return ns;
+}
+
+// ------------------------------------------------------------------------
+// Free functions.
+// ------------------------------------------------------------------------
+
+bool
+impl::exists(const path& p)
+{
+ atf_error_t err;
+ bool b;
+
+ err = atf_fs_exists(p.c_path(), &b);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ return b;
+}
+
+bool
+impl::have_prog_in_path(const std::string& prog)
+{
+ PRE(prog.find('/') == std::string::npos);
+
+ // Do not bother to provide a default value for PATH. If it is not
+ // there something is broken in the user's environment.
+ if (!atf::env::has("PATH"))
+ throw std::runtime_error("PATH not defined in the environment");
+ std::vector< std::string > dirs =
+ atf::text::split(atf::env::get("PATH"), ":");
+
+ bool found = false;
+ for (std::vector< std::string >::const_iterator iter = dirs.begin();
+ !found && iter != dirs.end(); iter++) {
+ const path& dir = path(*iter);
+
+ if (is_executable(dir / prog))
+ found = true;
+ }
+ return found;
+}
+
+bool
+impl::is_executable(const path& p)
+{
+ if (!exists(p))
+ return false;
+ return safe_access(p, atf_fs_access_x, EACCES);
+}
+
+void
+impl::remove(const path& p)
+{
+ if (file_info(p).get_type() == file_info::dir_type)
+ throw atf::system_error(IMPL_NAME "::remove(" + p.str() + ")",
+ "Is a directory",
+ EPERM);
+ if (::unlink(p.c_str()) == -1)
+ throw atf::system_error(IMPL_NAME "::remove(" + p.str() + ")",
+ "unlink(" + p.str() + ") failed",
+ errno);
+}
+
+void
+impl::rmdir(const path& p)
+{
+ atf_error_t err = atf_fs_rmdir(p.c_path());
+ if (atf_is_error(err))
+ throw_atf_error(err);
+}
diff --git a/unit/atf-src/atf-c++/detail/fs.hpp b/unit/atf-src/atf-c++/detail/fs.hpp
new file mode 100644
index 0000000..d6670f6
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/fs.hpp
@@ -0,0 +1,387 @@
+// 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_CXX_DETAIL_FS_HPP)
+#define ATF_CXX_DETAIL_FS_HPP
+
+extern "C" {
+#include <sys/types.h>
+}
+
+#include <map>
+#include <memory>
+#include <ostream>
+#include <set>
+#include <stdexcept>
+#include <string>
+
+extern "C" {
+#include "atf-c/detail/fs.h"
+}
+
+namespace atf {
+
+namespace io {
+class systembuf;
+} // namespace io
+
+namespace fs {
+
+// ------------------------------------------------------------------------
+// The "path" class.
+// ------------------------------------------------------------------------
+
+//!
+//! \brief A class to represent a path to a file.
+//!
+//! The path class represents the route to a file or directory in the
+//! file system. All file manipulation operations use this class to
+//! represent their arguments as it takes care of normalizing user-provided
+//! strings and ensures they are valid.
+//!
+//! It is important to note that the file pointed to by a path need not
+//! exist.
+//!
+class path {
+ //!
+ //! \brief Internal representation of a path.
+ //!
+ atf_fs_path_t m_path;
+
+public:
+ //! \brief Constructs a new path from a user-provided string.
+ //!
+ //! This constructor takes a string, either provided by the program's
+ //! code or by the user and constructs a new path object. The string
+ //! is normalized to not contain multiple delimiters together and to
+ //! remove any trailing one.
+ //!
+ //! The input string cannot be empty.
+ //!
+ explicit path(const std::string&);
+
+ //!
+ //! \brief Copy constructor.
+ //!
+ path(const path&);
+
+ //!
+ //! \brief Copy constructor.
+ //!
+ path(const atf_fs_path_t *);
+
+ //!
+ //! \brief Destructor for the path class.
+ //!
+ ~path(void);
+
+ //!
+ //! \brief Returns a pointer to a C-style string representing this path.
+ //!
+ const char* c_str(void) const;
+
+ //!
+ //! \brief Returns a pointer to the implementation data.
+ //!
+ const atf_fs_path_t* c_path(void) const;
+
+ //!
+ //! \brief Returns a string representing this path.
+ //! XXX Really needed?
+ //!
+ std::string str(void) const;
+
+ //!
+ //! \brief Returns the branch path of this path.
+ //!
+ //! Calculates and returns the branch path of this path. In other
+ //! words, it returns what the standard ::dirname function would return.
+ //!
+ path branch_path(void) const;
+
+ //!
+ //! \brief Returns the leaf name of this path.
+ //!
+ //! Calculates and returns the leaf name of this path. In other words,
+ //! it returns what the standard ::basename function would return.
+ //!
+ std::string leaf_name(void) const;
+
+ //!
+ //! \brief Checks whether this path is absolute or not.
+ //!
+ //! Returns a boolean indicating if this is an absolute path or not;
+ //! i.e. if it starts with a slash.
+ //!
+ bool is_absolute(void) const;
+
+ //!
+ //! \brief Checks whether this path points to the root directory or not.
+ //!
+ //! Returns a boolean indicating if this is path points to the root
+ //! directory or not. The checks made by this are extremely simple (so
+ //! the results cannot always be trusted) but they are enough for our
+ //! modest sanity-checking needs. I.e. "/../" could return false.
+ //!
+ bool is_root(void) const;
+
+ //!
+ //! \brief Converts the path to be absolute.
+ //!
+ //! \pre The path was not absolute.
+ //!
+ path to_absolute(void) const;
+
+ //!
+ //! \brief Assignment operator.
+ //!
+ path& operator=(const path&);
+
+ //!
+ //! \brief Checks if two paths are equal.
+ //!
+ bool operator==(const path&) const;
+
+ //!
+ //! \brief Checks if two paths are different.
+ //!
+ bool operator!=(const path&) const;
+
+ //!
+ //! \brief Concatenates a path with a string.
+ //!
+ //! Constructs a new path object that is the concatenation of the
+ //! left-hand path with the right-hand string. The string is normalized
+ //! before the concatenation, and a path delimiter is introduced between
+ //! the two components if needed.
+ //!
+ path operator/(const std::string&) const;
+
+ //!
+ //! \brief Concatenates a path with another path.
+ //!
+ //! Constructs a new path object that is the concatenation of the
+ //! left-hand path with the right-hand one. A path delimiter is
+ //! introduced between the two components if needed.
+ //!
+ path operator/(const path&) const;
+
+ //!
+ //! \brief Checks if a path has to be sorted before another one
+ //! lexicographically.
+ //!
+ bool operator<(const path&) const;
+};
+
+// ------------------------------------------------------------------------
+// The "file_info" class.
+// ------------------------------------------------------------------------
+
+class directory;
+
+//!
+//! \brief A class that contains information about a file.
+//!
+//! The file_info class holds information about an specific file that
+//! exists in the file system.
+//!
+class file_info {
+ atf_fs_stat_t m_stat;
+
+public:
+ //!
+ //! \brief The file's type.
+ //!
+ static const int blk_type;
+ static const int chr_type;
+ static const int dir_type;
+ static const int fifo_type;
+ static const int lnk_type;
+ static const int reg_type;
+ static const int sock_type;
+ static const int wht_type;
+
+ //!
+ //! \brief Constructs a new file_info based on a given file.
+ //!
+ //! This constructor creates a new file_info object and fills it with
+ //! the data returned by ::stat when run on the given file, which must
+ //! exist.
+ //!
+ explicit file_info(const path&);
+
+ //!
+ //! \brief The copy constructor.
+ //!
+ file_info(const file_info&);
+
+ //!
+ //! \brief The destructor.
+ //!
+ ~file_info(void);
+
+ //!
+ //! \brief Returns the device containing the file.
+ //!
+ dev_t get_device(void) const;
+
+ //!
+ //! \brief Returns the file's inode.
+ //!
+ ino_t get_inode(void) const;
+
+ //!
+ //! \brief Returns the file's permissions.
+ //!
+ mode_t get_mode(void) const;
+
+ //!
+ //! \brief Returns the file's size.
+ //!
+ off_t get_size(void) const;
+
+ //!
+ //! \brief Returns the file's type.
+ //!
+ int get_type(void) const;
+
+ //!
+ //! \brief Returns whether the file is readable by its owner or not.
+ //!
+ bool is_owner_readable(void) const;
+
+ //!
+ //! \brief Returns whether the file is writable by its owner or not.
+ //!
+ bool is_owner_writable(void) const;
+
+ //!
+ //! \brief Returns whether the file is executable by its owner or not.
+ //!
+ bool is_owner_executable(void) const;
+
+ //!
+ //! \brief Returns whether the file is readable by the users belonging
+ //! to its group or not.
+ //!
+ bool is_group_readable(void) const;
+
+ //!
+ //! \brief Returns whether the file is writable the users belonging to
+ //! its group or not.
+ //!
+ bool is_group_writable(void) const;
+
+ //!
+ //! \brief Returns whether the file is executable by the users
+ //! belonging to its group or not.
+ //!
+ bool is_group_executable(void) const;
+
+ //!
+ //! \brief Returns whether the file is readable by people different
+ //! than the owner and those belonging to the group or not.
+ //!
+ bool is_other_readable(void) const;
+
+ //!
+ //! \brief Returns whether the file is write by people different
+ //! than the owner and those belonging to the group or not.
+ //!
+ bool is_other_writable(void) const;
+
+ //!
+ //! \brief Returns whether the file is executable by people different
+ //! than the owner and those belonging to the group or not.
+ //!
+ bool is_other_executable(void) const;
+};
+
+// ------------------------------------------------------------------------
+// The "directory" class.
+// ------------------------------------------------------------------------
+
+//!
+//! \brief A class representing a file system directory.
+//!
+//! The directory class represents a group of files in the file system and
+//! corresponds to exactly one directory.
+//!
+class directory : public std::map< std::string, file_info > {
+public:
+ //!
+ //! \brief Constructs a new directory.
+ //!
+ //! Constructs a new directory object representing the given path.
+ //! The directory must exist at creation time as the contents of the
+ //! class are gathered from it.
+ //!
+ directory(const path&);
+
+ //!
+ //! \brief Returns the file names of the files in the directory.
+ //!
+ //! Returns the leaf names of all files contained in the directory.
+ //! I.e. the keys of the directory map.
+ //!
+ std::set< std::string > names(void) const;
+};
+
+// ------------------------------------------------------------------------
+// Free functions.
+// ------------------------------------------------------------------------
+
+//!
+//! \brief Checks if the given path exists.
+//!
+bool exists(const path&);
+
+//!
+//! \brief Looks for the given program in the PATH.
+//!
+//! Given a program name (without slashes) looks for it in the path and
+//! returns its full path name if found, otherwise an empty path.
+//!
+bool have_prog_in_path(const std::string&);
+
+//!
+//! \brief Checks if the given path exists, is accessible and is executable.
+//!
+bool is_executable(const path&);
+
+//!
+//! \brief Removes a given file.
+//!
+void remove(const path&);
+
+//!
+//! \brief Removes an empty directory.
+//!
+void rmdir(const path&);
+
+} // namespace fs
+} // namespace atf
+
+#endif // !defined(ATF_CXX_DETAIL_FS_HPP)
diff --git a/unit/atf-src/atf-c++/detail/fs_test.cpp b/unit/atf-src/atf-c++/detail/fs_test.cpp
new file mode 100644
index 0000000..bf64ab2
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/fs_test.cpp
@@ -0,0 +1,542 @@
+// 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.hpp"
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/stat.h>
+}
+
+#include <fstream>
+#include <cerrno>
+#include <cstdio>
+
+#include <atf-c++.hpp>
+
+#include "atf-c++/detail/exceptions.hpp"
+
+// ------------------------------------------------------------------------
+// Auxiliary functions.
+// ------------------------------------------------------------------------
+
+static
+void
+create_files(void)
+{
+ ::mkdir("files", 0755);
+ ::mkdir("files/dir", 0755);
+
+ std::ofstream os("files/reg");
+ os.close();
+
+ // TODO: Should create all other file types (blk, chr, fifo, lnk, sock)
+ // and test for them... but the underlying file system may not support
+ // most of these. Specially as we are working on /tmp, which can be
+ // mounted with flags such as "nodev". See how to deal with this
+ // situation.
+}
+
+// ------------------------------------------------------------------------
+// Test cases for the "path" class.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(path_normalize);
+ATF_TEST_CASE_HEAD(path_normalize)
+{
+ set_md_var("descr", "Tests the path's normalization");
+}
+ATF_TEST_CASE_BODY(path_normalize)
+{
+ using atf::fs::path;
+
+ ATF_REQUIRE_EQ(path(".").str(), ".");
+ ATF_REQUIRE_EQ(path("..").str(), "..");
+
+ ATF_REQUIRE_EQ(path("foo").str(), "foo");
+ ATF_REQUIRE_EQ(path("foo/bar").str(), "foo/bar");
+ ATF_REQUIRE_EQ(path("foo/bar/").str(), "foo/bar");
+
+ ATF_REQUIRE_EQ(path("/foo").str(), "/foo");
+ ATF_REQUIRE_EQ(path("/foo/bar").str(), "/foo/bar");
+ ATF_REQUIRE_EQ(path("/foo/bar/").str(), "/foo/bar");
+
+ ATF_REQUIRE_EQ(path("///foo").str(), "/foo");
+ ATF_REQUIRE_EQ(path("///foo///bar").str(), "/foo/bar");
+ ATF_REQUIRE_EQ(path("///foo///bar///").str(), "/foo/bar");
+}
+
+ATF_TEST_CASE(path_is_absolute);
+ATF_TEST_CASE_HEAD(path_is_absolute)
+{
+ set_md_var("descr", "Tests the path::is_absolute function");
+}
+ATF_TEST_CASE_BODY(path_is_absolute)
+{
+ using atf::fs::path;
+
+ ATF_REQUIRE( path("/").is_absolute());
+ ATF_REQUIRE( path("////").is_absolute());
+ ATF_REQUIRE( path("////a").is_absolute());
+ ATF_REQUIRE( path("//a//").is_absolute());
+ ATF_REQUIRE(!path("a////").is_absolute());
+ ATF_REQUIRE(!path("../foo").is_absolute());
+}
+
+ATF_TEST_CASE(path_is_root);
+ATF_TEST_CASE_HEAD(path_is_root)
+{
+ set_md_var("descr", "Tests the path::is_root function");
+}
+ATF_TEST_CASE_BODY(path_is_root)
+{
+ using atf::fs::path;
+
+ ATF_REQUIRE( path("/").is_root());
+ ATF_REQUIRE( path("////").is_root());
+ ATF_REQUIRE(!path("////a").is_root());
+ ATF_REQUIRE(!path("//a//").is_root());
+ ATF_REQUIRE(!path("a////").is_root());
+ ATF_REQUIRE(!path("../foo").is_root());
+}
+
+ATF_TEST_CASE(path_branch_path);
+ATF_TEST_CASE_HEAD(path_branch_path)
+{
+ set_md_var("descr", "Tests the path::branch_path function");
+}
+ATF_TEST_CASE_BODY(path_branch_path)
+{
+ using atf::fs::path;
+
+ ATF_REQUIRE_EQ(path(".").branch_path().str(), ".");
+ ATF_REQUIRE_EQ(path("foo").branch_path().str(), ".");
+ ATF_REQUIRE_EQ(path("foo/bar").branch_path().str(), "foo");
+ ATF_REQUIRE_EQ(path("/foo").branch_path().str(), "/");
+ ATF_REQUIRE_EQ(path("/foo/bar").branch_path().str(), "/foo");
+}
+
+ATF_TEST_CASE(path_leaf_name);
+ATF_TEST_CASE_HEAD(path_leaf_name)
+{
+ set_md_var("descr", "Tests the path::leaf_name function");
+}
+ATF_TEST_CASE_BODY(path_leaf_name)
+{
+ using atf::fs::path;
+
+ ATF_REQUIRE_EQ(path(".").leaf_name(), ".");
+ ATF_REQUIRE_EQ(path("foo").leaf_name(), "foo");
+ ATF_REQUIRE_EQ(path("foo/bar").leaf_name(), "bar");
+ ATF_REQUIRE_EQ(path("/foo").leaf_name(), "foo");
+ ATF_REQUIRE_EQ(path("/foo/bar").leaf_name(), "bar");
+}
+
+ATF_TEST_CASE(path_compare_equal);
+ATF_TEST_CASE_HEAD(path_compare_equal)
+{
+ set_md_var("descr", "Tests the comparison for equality between paths");
+}
+ATF_TEST_CASE_BODY(path_compare_equal)
+{
+ using atf::fs::path;
+
+ ATF_REQUIRE(path("/") == path("///"));
+ ATF_REQUIRE(path("/a") == path("///a"));
+ ATF_REQUIRE(path("/a") == path("///a///"));
+
+ ATF_REQUIRE(path("a/b/c") == path("a//b//c"));
+ ATF_REQUIRE(path("a/b/c") == path("a//b//c///"));
+}
+
+ATF_TEST_CASE(path_compare_different);
+ATF_TEST_CASE_HEAD(path_compare_different)
+{
+ set_md_var("descr", "Tests the comparison for difference between paths");
+}
+ATF_TEST_CASE_BODY(path_compare_different)
+{
+ using atf::fs::path;
+
+ ATF_REQUIRE(path("/") != path("//a/"));
+ ATF_REQUIRE(path("/a") != path("a///"));
+
+ ATF_REQUIRE(path("a/b/c") != path("a/b"));
+ ATF_REQUIRE(path("a/b/c") != path("a//b"));
+ ATF_REQUIRE(path("a/b/c") != path("/a/b/c"));
+ ATF_REQUIRE(path("a/b/c") != path("/a//b//c"));
+}
+
+ATF_TEST_CASE(path_concat);
+ATF_TEST_CASE_HEAD(path_concat)
+{
+ set_md_var("descr", "Tests the concatenation of multiple paths");
+}
+ATF_TEST_CASE_BODY(path_concat)
+{
+ using atf::fs::path;
+
+ ATF_REQUIRE_EQ((path("foo") / "bar").str(), "foo/bar");
+ ATF_REQUIRE_EQ((path("foo/") / "/bar").str(), "foo/bar");
+ ATF_REQUIRE_EQ((path("foo/") / "/bar/baz").str(), "foo/bar/baz");
+ ATF_REQUIRE_EQ((path("foo/") / "///bar///baz").str(), "foo/bar/baz");
+}
+
+ATF_TEST_CASE(path_to_absolute);
+ATF_TEST_CASE_HEAD(path_to_absolute)
+{
+ set_md_var("descr", "Tests the conversion of a relative path to an "
+ "absolute one");
+}
+ATF_TEST_CASE_BODY(path_to_absolute)
+{
+ using atf::fs::file_info;
+ using atf::fs::path;
+
+ create_files();
+
+ {
+ const path p(".");
+ path pa = p.to_absolute();
+ ATF_REQUIRE(pa.is_absolute());
+
+ file_info fi(p);
+ file_info fia(pa);
+ ATF_REQUIRE_EQ(fi.get_device(), fia.get_device());
+ ATF_REQUIRE_EQ(fi.get_inode(), fia.get_inode());
+ }
+
+ {
+ const path p("files/reg");
+ path pa = p.to_absolute();
+ ATF_REQUIRE(pa.is_absolute());
+
+ file_info fi(p);
+ file_info fia(pa);
+ ATF_REQUIRE_EQ(fi.get_device(), fia.get_device());
+ ATF_REQUIRE_EQ(fi.get_inode(), fia.get_inode());
+ }
+}
+
+ATF_TEST_CASE(path_op_less);
+ATF_TEST_CASE_HEAD(path_op_less)
+{
+ set_md_var("descr", "Tests that the path's less-than operator works");
+}
+ATF_TEST_CASE_BODY(path_op_less)
+{
+ using atf::fs::path;
+
+ create_files();
+
+ ATF_REQUIRE(!(path("aaa") < path("aaa")));
+
+ ATF_REQUIRE( path("aab") < path("abc"));
+ ATF_REQUIRE(!(path("abc") < path("aab")));
+}
+
+// ------------------------------------------------------------------------
+// Test cases for the "directory" class.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(directory_read);
+ATF_TEST_CASE_HEAD(directory_read)
+{
+ set_md_var("descr", "Tests the directory class creation, which reads "
+ "the contents of a directory");
+}
+ATF_TEST_CASE_BODY(directory_read)
+{
+ using atf::fs::directory;
+ using atf::fs::path;
+
+ create_files();
+
+ directory d(path("files"));
+ ATF_REQUIRE_EQ(d.size(), 4);
+ ATF_REQUIRE(d.find(".") != d.end());
+ ATF_REQUIRE(d.find("..") != d.end());
+ ATF_REQUIRE(d.find("dir") != d.end());
+ ATF_REQUIRE(d.find("reg") != d.end());
+}
+
+ATF_TEST_CASE(directory_file_info);
+ATF_TEST_CASE_HEAD(directory_file_info)
+{
+ set_md_var("descr", "Tests that the file_info objects attached to the "
+ "directory are valid");
+}
+ATF_TEST_CASE_BODY(directory_file_info)
+{
+ using atf::fs::directory;
+ using atf::fs::file_info;
+ using atf::fs::path;
+
+ create_files();
+
+ directory d(path("files"));
+
+ {
+ directory::const_iterator iter = d.find("dir");
+ ATF_REQUIRE(iter != d.end());
+ const file_info& fi = (*iter).second;
+ ATF_REQUIRE(fi.get_type() == file_info::dir_type);
+ }
+
+ {
+ directory::const_iterator iter = d.find("reg");
+ ATF_REQUIRE(iter != d.end());
+ const file_info& fi = (*iter).second;
+ ATF_REQUIRE(fi.get_type() == file_info::reg_type);
+ }
+}
+
+ATF_TEST_CASE(directory_names);
+ATF_TEST_CASE_HEAD(directory_names)
+{
+ set_md_var("descr", "Tests the directory's names method");
+}
+ATF_TEST_CASE_BODY(directory_names)
+{
+ using atf::fs::directory;
+ using atf::fs::path;
+
+ create_files();
+
+ directory d(path("files"));
+ std::set< std::string > ns = d.names();
+ ATF_REQUIRE_EQ(ns.size(), 4);
+ ATF_REQUIRE(ns.find(".") != ns.end());
+ ATF_REQUIRE(ns.find("..") != ns.end());
+ ATF_REQUIRE(ns.find("dir") != ns.end());
+ ATF_REQUIRE(ns.find("reg") != ns.end());
+}
+
+// ------------------------------------------------------------------------
+// Test cases for the "file_info" class.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(file_info_stat);
+ATF_TEST_CASE_HEAD(file_info_stat)
+{
+ set_md_var("descr", "Tests the file_info creation and its basic contents");
+}
+ATF_TEST_CASE_BODY(file_info_stat)
+{
+ using atf::fs::file_info;
+ using atf::fs::path;
+
+ create_files();
+
+ {
+ path p("files/dir");
+ file_info fi(p);
+ ATF_REQUIRE(fi.get_type() == file_info::dir_type);
+ }
+
+ {
+ path p("files/reg");
+ file_info fi(p);
+ ATF_REQUIRE(fi.get_type() == file_info::reg_type);
+ }
+}
+
+ATF_TEST_CASE(file_info_perms);
+ATF_TEST_CASE_HEAD(file_info_perms)
+{
+ set_md_var("descr", "Tests the file_info methods to get the file's "
+ "permissions");
+}
+ATF_TEST_CASE_BODY(file_info_perms)
+{
+ using atf::fs::file_info;
+ using atf::fs::path;
+
+ path p("file");
+
+ std::ofstream os(p.c_str());
+ os.close();
+
+#define perms(ur, uw, ux, gr, gw, gx, othr, othw, othx) \
+ { \
+ file_info fi(p); \
+ ATF_REQUIRE(fi.is_owner_readable() == ur); \
+ ATF_REQUIRE(fi.is_owner_writable() == uw); \
+ ATF_REQUIRE(fi.is_owner_executable() == ux); \
+ ATF_REQUIRE(fi.is_group_readable() == gr); \
+ ATF_REQUIRE(fi.is_group_writable() == gw); \
+ ATF_REQUIRE(fi.is_group_executable() == gx); \
+ ATF_REQUIRE(fi.is_other_readable() == othr); \
+ ATF_REQUIRE(fi.is_other_writable() == othw); \
+ ATF_REQUIRE(fi.is_other_executable() == othx); \
+ }
+
+ ::chmod(p.c_str(), 0000);
+ perms(false, false, false, false, false, false, false, false, false);
+
+ ::chmod(p.c_str(), 0001);
+ perms(false, false, false, false, false, false, false, false, true);
+
+ ::chmod(p.c_str(), 0010);
+ perms(false, false, false, false, false, true, false, false, false);
+
+ ::chmod(p.c_str(), 0100);
+ perms(false, false, true, false, false, false, false, false, false);
+
+ ::chmod(p.c_str(), 0002);
+ perms(false, false, false, false, false, false, false, true, false);
+
+ ::chmod(p.c_str(), 0020);
+ perms(false, false, false, false, true, false, false, false, false);
+
+ ::chmod(p.c_str(), 0200);
+ perms(false, true, false, false, false, false, false, false, false);
+
+ ::chmod(p.c_str(), 0004);
+ perms(false, false, false, false, false, false, true, false, false);
+
+ ::chmod(p.c_str(), 0040);
+ perms(false, false, false, true, false, false, false, false, false);
+
+ ::chmod(p.c_str(), 0400);
+ perms(true, false, false, false, false, false, false, false, false);
+
+ ::chmod(p.c_str(), 0644);
+ perms(true, true, false, true, false, false, true, false, false);
+
+ ::chmod(p.c_str(), 0755);
+ perms(true, true, true, true, false, true, true, false, true);
+
+ ::chmod(p.c_str(), 0777);
+ perms(true, true, true, true, true, true, true, true, true);
+
+#undef perms
+}
+
+// ------------------------------------------------------------------------
+// Test cases for the free functions.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(exists);
+ATF_TEST_CASE_HEAD(exists)
+{
+ set_md_var("descr", "Tests the exists function");
+}
+ATF_TEST_CASE_BODY(exists)
+{
+ using atf::fs::exists;
+ using atf::fs::path;
+
+ create_files();
+
+ ATF_REQUIRE( exists(path("files")));
+ ATF_REQUIRE(!exists(path("file")));
+ ATF_REQUIRE(!exists(path("files2")));
+
+ ATF_REQUIRE( exists(path("files/.")));
+ ATF_REQUIRE( exists(path("files/..")));
+ ATF_REQUIRE( exists(path("files/dir")));
+ ATF_REQUIRE( exists(path("files/reg")));
+ ATF_REQUIRE(!exists(path("files/foo")));
+}
+
+ATF_TEST_CASE(is_executable);
+ATF_TEST_CASE_HEAD(is_executable)
+{
+ set_md_var("descr", "Tests the is_executable function");
+}
+ATF_TEST_CASE_BODY(is_executable)
+{
+ using atf::fs::is_executable;
+ using atf::fs::path;
+
+ create_files();
+
+ ATF_REQUIRE( is_executable(path("files")));
+ ATF_REQUIRE( is_executable(path("files/.")));
+ ATF_REQUIRE( is_executable(path("files/..")));
+ ATF_REQUIRE( is_executable(path("files/dir")));
+
+ ATF_REQUIRE(!is_executable(path("non-existent")));
+
+ ATF_REQUIRE(!is_executable(path("files/reg")));
+ ATF_REQUIRE(::chmod("files/reg", 0755) != -1);
+ ATF_REQUIRE( is_executable(path("files/reg")));
+}
+
+ATF_TEST_CASE(remove);
+ATF_TEST_CASE_HEAD(remove)
+{
+ set_md_var("descr", "Tests the remove function");
+}
+ATF_TEST_CASE_BODY(remove)
+{
+ using atf::fs::exists;
+ using atf::fs::path;
+ using atf::fs::remove;
+
+ create_files();
+
+ ATF_REQUIRE( exists(path("files/reg")));
+ remove(path("files/reg"));
+ ATF_REQUIRE(!exists(path("files/reg")));
+
+ ATF_REQUIRE( exists(path("files/dir")));
+ ATF_REQUIRE_THROW(atf::system_error, remove(path("files/dir")));
+ ATF_REQUIRE( exists(path("files/dir")));
+}
+
+// ------------------------------------------------------------------------
+// Main.
+// ------------------------------------------------------------------------
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ // Add the tests for the "path" class.
+ ATF_ADD_TEST_CASE(tcs, path_normalize);
+ ATF_ADD_TEST_CASE(tcs, path_is_absolute);
+ ATF_ADD_TEST_CASE(tcs, path_is_root);
+ ATF_ADD_TEST_CASE(tcs, path_branch_path);
+ ATF_ADD_TEST_CASE(tcs, path_leaf_name);
+ ATF_ADD_TEST_CASE(tcs, path_compare_equal);
+ ATF_ADD_TEST_CASE(tcs, path_compare_different);
+ ATF_ADD_TEST_CASE(tcs, path_concat);
+ ATF_ADD_TEST_CASE(tcs, path_to_absolute);
+ ATF_ADD_TEST_CASE(tcs, path_op_less);
+
+ // Add the tests for the "file_info" class.
+ ATF_ADD_TEST_CASE(tcs, file_info_stat);
+ ATF_ADD_TEST_CASE(tcs, file_info_perms);
+
+ // Add the tests for the "directory" class.
+ ATF_ADD_TEST_CASE(tcs, directory_read);
+ ATF_ADD_TEST_CASE(tcs, directory_names);
+ ATF_ADD_TEST_CASE(tcs, directory_file_info);
+
+ // Add the tests for the free functions.
+ ATF_ADD_TEST_CASE(tcs, exists);
+ ATF_ADD_TEST_CASE(tcs, is_executable);
+ ATF_ADD_TEST_CASE(tcs, remove);
+}
diff --git a/unit/atf-src/atf-c++/detail/process.cpp b/unit/atf-src/atf-c++/detail/process.cpp
new file mode 100644
index 0000000..8139536
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/process.cpp
@@ -0,0 +1,346 @@
+// 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.hpp"
+
+extern "C" {
+#include <signal.h>
+
+#include "atf-c/detail/process.h"
+#include "atf-c/error.h"
+}
+
+#include <iostream>
+
+#include "atf-c++/detail/exceptions.hpp"
+#include "atf-c++/detail/sanity.hpp"
+
+namespace detail = atf::process::detail;
+namespace impl = atf::process;
+#define IMPL_NAME "atf::process"
+
+// ------------------------------------------------------------------------
+// Auxiliary functions.
+// ------------------------------------------------------------------------
+
+template< class C >
+atf::auto_array< const char* >
+collection_to_argv(const C& c)
+{
+ atf::auto_array< const char* > argv(new const char*[c.size() + 1]);
+
+ std::size_t pos = 0;
+ for (typename C::const_iterator iter = c.begin(); iter != c.end();
+ iter++) {
+ argv[pos] = (*iter).c_str();
+ pos++;
+ }
+ INV(pos == c.size());
+ argv[pos] = NULL;
+
+ return argv;
+}
+
+template< class C >
+C
+argv_to_collection(const char* const* argv)
+{
+ C c;
+
+ for (const char* const* iter = argv; *iter != NULL; iter++)
+ c.push_back(std::string(*iter));
+
+ return c;
+}
+
+// ------------------------------------------------------------------------
+// The "argv_array" type.
+// ------------------------------------------------------------------------
+
+impl::argv_array::argv_array(void) :
+ m_exec_argv(collection_to_argv(m_args))
+{
+}
+
+impl::argv_array::argv_array(const char* arg1, ...)
+{
+ m_args.push_back(arg1);
+
+ {
+ va_list ap;
+ const char* nextarg;
+
+ va_start(ap, arg1);
+ while ((nextarg = va_arg(ap, const char*)) != NULL)
+ m_args.push_back(nextarg);
+ va_end(ap);
+ }
+
+ ctor_init_exec_argv();
+}
+
+impl::argv_array::argv_array(const char* const* ca) :
+ m_args(argv_to_collection< args_vector >(ca)),
+ m_exec_argv(collection_to_argv(m_args))
+{
+}
+
+impl::argv_array::argv_array(const argv_array& a) :
+ m_args(a.m_args),
+ m_exec_argv(collection_to_argv(m_args))
+{
+}
+
+void
+impl::argv_array::ctor_init_exec_argv(void)
+{
+ m_exec_argv = collection_to_argv(m_args);
+}
+
+const char* const*
+impl::argv_array::exec_argv(void)
+ const
+{
+ return m_exec_argv.get();
+}
+
+impl::argv_array::size_type
+impl::argv_array::size(void)
+ const
+{
+ return m_args.size();
+}
+
+const char*
+impl::argv_array::operator[](int idx)
+ const
+{
+ return m_args[idx].c_str();
+}
+
+impl::argv_array::const_iterator
+impl::argv_array::begin(void)
+ const
+{
+ return m_args.begin();
+}
+
+impl::argv_array::const_iterator
+impl::argv_array::end(void)
+ const
+{
+ return m_args.end();
+}
+
+impl::argv_array&
+impl::argv_array::operator=(const argv_array& a)
+{
+ if (this != &a) {
+ m_args = a.m_args;
+ m_exec_argv = collection_to_argv(m_args);
+ }
+ return *this;
+}
+
+// ------------------------------------------------------------------------
+// The "stream" types.
+// ------------------------------------------------------------------------
+
+impl::basic_stream::basic_stream(void) :
+ m_inited(false)
+{
+}
+
+impl::basic_stream::~basic_stream(void)
+{
+ if (m_inited)
+ atf_process_stream_fini(&m_sb);
+}
+
+const atf_process_stream_t*
+impl::basic_stream::get_sb(void)
+ const
+{
+ INV(m_inited);
+ return &m_sb;
+}
+
+impl::stream_capture::stream_capture(void)
+{
+ atf_error_t err = atf_process_stream_init_capture(&m_sb);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+ m_inited = true;
+}
+
+impl::stream_connect::stream_connect(const int src_fd, const int tgt_fd)
+{
+ atf_error_t err = atf_process_stream_init_connect(&m_sb, src_fd, tgt_fd);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+ m_inited = true;
+}
+
+impl::stream_inherit::stream_inherit(void)
+{
+ atf_error_t err = atf_process_stream_init_inherit(&m_sb);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+ m_inited = true;
+}
+
+impl::stream_redirect_fd::stream_redirect_fd(const int fd)
+{
+ atf_error_t err = atf_process_stream_init_redirect_fd(&m_sb, fd);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+ m_inited = true;
+}
+
+impl::stream_redirect_path::stream_redirect_path(const fs::path& p)
+{
+ atf_error_t err = atf_process_stream_init_redirect_path(&m_sb, p.c_path());
+ if (atf_is_error(err))
+ throw_atf_error(err);
+ m_inited = true;
+}
+
+// ------------------------------------------------------------------------
+// The "status" type.
+// ------------------------------------------------------------------------
+
+impl::status::status(atf_process_status_t& s) :
+ m_status(s)
+{
+}
+
+impl::status::~status(void)
+{
+ atf_process_status_fini(&m_status);
+}
+
+bool
+impl::status::exited(void)
+ const
+{
+ return atf_process_status_exited(&m_status);
+}
+
+int
+impl::status::exitstatus(void)
+ const
+{
+ return atf_process_status_exitstatus(&m_status);
+}
+
+bool
+impl::status::signaled(void)
+ const
+{
+ return atf_process_status_signaled(&m_status);
+}
+
+int
+impl::status::termsig(void)
+ const
+{
+ return atf_process_status_termsig(&m_status);
+}
+
+bool
+impl::status::coredump(void)
+ const
+{
+ return atf_process_status_coredump(&m_status);
+}
+
+// ------------------------------------------------------------------------
+// The "child" type.
+// ------------------------------------------------------------------------
+
+impl::child::child(atf_process_child_t& c) :
+ m_child(c),
+ m_waited(false)
+{
+}
+
+impl::child::~child(void)
+{
+ if (!m_waited) {
+ ::kill(atf_process_child_pid(&m_child), SIGTERM);
+
+ atf_process_status_t s;
+ atf_error_t err = atf_process_child_wait(&m_child, &s);
+ INV(!atf_is_error(err));
+ atf_process_status_fini(&s);
+ }
+}
+
+impl::status
+impl::child::wait(void)
+{
+ atf_process_status_t s;
+
+ atf_error_t err = atf_process_child_wait(&m_child, &s);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ m_waited = true;
+ return status(s);
+}
+
+pid_t
+impl::child::pid(void)
+ const
+{
+ return atf_process_child_pid(&m_child);
+}
+
+int
+impl::child::stdout_fd(void)
+{
+ return atf_process_child_stdout(&m_child);
+}
+
+int
+impl::child::stderr_fd(void)
+{
+ return atf_process_child_stderr(&m_child);
+}
+
+// ------------------------------------------------------------------------
+// Free functions.
+// ------------------------------------------------------------------------
+
+void
+detail::flush_streams(void)
+{
+ // TODO: This should only be executed when inheriting the stdout or
+ // stderr file descriptors. However, the flushing is specific to the
+ // iostreams, so we cannot do it from the C library where all the process
+ // logic is performed. Come up with a better design.
+ std::cout.flush();
+ std::cerr.flush();
+}
diff --git a/unit/atf-src/atf-c++/detail/process.hpp b/unit/atf-src/atf-c++/detail/process.hpp
new file mode 100644
index 0000000..0d4989a
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/process.hpp
@@ -0,0 +1,274 @@
+// 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_CXX_DETAIL_PROCESS_HPP)
+#define ATF_CXX_DETAIL_PROCESS_HPP
+
+extern "C" {
+#include <sys/types.h>
+
+#include <atf-c/detail/process.h>
+#include <atf-c/error.h>
+}
+
+#include <string>
+#include <vector>
+
+#include <atf-c++/detail/auto_array.hpp>
+#include <atf-c++/detail/exceptions.hpp>
+#include <atf-c++/detail/fs.hpp>
+
+namespace atf {
+namespace process {
+
+class child;
+class status;
+
+// ------------------------------------------------------------------------
+// The "argv_array" type.
+// ------------------------------------------------------------------------
+
+class argv_array {
+ typedef std::vector< std::string > args_vector;
+ args_vector m_args;
+
+ // TODO: This is immutable, so we should be able to use
+ // std::tr1::shared_array instead when it becomes widely available.
+ // The reason would be to remove all copy constructors and assignment
+ // operators from this class.
+ auto_array< const char* > m_exec_argv;
+ void ctor_init_exec_argv(void);
+
+public:
+ typedef args_vector::const_iterator const_iterator;
+ typedef args_vector::size_type size_type;
+
+ argv_array(void);
+ argv_array(const char*, ...);
+ explicit argv_array(const char* const*);
+ template< class C > explicit argv_array(const C&);
+ argv_array(const argv_array&);
+
+ const char* const* exec_argv(void) const;
+ size_type size(void) const;
+ const char* operator[](int) const;
+
+ const_iterator begin(void) const;
+ const_iterator end(void) const;
+
+ argv_array& operator=(const argv_array&);
+};
+
+template< class C >
+argv_array::argv_array(const C& c)
+{
+ for (typename C::const_iterator iter = c.begin(); iter != c.end();
+ iter++)
+ m_args.push_back(*iter);
+ ctor_init_exec_argv();
+}
+
+// ------------------------------------------------------------------------
+// The "stream" types.
+// ------------------------------------------------------------------------
+
+class basic_stream {
+protected:
+ atf_process_stream_t m_sb;
+ bool m_inited;
+
+ const atf_process_stream_t* get_sb(void) const;
+
+public:
+ basic_stream(void);
+ ~basic_stream(void);
+};
+
+class stream_capture : basic_stream {
+ // Allow access to the getters.
+ template< class OutStream, class ErrStream > friend
+ child fork(void (*)(void*), const OutStream&, const ErrStream&, void*);
+ template< class OutStream, class ErrStream > friend
+ status exec(const atf::fs::path&, const argv_array&,
+ const OutStream&, const ErrStream&, void (*)(void));
+
+public:
+ stream_capture(void);
+};
+
+class stream_connect : basic_stream {
+ // Allow access to the getters.
+ template< class OutStream, class ErrStream > friend
+ child fork(void (*)(void*), const OutStream&, const ErrStream&, void*);
+ template< class OutStream, class ErrStream > friend
+ status exec(const atf::fs::path&, const argv_array&,
+ const OutStream&, const ErrStream&, void (*)(void));
+
+public:
+ stream_connect(const int, const int);
+};
+
+class stream_inherit : basic_stream {
+ // Allow access to the getters.
+ template< class OutStream, class ErrStream > friend
+ child fork(void (*)(void*), const OutStream&, const ErrStream&, void*);
+ template< class OutStream, class ErrStream > friend
+ status exec(const atf::fs::path&, const argv_array&,
+ const OutStream&, const ErrStream&, void (*)(void));
+
+public:
+ stream_inherit(void);
+};
+
+class stream_redirect_fd : basic_stream {
+ // Allow access to the getters.
+ template< class OutStream, class ErrStream > friend
+ child fork(void (*)(void*), const OutStream&, const ErrStream&, void*);
+ template< class OutStream, class ErrStream > friend
+ status exec(const atf::fs::path&, const argv_array&,
+ const OutStream&, const ErrStream&, void (*)(void));
+
+public:
+ stream_redirect_fd(const int);
+};
+
+class stream_redirect_path : basic_stream {
+ // Allow access to the getters.
+ template< class OutStream, class ErrStream > friend
+ child fork(void (*)(void*), const OutStream&, const ErrStream&, void*);
+ template< class OutStream, class ErrStream > friend
+ status exec(const atf::fs::path&, const argv_array&,
+ const OutStream&, const ErrStream&, void (*)(void));
+
+public:
+ stream_redirect_path(const fs::path&);
+};
+
+// ------------------------------------------------------------------------
+// The "status" type.
+// ------------------------------------------------------------------------
+
+class status {
+ atf_process_status_t m_status;
+
+ friend class child;
+ template< class OutStream, class ErrStream > friend
+ status exec(const atf::fs::path&, const argv_array&,
+ const OutStream&, const ErrStream&, void (*)(void));
+
+ status(atf_process_status_t&);
+
+public:
+ ~status(void);
+
+ bool exited(void) const;
+ int exitstatus(void) const;
+
+ bool signaled(void) const;
+ int termsig(void) const;
+ bool coredump(void) const;
+};
+
+// ------------------------------------------------------------------------
+// The "child" type.
+// ------------------------------------------------------------------------
+
+class child {
+ atf_process_child_t m_child;
+ bool m_waited;
+
+ template< class OutStream, class ErrStream > friend
+ child fork(void (*)(void*), const OutStream&, const ErrStream&, void*);
+
+ child(atf_process_child_t& c);
+
+public:
+ ~child(void);
+
+ status wait(void);
+
+ pid_t pid(void) const;
+ int stdout_fd(void);
+ int stderr_fd(void);
+};
+
+// ------------------------------------------------------------------------
+// Free functions.
+// ------------------------------------------------------------------------
+
+namespace detail {
+void flush_streams(void);
+} // namespace detail
+
+// TODO: The void* cookie can probably be templatized, thus also allowing
+// const data structures.
+template< class OutStream, class ErrStream >
+child
+fork(void (*start)(void*), const OutStream& outsb,
+ const ErrStream& errsb, void* v)
+{
+ atf_process_child_t c;
+
+ detail::flush_streams();
+ atf_error_t err = atf_process_fork(&c, start, outsb.get_sb(),
+ errsb.get_sb(), v);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ return child(c);
+}
+
+template< class OutStream, class ErrStream >
+status
+exec(const atf::fs::path& prog, const argv_array& argv,
+ const OutStream& outsb, const ErrStream& errsb,
+ void (*prehook)(void))
+{
+ atf_process_status_t s;
+
+ detail::flush_streams();
+ atf_error_t err = atf_process_exec_array(&s, prog.c_path(),
+ argv.exec_argv(),
+ outsb.get_sb(),
+ errsb.get_sb(),
+ prehook);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ return status(s);
+}
+
+template< class OutStream, class ErrStream >
+status
+exec(const atf::fs::path& prog, const argv_array& argv,
+ const OutStream& outsb, const ErrStream& errsb)
+{
+ return exec(prog, argv, outsb, errsb, NULL);
+}
+
+} // namespace process
+} // namespace atf
+
+#endif // !defined(ATF_CXX_DETAIL_PROCESS_HPP)
diff --git a/unit/atf-src/atf-c++/detail/process_test.cpp b/unit/atf-src/atf-c++/detail/process_test.cpp
new file mode 100644
index 0000000..0686d2a
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/process_test.cpp
@@ -0,0 +1,354 @@
+// 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.hpp"
+
+#include <cstdlib>
+#include <cstring>
+
+#include <atf-c++.hpp>
+
+#include "atf-c++/detail/test_helpers.hpp"
+
+// TODO: Testing the fork function is a huge task and I'm afraid of
+// copy/pasting tons of stuff from the C version. I'd rather not do that
+// until some code can be shared, which cannot happen until the C++ binding
+// is cleaned by a fair amount. Instead... just rely (at the moment) on
+// the system tests for the tools using this module.
+
+// ------------------------------------------------------------------------
+// Auxiliary functions.
+// ------------------------------------------------------------------------
+
+static
+std::size_t
+array_size(const char* const* array)
+{
+ std::size_t size = 0;
+
+ for (const char* const* ptr = array; *ptr != NULL; ptr++)
+ size++;
+
+ return size;
+}
+
+static
+atf::process::status
+exec_process_helpers(const atf::tests::tc& tc, const char* helper_name)
+{
+ using atf::process::exec;
+
+ std::vector< std::string > argv;
+ argv.push_back(get_process_helpers_path(tc, true).leaf_name());
+ argv.push_back(helper_name);
+
+ return exec(get_process_helpers_path(tc, true),
+ atf::process::argv_array(argv),
+ atf::process::stream_inherit(),
+ atf::process::stream_inherit());
+}
+
+// ------------------------------------------------------------------------
+// Tests for the "argv_array" type.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(argv_array_init_carray);
+ATF_TEST_CASE_HEAD(argv_array_init_carray)
+{
+ set_md_var("descr", "Tests that argv_array is correctly constructed "
+ "from a C-style array of strings");
+}
+ATF_TEST_CASE_BODY(argv_array_init_carray)
+{
+ {
+ const char* const carray[] = { NULL };
+ atf::process::argv_array argv(carray);
+
+ ATF_REQUIRE_EQ(argv.size(), 0);
+ }
+
+ {
+ const char* const carray[] = { "arg0", NULL };
+ atf::process::argv_array argv(carray);
+
+ ATF_REQUIRE_EQ(argv.size(), 1);
+ ATF_REQUIRE(std::strcmp(argv[0], carray[0]) == 0);
+ }
+
+ {
+ const char* const carray[] = { "arg0", "arg1", "arg2", NULL };
+ atf::process::argv_array argv(carray);
+
+ ATF_REQUIRE_EQ(argv.size(), 3);
+ ATF_REQUIRE(std::strcmp(argv[0], carray[0]) == 0);
+ ATF_REQUIRE(std::strcmp(argv[1], carray[1]) == 0);
+ ATF_REQUIRE(std::strcmp(argv[2], carray[2]) == 0);
+ }
+}
+
+ATF_TEST_CASE(argv_array_init_col);
+ATF_TEST_CASE_HEAD(argv_array_init_col)
+{
+ set_md_var("descr", "Tests that argv_array is correctly constructed "
+ "from a string collection");
+}
+ATF_TEST_CASE_BODY(argv_array_init_col)
+{
+ {
+ std::vector< std::string > col;
+ atf::process::argv_array argv(col);
+
+ ATF_REQUIRE_EQ(argv.size(), 0);
+ }
+
+ {
+ std::vector< std::string > col;
+ col.push_back("arg0");
+ atf::process::argv_array argv(col);
+
+ ATF_REQUIRE_EQ(argv.size(), 1);
+ ATF_REQUIRE_EQ(argv[0], col[0]);
+ }
+
+ {
+ std::vector< std::string > col;
+ col.push_back("arg0");
+ col.push_back("arg1");
+ col.push_back("arg2");
+ atf::process::argv_array argv(col);
+
+ ATF_REQUIRE_EQ(argv.size(), 3);
+ ATF_REQUIRE_EQ(argv[0], col[0]);
+ ATF_REQUIRE_EQ(argv[1], col[1]);
+ ATF_REQUIRE_EQ(argv[2], col[2]);
+ }
+}
+
+ATF_TEST_CASE(argv_array_init_empty);
+ATF_TEST_CASE_HEAD(argv_array_init_empty)
+{
+ set_md_var("descr", "Tests that argv_array is correctly constructed "
+ "by the default constructor");
+}
+ATF_TEST_CASE_BODY(argv_array_init_empty)
+{
+ atf::process::argv_array argv;
+
+ ATF_REQUIRE_EQ(argv.size(), 0);
+}
+
+ATF_TEST_CASE(argv_array_init_varargs);
+ATF_TEST_CASE_HEAD(argv_array_init_varargs)
+{
+ set_md_var("descr", "Tests that argv_array is correctly constructed "
+ "from a variable list of arguments");
+}
+ATF_TEST_CASE_BODY(argv_array_init_varargs)
+{
+ {
+ atf::process::argv_array argv("arg0", NULL);
+
+ ATF_REQUIRE_EQ(argv.size(), 1);
+ ATF_REQUIRE_EQ(argv[0], std::string("arg0"));
+ }
+
+ {
+ atf::process::argv_array argv("arg0", "arg1", "arg2", NULL);
+
+ ATF_REQUIRE_EQ(argv.size(), 3);
+ ATF_REQUIRE_EQ(argv[0], std::string("arg0"));
+ ATF_REQUIRE_EQ(argv[1], std::string("arg1"));
+ ATF_REQUIRE_EQ(argv[2], std::string("arg2"));
+ }
+}
+
+ATF_TEST_CASE(argv_array_assign);
+ATF_TEST_CASE_HEAD(argv_array_assign)
+{
+ set_md_var("descr", "Tests that assigning an argv_array works");
+}
+ATF_TEST_CASE_BODY(argv_array_assign)
+{
+ using atf::process::argv_array;
+
+ const char* const carray1[] = { "arg1", NULL };
+ const char* const carray2[] = { "arg1", "arg2", NULL };
+
+ std::auto_ptr< argv_array > argv1(new argv_array(carray1));
+ std::auto_ptr< argv_array > argv2(new argv_array(carray2));
+
+ *argv2 = *argv1;
+ ATF_REQUIRE_EQ(argv2->size(), argv1->size());
+ ATF_REQUIRE(std::strcmp((*argv2)[0], (*argv1)[0]) == 0);
+
+ ATF_REQUIRE(argv2->exec_argv() != argv1->exec_argv());
+ argv1.release();
+ {
+ const char* const* eargv2 = argv2->exec_argv();
+ ATF_REQUIRE(std::strcmp(eargv2[0], carray1[0]) == 0);
+ ATF_REQUIRE_EQ(eargv2[1], static_cast< const char* >(NULL));
+ }
+
+ argv2.release();
+}
+
+ATF_TEST_CASE(argv_array_copy);
+ATF_TEST_CASE_HEAD(argv_array_copy)
+{
+ set_md_var("descr", "Tests that copying an argv_array constructed from "
+ "a C-style array of strings works");
+}
+ATF_TEST_CASE_BODY(argv_array_copy)
+{
+ using atf::process::argv_array;
+
+ const char* const carray[] = { "arg0", NULL };
+
+ std::auto_ptr< argv_array > argv1(new argv_array(carray));
+ std::auto_ptr< argv_array > argv2(new argv_array(*argv1));
+
+ ATF_REQUIRE_EQ(argv2->size(), argv1->size());
+ ATF_REQUIRE(std::strcmp((*argv2)[0], (*argv1)[0]) == 0);
+
+ ATF_REQUIRE(argv2->exec_argv() != argv1->exec_argv());
+ argv1.release();
+ {
+ const char* const* eargv2 = argv2->exec_argv();
+ ATF_REQUIRE(std::strcmp(eargv2[0], carray[0]) == 0);
+ ATF_REQUIRE_EQ(eargv2[1], static_cast< const char* >(NULL));
+ }
+
+ argv2.release();
+}
+
+ATF_TEST_CASE(argv_array_exec_argv);
+ATF_TEST_CASE_HEAD(argv_array_exec_argv)
+{
+ set_md_var("descr", "Tests that the exec argv provided by an argv_array "
+ "is correct");
+}
+ATF_TEST_CASE_BODY(argv_array_exec_argv)
+{
+ using atf::process::argv_array;
+
+ {
+ argv_array argv;
+ const char* const* eargv = argv.exec_argv();
+ ATF_REQUIRE_EQ(array_size(eargv), 0);
+ ATF_REQUIRE_EQ(eargv[0], static_cast< const char* >(NULL));
+ }
+
+ {
+ const char* const carray[] = { "arg0", NULL };
+ argv_array argv(carray);
+ const char* const* eargv = argv.exec_argv();
+ ATF_REQUIRE_EQ(array_size(eargv), 1);
+ ATF_REQUIRE(std::strcmp(eargv[0], "arg0") == 0);
+ ATF_REQUIRE_EQ(eargv[1], static_cast< const char* >(NULL));
+ }
+
+ {
+ std::vector< std::string > col;
+ col.push_back("arg0");
+ argv_array argv(col);
+ const char* const* eargv = argv.exec_argv();
+ ATF_REQUIRE_EQ(array_size(eargv), 1);
+ ATF_REQUIRE(std::strcmp(eargv[0], "arg0") == 0);
+ ATF_REQUIRE_EQ(eargv[1], static_cast< const char* >(NULL));
+ }
+}
+
+ATF_TEST_CASE(argv_array_iter);
+ATF_TEST_CASE_HEAD(argv_array_iter)
+{
+ set_md_var("descr", "Tests that an argv_array can be iterated");
+}
+ATF_TEST_CASE_BODY(argv_array_iter)
+{
+ using atf::process::argv_array;
+
+ std::vector< std::string > vector;
+ vector.push_back("arg0");
+ vector.push_back("arg1");
+ vector.push_back("arg2");
+
+ argv_array argv(vector);
+ ATF_REQUIRE_EQ(argv.size(), 3);
+ std::vector< std::string >::size_type pos = 0;
+ for (argv_array::const_iterator iter = argv.begin(); iter != argv.end();
+ iter++) {
+ ATF_REQUIRE_EQ(*iter, vector[pos]);
+ pos++;
+ }
+}
+
+// ------------------------------------------------------------------------
+// Tests cases for the free functions.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(exec_failure);
+ATF_TEST_CASE_HEAD(exec_failure)
+{
+ set_md_var("descr", "Tests execing a command that reports failure");
+}
+ATF_TEST_CASE_BODY(exec_failure)
+{
+ const atf::process::status s = exec_process_helpers(*this, "exit-failure");
+ ATF_REQUIRE(s.exited());
+ ATF_REQUIRE_EQ(s.exitstatus(), EXIT_FAILURE);
+}
+
+ATF_TEST_CASE(exec_success);
+ATF_TEST_CASE_HEAD(exec_success)
+{
+ set_md_var("descr", "Tests execing a command that reports success");
+}
+ATF_TEST_CASE_BODY(exec_success)
+{
+ const atf::process::status s = exec_process_helpers(*this, "exit-success");
+ ATF_REQUIRE(s.exited());
+ ATF_REQUIRE_EQ(s.exitstatus(), EXIT_SUCCESS);
+}
+
+// ------------------------------------------------------------------------
+// Main.
+// ------------------------------------------------------------------------
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ // Add the test cases for the "argv_array" type.
+ ATF_ADD_TEST_CASE(tcs, argv_array_assign);
+ ATF_ADD_TEST_CASE(tcs, argv_array_copy);
+ ATF_ADD_TEST_CASE(tcs, argv_array_exec_argv);
+ ATF_ADD_TEST_CASE(tcs, argv_array_init_carray);
+ ATF_ADD_TEST_CASE(tcs, argv_array_init_col);
+ ATF_ADD_TEST_CASE(tcs, argv_array_init_empty);
+ ATF_ADD_TEST_CASE(tcs, argv_array_init_varargs);
+ ATF_ADD_TEST_CASE(tcs, argv_array_iter);
+
+ // Add the test cases for the free functions.
+ ATF_ADD_TEST_CASE(tcs, exec_failure);
+ ATF_ADD_TEST_CASE(tcs, exec_success);
+}
diff --git a/unit/atf-src/atf-c++/detail/sanity.hpp b/unit/atf-src/atf-c++/detail/sanity.hpp
new file mode 100644
index 0000000..3d89ab4
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/sanity.hpp
@@ -0,0 +1,33 @@
+// 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_CXX_DETAIL_SANITY_HPP)
+#define ATF_CXX_DETAIL_SANITY_HPP
+
+extern "C" {
+#include <atf-c/detail/sanity.h>
+}
+
+#endif // !defined(ATF_CXX_DETAIL_SANITY_HPP)
diff --git a/unit/atf-src/atf-c++/detail/test_helpers.cpp b/unit/atf-src/atf-c++/detail/test_helpers.cpp
new file mode 100644
index 0000000..d3f9400
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/test_helpers.cpp
@@ -0,0 +1,100 @@
+// 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++/detail/test_helpers.hpp"
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <atf-c++.hpp>
+
+#include "atf-c++/check.hpp"
+#include "atf-c++/detail/env.hpp"
+#include "atf-c++/detail/fs.hpp"
+#include "atf-c++/detail/process.hpp"
+
+// Path to the directory containing the libatf-c tests, used to locate the
+// process_helpers program. If NULL (the default), the code will use a
+// relative path. Otherwise, the provided path will be used; this is so
+// that we can locate the helpers binary if the installation uses a
+// different layout than the one we provide (as is the case in FreeBSD).
+#if defined(ATF_C_TESTS_BASE)
+static const char* atf_c_tests_base = ATF_C_TESTS_BASE;
+#else
+static const char* atf_c_tests_base = NULL;
+#endif
+#undef ATF_C_TESTS_BASE
+
+bool
+build_check_cxx_o(const char* sfile)
+{
+ std::vector< std::string > optargs;
+ optargs.push_back("-I" + atf::env::get("ATF_INCLUDEDIR", ATF_INCLUDEDIR));
+ optargs.push_back("-Wall");
+ optargs.push_back("-Werror");
+
+ return atf::check::build_cxx_o(sfile, "test.o",
+ atf::process::argv_array(optargs));
+}
+
+bool
+build_check_cxx_o_srcdir(const atf::tests::tc& tc, const char* sfile)
+{
+ const atf::fs::path sfilepath =
+ atf::fs::path(tc.get_config_var("srcdir")) / sfile;
+ return build_check_cxx_o(sfilepath.c_str());
+}
+
+void
+header_check(const char *hdrname)
+{
+ std::ofstream srcfile("test.cpp");
+ ATF_REQUIRE(srcfile);
+ srcfile << "#include <" << hdrname << ">\n";
+ srcfile.close();
+
+ const std::string failmsg = std::string("Header check failed; ") +
+ hdrname + " is not self-contained";
+ if (!build_check_cxx_o("test.cpp"))
+ ATF_FAIL(failmsg);
+}
+
+atf::fs::path
+get_process_helpers_path(const atf::tests::tc& tc, bool is_detail)
+{
+ const char* helper = "detail/process_helpers";
+ if (atf_c_tests_base == NULL) {
+ if (is_detail)
+ return atf::fs::path(tc.get_config_var("srcdir")) /
+ ".." / ".." / "atf-c" / helper;
+ else
+ return atf::fs::path(tc.get_config_var("srcdir")) /
+ ".." / "atf-c" / helper;
+ } else {
+ return atf::fs::path(atf_c_tests_base) / helper;
+ }
+}
diff --git a/unit/atf-src/atf-c++/detail/test_helpers.hpp b/unit/atf-src/atf-c++/detail/test_helpers.hpp
new file mode 100644
index 0000000..f166ee2
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/test_helpers.hpp
@@ -0,0 +1,107 @@
+// 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_CXX_DETAIL_TEST_HELPERS_H)
+# error "Cannot include test_helpers.hpp more than once."
+#else
+# define ATF_CXX_DETAIL_TEST_HELPERS_H
+#endif
+
+#include <cstdlib>
+#include <iostream>
+#include <sstream>
+#include <utility>
+
+#include <atf-c++.hpp>
+
+#include <atf-c++/detail/process.hpp>
+
+#define HEADER_TC(name, hdrname) \
+ ATF_TEST_CASE(name); \
+ ATF_TEST_CASE_HEAD(name) \
+ { \
+ set_md_var("descr", "Tests that the " hdrname " file can be " \
+ "included on its own, without any prerequisites"); \
+ } \
+ ATF_TEST_CASE_BODY(name) \
+ { \
+ header_check(hdrname); \
+ }
+
+#define BUILD_TC(name, sfile, descr, failmsg) \
+ ATF_TEST_CASE(name); \
+ ATF_TEST_CASE_HEAD(name) \
+ { \
+ set_md_var("descr", descr); \
+ } \
+ ATF_TEST_CASE_BODY(name) \
+ { \
+ if (!build_check_cxx_o_srcdir(*this, sfile)) \
+ ATF_FAIL(failmsg); \
+ }
+
+namespace atf {
+namespace tests {
+class tc;
+}
+}
+
+void header_check(const char*);
+bool build_check_cxx_o(const char*);
+bool build_check_cxx_o_srcdir(const atf::tests::tc&, const char*);
+atf::fs::path get_process_helpers_path(const atf::tests::tc&, bool);
+
+struct run_h_tc_data {
+ const atf::tests::vars_map& m_config;
+
+ run_h_tc_data(const atf::tests::vars_map& config) :
+ m_config(config) {}
+};
+
+template< class TestCase >
+void
+run_h_tc_child(void* v)
+{
+ run_h_tc_data* data = static_cast< run_h_tc_data* >(v);
+
+ TestCase tc;
+ tc.init(data->m_config);
+ tc.run("result");
+ std::exit(EXIT_SUCCESS);
+}
+
+template< class TestCase >
+void
+run_h_tc(atf::tests::vars_map config = atf::tests::vars_map())
+{
+ run_h_tc_data data(config);
+ atf::process::child c = atf::process::fork(
+ run_h_tc_child< TestCase >,
+ atf::process::stream_redirect_path(atf::fs::path("stdout")),
+ atf::process::stream_redirect_path(atf::fs::path("stderr")),
+ &data);
+ const atf::process::status s = c.wait();
+ ATF_REQUIRE(s.exited());
+}
diff --git a/unit/atf-src/atf-c++/detail/text.cpp b/unit/atf-src/atf-c++/detail/text.cpp
new file mode 100644
index 0000000..35a0bec
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/text.cpp
@@ -0,0 +1,156 @@
+// 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/text.hpp"
+
+extern "C" {
+#include <regex.h>
+}
+
+#include <cctype>
+#include <cstring>
+
+extern "C" {
+#include "atf-c/detail/text.h"
+#include "atf-c/error.h"
+}
+
+#include "atf-c++/detail/exceptions.hpp"
+
+namespace impl = atf::text;
+#define IMPL_NAME "atf::text"
+
+char*
+impl::duplicate(const char* str)
+{
+ char* copy = new char[std::strlen(str) + 1];
+ std::strcpy(copy, str);
+ return copy;
+}
+
+bool
+impl::match(const std::string& str, const std::string& regex)
+{
+ bool found;
+
+ // Special case: regcomp does not like empty regular expressions.
+ if (regex.empty()) {
+ found = str.empty();
+ } else {
+ ::regex_t preg;
+
+ if (::regcomp(&preg, regex.c_str(), REG_EXTENDED) != 0)
+ throw std::runtime_error("Invalid regular expression '" + regex +
+ "'");
+
+ const int res = ::regexec(&preg, str.c_str(), 0, NULL, 0);
+ regfree(&preg);
+ if (res != 0 && res != REG_NOMATCH)
+ throw std::runtime_error("Invalid regular expression " + regex);
+
+ found = res == 0;
+ }
+
+ return found;
+}
+
+std::string
+impl::to_lower(const std::string& str)
+{
+ std::string lc;
+ for (std::string::const_iterator iter = str.begin(); iter != str.end();
+ iter++)
+ lc += std::tolower(*iter);
+ return lc;
+}
+
+std::vector< std::string >
+impl::split(const std::string& str, const std::string& delim)
+{
+ std::vector< std::string > words;
+
+ std::string::size_type pos = 0, newpos = 0;
+ while (pos < str.length() && newpos != std::string::npos) {
+ newpos = str.find(delim, pos);
+ if (newpos != pos)
+ words.push_back(str.substr(pos, newpos - pos));
+ pos = newpos + delim.length();
+ }
+
+ return words;
+}
+
+std::string
+impl::trim(const std::string& str)
+{
+ std::string::size_type pos1 = str.find_first_not_of(" \t");
+ std::string::size_type pos2 = str.find_last_not_of(" \t");
+
+ if (pos1 == std::string::npos && pos2 == std::string::npos)
+ return "";
+ else if (pos1 == std::string::npos)
+ return str.substr(0, str.length() - pos2);
+ else if (pos2 == std::string::npos)
+ return str.substr(pos1);
+ else
+ return str.substr(pos1, pos2 - pos1 + 1);
+}
+
+bool
+impl::to_bool(const std::string& str)
+{
+ bool b;
+
+ atf_error_t err = atf_text_to_bool(str.c_str(), &b);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+
+ return b;
+}
+
+int64_t
+impl::to_bytes(std::string str)
+{
+ if (str.empty())
+ throw std::runtime_error("Empty value");
+
+ const char unit = str[str.length() - 1];
+ int64_t multiplier;
+ switch (unit) {
+ case 'k': case 'K': multiplier = 1 << 10; break;
+ case 'm': case 'M': multiplier = 1 << 20; break;
+ case 'g': case 'G': multiplier = 1 << 30; break;
+ case 't': case 'T': multiplier = int64_t(1) << 40; break;
+ default:
+ if (!std::isdigit(unit))
+ throw std::runtime_error(std::string("Unknown size unit '") + unit
+ + "'");
+ multiplier = 1;
+ }
+ if (multiplier != 1)
+ str.erase(str.length() - 1);
+
+ return to_type< int64_t >(str) * multiplier;
+}
diff --git a/unit/atf-src/atf-c++/detail/text.hpp b/unit/atf-src/atf-c++/detail/text.hpp
new file mode 100644
index 0000000..63c3190
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/text.hpp
@@ -0,0 +1,149 @@
+// 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_CXX_DETAIL_TEXT_HPP)
+#define ATF_CXX_DETAIL_TEXT_HPP
+
+extern "C" {
+#include <stdint.h>
+}
+
+#include <sstream>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace atf {
+namespace text {
+
+//!
+//! \brief Duplicates a C string using the new[] allocator.
+//!
+//! Replaces the functionality of strdup by using the new[] allocator and
+//! thus allowing the resulting memory to be managed by utils::auto_array.
+//!
+char* duplicate(const char*);
+
+//!
+//! \brief Joins multiple words into a string.
+//!
+//! Joins a list of words into a string, separating them using the provided
+//! separator. Empty words are not omitted.
+//!
+template< class T >
+std::string
+join(const T& words, const std::string& separator)
+{
+ std::string str;
+
+ typename T::const_iterator iter = words.begin();
+ bool done = iter == words.end();
+ while (!done) {
+ str += *iter;
+ iter++;
+ if (iter != words.end())
+ str += separator;
+ else
+ done = true;
+ }
+
+ return str;
+}
+
+//!
+//! \brief Checks if the string matches a regular expression.
+//!
+bool match(const std::string&, const std::string&);
+
+//!
+//! \brief Splits a string into words.
+//!
+//! Splits the given string into multiple words, all separated by the
+//! given delimiter. Multiple occurrences of the same delimiter are
+//! not condensed so that rejoining the words later on using the same
+//! delimiter results in the original string.
+//!
+std::vector< std::string > split(const std::string&, const std::string&);
+
+//!
+//! \brief Removes whitespace from the beginning and end of a string.
+//!
+std::string trim(const std::string&);
+
+//!
+//! \brief Converts a string to a boolean value.
+//!
+bool to_bool(const std::string&);
+
+//!
+//! \brief Converts the given string to a bytes size.
+//!
+int64_t to_bytes(std::string);
+
+//!
+//! \brief Changes the case of a string to lowercase.
+//!
+//! Returns a new string that is a lowercased version of the original
+//! one.
+//!
+std::string to_lower(const std::string&);
+
+//!
+//! \brief Converts the given object to a string.
+//!
+//! Returns a string with the representation of the given object. There
+//! must exist an operator<< method for that object.
+//!
+template< class T >
+std::string
+to_string(const T& ob)
+{
+ std::ostringstream ss;
+ ss << ob;
+ return ss.str();
+}
+
+//!
+//! \brief Converts the given string to another type.
+//!
+//! Attempts to convert the given string to the requested type. Throws
+//! an exception if the conversion failed.
+//!
+template< class T >
+T
+to_type(const std::string& str)
+{
+ std::istringstream ss(str);
+ T value;
+ ss >> value;
+ if (!ss.eof() || (ss.eof() && (ss.fail() || ss.bad())))
+ throw std::runtime_error("Cannot convert string to requested type");
+ return value;
+}
+
+} // namespace text
+} // namespace atf
+
+#endif // !defined(ATF_CXX_DETAIL_TEXT_HPP)
diff --git a/unit/atf-src/atf-c++/detail/text_test.cpp b/unit/atf-src/atf-c++/detail/text_test.cpp
new file mode 100644
index 0000000..49d3774
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/text_test.cpp
@@ -0,0 +1,386 @@
+// 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/text.hpp"
+
+#include <cstring>
+#include <set>
+#include <vector>
+
+#include <atf-c++.hpp>
+
+// ------------------------------------------------------------------------
+// Test cases for the free functions.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(duplicate);
+ATF_TEST_CASE_HEAD(duplicate)
+{
+ set_md_var("descr", "Tests the duplicate function");
+}
+ATF_TEST_CASE_BODY(duplicate)
+{
+ using atf::text::duplicate;
+
+ const char* orig = "foo";
+
+ char* copy = duplicate(orig);
+ ATF_REQUIRE_EQ(std::strlen(copy), 3);
+ ATF_REQUIRE(std::strcmp(copy, "foo") == 0);
+
+ std::strcpy(copy, "bar");
+ ATF_REQUIRE(std::strcmp(copy, "bar") == 0);
+ ATF_REQUIRE(std::strcmp(orig, "foo") == 0);
+}
+
+ATF_TEST_CASE(join);
+ATF_TEST_CASE_HEAD(join)
+{
+ set_md_var("descr", "Tests the join function");
+}
+ATF_TEST_CASE_BODY(join)
+{
+ using atf::text::join;
+
+ // First set of tests using a non-sorted collection, std::vector.
+ {
+ std::vector< std::string > words;
+ std::string str;
+
+ words.clear();
+ str = join(words, ",");
+ ATF_REQUIRE_EQ(str, "");
+
+ words.clear();
+ words.push_back("");
+ str = join(words, ",");
+ ATF_REQUIRE_EQ(str, "");
+
+ words.clear();
+ words.push_back("");
+ words.push_back("");
+ str = join(words, ",");
+ ATF_REQUIRE_EQ(str, ",");
+
+ words.clear();
+ words.push_back("foo");
+ words.push_back("");
+ words.push_back("baz");
+ str = join(words, ",");
+ ATF_REQUIRE_EQ(str, "foo,,baz");
+
+ words.clear();
+ words.push_back("foo");
+ words.push_back("bar");
+ words.push_back("baz");
+ str = join(words, ",");
+ ATF_REQUIRE_EQ(str, "foo,bar,baz");
+ }
+
+ // Second set of tests using a sorted collection, std::set.
+ {
+ std::set< std::string > words;
+ std::string str;
+
+ words.clear();
+ str = join(words, ",");
+ ATF_REQUIRE_EQ(str, "");
+
+ words.clear();
+ words.insert("");
+ str = join(words, ",");
+ ATF_REQUIRE_EQ(str, "");
+
+ words.clear();
+ words.insert("foo");
+ words.insert("");
+ words.insert("baz");
+ str = join(words, ",");
+ ATF_REQUIRE_EQ(str, ",baz,foo");
+
+ words.clear();
+ words.insert("foo");
+ words.insert("bar");
+ words.insert("baz");
+ str = join(words, ",");
+ ATF_REQUIRE_EQ(str, "bar,baz,foo");
+ }
+}
+
+ATF_TEST_CASE(match);
+ATF_TEST_CASE_HEAD(match)
+{
+ set_md_var("descr", "Tests the match function");
+}
+ATF_TEST_CASE_BODY(match)
+{
+ using atf::text::match;
+
+ ATF_REQUIRE_THROW(std::runtime_error, match("", "["));
+
+ ATF_REQUIRE(match("", ""));
+ ATF_REQUIRE(!match("foo", ""));
+
+ ATF_REQUIRE(match("", ".*"));
+ ATF_REQUIRE(match("", "[a-z]*"));
+
+ ATF_REQUIRE(match("hello", "hello"));
+ ATF_REQUIRE(match("hello", "[a-z]+"));
+ ATF_REQUIRE(match("hello", "^[a-z]+$"));
+
+ ATF_REQUIRE(!match("hello", "helooo"));
+ ATF_REQUIRE(!match("hello", "[a-z]+5"));
+ ATF_REQUIRE(!match("hello", "^ [a-z]+$"));
+}
+
+ATF_TEST_CASE(split);
+ATF_TEST_CASE_HEAD(split)
+{
+ set_md_var("descr", "Tests the split function");
+}
+ATF_TEST_CASE_BODY(split)
+{
+ using atf::text::split;
+
+ std::vector< std::string > words;
+
+ words = split("", " ");
+ ATF_REQUIRE_EQ(words.size(), 0);
+
+ words = split(" ", " ");
+ ATF_REQUIRE_EQ(words.size(), 0);
+
+ words = split(" ", " ");
+ ATF_REQUIRE_EQ(words.size(), 0);
+
+ words = split("a b", " ");
+ ATF_REQUIRE_EQ(words.size(), 2);
+ ATF_REQUIRE_EQ(words[0], "a");
+ ATF_REQUIRE_EQ(words[1], "b");
+
+ words = split("a b c d", " ");
+ ATF_REQUIRE_EQ(words.size(), 4);
+ ATF_REQUIRE_EQ(words[0], "a");
+ ATF_REQUIRE_EQ(words[1], "b");
+ ATF_REQUIRE_EQ(words[2], "c");
+ ATF_REQUIRE_EQ(words[3], "d");
+
+ words = split("foo bar", " ");
+ ATF_REQUIRE_EQ(words.size(), 2);
+ ATF_REQUIRE_EQ(words[0], "foo");
+ ATF_REQUIRE_EQ(words[1], "bar");
+
+ words = split("foo bar baz foobar", " ");
+ ATF_REQUIRE_EQ(words.size(), 4);
+ ATF_REQUIRE_EQ(words[0], "foo");
+ ATF_REQUIRE_EQ(words[1], "bar");
+ ATF_REQUIRE_EQ(words[2], "baz");
+ ATF_REQUIRE_EQ(words[3], "foobar");
+
+ words = split(" foo bar", " ");
+ ATF_REQUIRE_EQ(words.size(), 2);
+ ATF_REQUIRE_EQ(words[0], "foo");
+ ATF_REQUIRE_EQ(words[1], "bar");
+
+ words = split("foo bar", " ");
+ ATF_REQUIRE_EQ(words.size(), 2);
+ ATF_REQUIRE_EQ(words[0], "foo");
+ ATF_REQUIRE_EQ(words[1], "bar");
+
+ words = split("foo bar ", " ");
+ ATF_REQUIRE_EQ(words.size(), 2);
+ ATF_REQUIRE_EQ(words[0], "foo");
+ ATF_REQUIRE_EQ(words[1], "bar");
+
+ words = split(" foo bar ", " ");
+ ATF_REQUIRE_EQ(words.size(), 2);
+ ATF_REQUIRE_EQ(words[0], "foo");
+ ATF_REQUIRE_EQ(words[1], "bar");
+}
+
+ATF_TEST_CASE(split_delims);
+ATF_TEST_CASE_HEAD(split_delims)
+{
+ set_md_var("descr", "Tests the split function using different delimiters");
+}
+ATF_TEST_CASE_BODY(split_delims)
+{
+ using atf::text::split;
+
+ std::vector< std::string > words;
+
+ words = split("", "/");
+ ATF_REQUIRE_EQ(words.size(), 0);
+
+ words = split(" ", "/");
+ ATF_REQUIRE_EQ(words.size(), 1);
+ ATF_REQUIRE_EQ(words[0], " ");
+
+ words = split(" ", "/");
+ ATF_REQUIRE_EQ(words.size(), 1);
+ ATF_REQUIRE_EQ(words[0], " ");
+
+ words = split("a/b", "/");
+ ATF_REQUIRE_EQ(words.size(), 2);
+ ATF_REQUIRE_EQ(words[0], "a");
+ ATF_REQUIRE_EQ(words[1], "b");
+
+ words = split("aLONGDELIMbcdLONGDELIMef", "LONGDELIM");
+ ATF_REQUIRE_EQ(words.size(), 3);
+ ATF_REQUIRE_EQ(words[0], "a");
+ ATF_REQUIRE_EQ(words[1], "bcd");
+ ATF_REQUIRE_EQ(words[2], "ef");
+}
+
+ATF_TEST_CASE(trim);
+ATF_TEST_CASE_HEAD(trim)
+{
+ set_md_var("descr", "Tests the trim function");
+}
+ATF_TEST_CASE_BODY(trim)
+{
+ using atf::text::trim;
+
+ ATF_REQUIRE_EQ(trim(""), "");
+ ATF_REQUIRE_EQ(trim(" "), "");
+ ATF_REQUIRE_EQ(trim("\t"), "");
+
+ ATF_REQUIRE_EQ(trim(" foo"), "foo");
+ ATF_REQUIRE_EQ(trim("\t foo"), "foo");
+ ATF_REQUIRE_EQ(trim(" \tfoo"), "foo");
+ ATF_REQUIRE_EQ(trim("foo\t "), "foo");
+ ATF_REQUIRE_EQ(trim("foo \t"), "foo");
+
+ ATF_REQUIRE_EQ(trim("foo bar"), "foo bar");
+ ATF_REQUIRE_EQ(trim("\t foo bar"), "foo bar");
+ ATF_REQUIRE_EQ(trim(" \tfoo bar"), "foo bar");
+ ATF_REQUIRE_EQ(trim("foo bar\t "), "foo bar");
+ ATF_REQUIRE_EQ(trim("foo bar \t"), "foo bar");
+}
+
+ATF_TEST_CASE(to_bool);
+ATF_TEST_CASE_HEAD(to_bool)
+{
+ set_md_var("descr", "Tests the to_string function");
+}
+ATF_TEST_CASE_BODY(to_bool)
+{
+ using atf::text::to_bool;
+
+ ATF_REQUIRE(to_bool("true"));
+ ATF_REQUIRE(to_bool("TRUE"));
+ ATF_REQUIRE(to_bool("yes"));
+ ATF_REQUIRE(to_bool("YES"));
+
+ ATF_REQUIRE(!to_bool("false"));
+ ATF_REQUIRE(!to_bool("FALSE"));
+ ATF_REQUIRE(!to_bool("no"));
+ ATF_REQUIRE(!to_bool("NO"));
+
+ ATF_REQUIRE_THROW(std::runtime_error, to_bool(""));
+ ATF_REQUIRE_THROW(std::runtime_error, to_bool("tru"));
+ ATF_REQUIRE_THROW(std::runtime_error, to_bool("true2"));
+ ATF_REQUIRE_THROW(std::runtime_error, to_bool("fals"));
+ ATF_REQUIRE_THROW(std::runtime_error, to_bool("false2"));
+}
+
+ATF_TEST_CASE(to_bytes);
+ATF_TEST_CASE_HEAD(to_bytes)
+{
+ set_md_var("descr", "Tests the to_bytes function");
+}
+ATF_TEST_CASE_BODY(to_bytes)
+{
+ using atf::text::to_bytes;
+
+ ATF_REQUIRE_EQ(0, to_bytes("0"));
+ ATF_REQUIRE_EQ(12345, to_bytes("12345"));
+ ATF_REQUIRE_EQ(2 * 1024, to_bytes("2k"));
+ ATF_REQUIRE_EQ(4 * 1024 * 1024, to_bytes("4m"));
+ ATF_REQUIRE_EQ(int64_t(8) * 1024 * 1024 * 1024, to_bytes("8g"));
+ ATF_REQUIRE_EQ(int64_t(16) * 1024 * 1024 * 1024 * 1024, to_bytes("16t"));
+
+ ATF_REQUIRE_THROW_RE(std::runtime_error, "Empty", to_bytes(""));
+ ATF_REQUIRE_THROW_RE(std::runtime_error, "Unknown size unit 'd'",
+ to_bytes("12d"));
+ ATF_REQUIRE_THROW(std::runtime_error, to_bytes(" "));
+ ATF_REQUIRE_THROW(std::runtime_error, to_bytes(" k"));
+}
+
+ATF_TEST_CASE(to_string);
+ATF_TEST_CASE_HEAD(to_string)
+{
+ set_md_var("descr", "Tests the to_string function");
+}
+ATF_TEST_CASE_BODY(to_string)
+{
+ using atf::text::to_string;
+
+ ATF_REQUIRE_EQ(to_string('a'), "a");
+ ATF_REQUIRE_EQ(to_string("a"), "a");
+ ATF_REQUIRE_EQ(to_string(5), "5");
+}
+
+ATF_TEST_CASE(to_type);
+ATF_TEST_CASE_HEAD(to_type)
+{
+ set_md_var("descr", "Tests the to_type function");
+}
+ATF_TEST_CASE_BODY(to_type)
+{
+ using atf::text::to_type;
+
+ ATF_REQUIRE_EQ(to_type< int >("0"), 0);
+ ATF_REQUIRE_EQ(to_type< int >("1234"), 1234);
+ ATF_REQUIRE_THROW(std::runtime_error, to_type< int >(" "));
+ ATF_REQUIRE_THROW(std::runtime_error, to_type< int >("0 a"));
+ ATF_REQUIRE_THROW(std::runtime_error, to_type< int >("a"));
+
+ ATF_REQUIRE_EQ(to_type< float >("0.5"), 0.5);
+ ATF_REQUIRE_EQ(to_type< float >("1234.5"), 1234.5);
+ ATF_REQUIRE_THROW(std::runtime_error, to_type< float >("0.5 a"));
+ ATF_REQUIRE_THROW(std::runtime_error, to_type< float >("a"));
+
+ ATF_REQUIRE_EQ(to_type< std::string >("a"), "a");
+}
+
+// ------------------------------------------------------------------------
+// Main.
+// ------------------------------------------------------------------------
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ // Add the test cases for the free functions.
+ ATF_ADD_TEST_CASE(tcs, duplicate);
+ ATF_ADD_TEST_CASE(tcs, join);
+ ATF_ADD_TEST_CASE(tcs, match);
+ ATF_ADD_TEST_CASE(tcs, split);
+ ATF_ADD_TEST_CASE(tcs, split_delims);
+ ATF_ADD_TEST_CASE(tcs, trim);
+ ATF_ADD_TEST_CASE(tcs, to_bool);
+ ATF_ADD_TEST_CASE(tcs, to_bytes);
+ ATF_ADD_TEST_CASE(tcs, to_string);
+ ATF_ADD_TEST_CASE(tcs, to_type);
+}
diff --git a/unit/atf-src/atf-c++/detail/version_helper.cpp b/unit/atf-src/atf-c++/detail/version_helper.cpp
new file mode 100644
index 0000000..0ee4791
--- /dev/null
+++ b/unit/atf-src/atf-c++/detail/version_helper.cpp
@@ -0,0 +1,41 @@
+// 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 <cstdlib>
+#include <iostream>
+
+int
+main(void)
+{
+ std::cout << PACKAGE_VERSION << "\n";
+ return EXIT_SUCCESS;
+}
diff --git a/unit/atf-src/atf-c++/macros.hpp b/unit/atf-src/atf-c++/macros.hpp
new file mode 100644
index 0000000..ea0b2dc
--- /dev/null
+++ b/unit/atf-src/atf-c++/macros.hpp
@@ -0,0 +1,225 @@
+// 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_CXX_MACROS_HPP)
+#define ATF_CXX_MACROS_HPP
+
+#include <sstream>
+#include <stdexcept>
+#include <vector>
+
+#include <atf-c++/tests.hpp>
+
+// Do not define inline methods for the test case classes. Doing so
+// significantly increases the memory requirements of GNU G++ during
+// compilation.
+
+#define ATF_TEST_CASE_WITHOUT_HEAD(name) \
+ namespace { \
+ class atfu_tc_ ## name : public atf::tests::tc { \
+ void body(void) const; \
+ public: \
+ atfu_tc_ ## name(void); \
+ }; \
+ static atfu_tc_ ## name* atfu_tcptr_ ## name; \
+ atfu_tc_ ## name::atfu_tc_ ## name(void) : atf::tests::tc(#name, false) {} \
+ }
+
+#define ATF_TEST_CASE(name) \
+ namespace { \
+ class atfu_tc_ ## name : public atf::tests::tc { \
+ void head(void); \
+ void body(void) const; \
+ public: \
+ atfu_tc_ ## name(void); \
+ }; \
+ static atfu_tc_ ## name* atfu_tcptr_ ## name; \
+ atfu_tc_ ## name::atfu_tc_ ## name(void) : atf::tests::tc(#name, false) {} \
+ }
+
+#define ATF_TEST_CASE_WITH_CLEANUP(name) \
+ namespace { \
+ class atfu_tc_ ## name : public atf::tests::tc { \
+ void head(void); \
+ void body(void) const; \
+ void cleanup(void) const; \
+ public: \
+ atfu_tc_ ## name(void); \
+ }; \
+ static atfu_tc_ ## name* atfu_tcptr_ ## name; \
+ atfu_tc_ ## name::atfu_tc_ ## name(void) : atf::tests::tc(#name, true) {} \
+ }
+
+#define ATF_TEST_CASE_NAME(name) atfu_tc_ ## name
+#define ATF_TEST_CASE_USE(name) (atfu_tcptr_ ## name) = NULL
+
+#define ATF_TEST_CASE_HEAD(name) \
+ void \
+ atfu_tc_ ## name::head(void)
+
+#define ATF_TEST_CASE_BODY(name) \
+ void \
+ atfu_tc_ ## name::body(void) \
+ const
+
+#define ATF_TEST_CASE_CLEANUP(name) \
+ void \
+ atfu_tc_ ## name::cleanup(void) \
+ const
+
+#define ATF_FAIL(reason) atf::tests::tc::fail(reason)
+
+#define ATF_SKIP(reason) atf::tests::tc::skip(reason)
+
+#define ATF_PASS() atf::tests::tc::pass()
+
+#define ATF_REQUIRE(expression) \
+ do { \
+ if (!(expression)) { \
+ std::ostringstream atfu_ss; \
+ atfu_ss << "Line " << __LINE__ << ": " << #expression \
+ << " not met"; \
+ atf::tests::tc::fail(atfu_ss.str()); \
+ } \
+ } while (false)
+
+#define ATF_REQUIRE_EQ(expected, actual) \
+ do { \
+ if ((expected) != (actual)) { \
+ std::ostringstream atfu_ss; \
+ atfu_ss << "Line " << __LINE__ << ": " \
+ << #expected << " != " << #actual \
+ << " (" << (expected) << " != " << (actual) << ")"; \
+ atf::tests::tc::fail(atfu_ss.str()); \
+ } \
+ } while (false)
+
+#define ATF_REQUIRE_IN(element, collection) \
+ ATF_REQUIRE((collection).find(element) != (collection).end())
+
+#define ATF_REQUIRE_NOT_IN(element, collection) \
+ ATF_REQUIRE((collection).find(element) == (collection).end())
+
+#define ATF_REQUIRE_MATCH(regexp, string) \
+ do { \
+ if (!atf::tests::detail::match(regexp, string)) { \
+ std::ostringstream atfu_ss; \
+ atfu_ss << "Line " << __LINE__ << ": '" << string << "' does not " \
+ << "match regexp '" << regexp << "'"; \
+ atf::tests::tc::fail(atfu_ss.str()); \
+ } \
+ } while (false)
+
+#define ATF_REQUIRE_THROW(expected_exception, statement) \
+ do { \
+ try { \
+ statement; \
+ std::ostringstream atfu_ss; \
+ atfu_ss << "Line " << __LINE__ \
+ << ": " #statement " did not throw " #expected_exception \
+ " as expected"; \
+ atf::tests::tc::fail(atfu_ss.str()); \
+ } catch (const expected_exception&) { \
+ } catch (const std::exception& atfu_e) { \
+ std::ostringstream atfu_ss; \
+ atfu_ss << "Line " << __LINE__ << ": " #statement " threw an " \
+ "unexpected error (not " #expected_exception "): " \
+ << atfu_e.what(); \
+ atf::tests::tc::fail(atfu_ss.str()); \
+ } catch (...) { \
+ std::ostringstream atfu_ss; \
+ atfu_ss << "Line " << __LINE__ << ": " #statement " threw an " \
+ "unexpected error (not " #expected_exception ")"; \
+ atf::tests::tc::fail(atfu_ss.str()); \
+ } \
+ } while (false)
+
+#define ATF_REQUIRE_THROW_RE(expected_exception, regexp, statement) \
+ do { \
+ try { \
+ statement; \
+ std::ostringstream atfu_ss; \
+ atfu_ss << "Line " << __LINE__ \
+ << ": " #statement " did not throw " #expected_exception \
+ " as expected"; \
+ atf::tests::tc::fail(atfu_ss.str()); \
+ } catch (const expected_exception& e) { \
+ if (!atf::tests::detail::match(regexp, e.what())) { \
+ std::ostringstream atfu_ss; \
+ atfu_ss << "Line " << __LINE__ \
+ << ": " #statement " threw " #expected_exception "(" \
+ << e.what() << "), but does not match '" << regexp \
+ << "'"; \
+ atf::tests::tc::fail(atfu_ss.str()); \
+ } \
+ } catch (const std::exception& atfu_e) { \
+ std::ostringstream atfu_ss; \
+ atfu_ss << "Line " << __LINE__ << ": " #statement " threw an " \
+ "unexpected error (not " #expected_exception "): " \
+ << atfu_e.what(); \
+ atf::tests::tc::fail(atfu_ss.str()); \
+ } catch (...) { \
+ std::ostringstream atfu_ss; \
+ atfu_ss << "Line " << __LINE__ << ": " #statement " threw an " \
+ "unexpected error (not " #expected_exception ")"; \
+ atf::tests::tc::fail(atfu_ss.str()); \
+ } \
+ } while (false)
+
+#define ATF_CHECK_ERRNO(expected_errno, bool_expr) \
+ atf::tests::tc::check_errno(__FILE__, __LINE__, expected_errno, \
+ #bool_expr, bool_expr)
+
+#define ATF_REQUIRE_ERRNO(expected_errno, bool_expr) \
+ atf::tests::tc::require_errno(__FILE__, __LINE__, expected_errno, \
+ #bool_expr, bool_expr)
+
+#define ATF_INIT_TEST_CASES(tcs) \
+ namespace atf { \
+ namespace tests { \
+ int run_tp(int, char**, \
+ void (*)(std::vector< atf::tests::tc * >&)); \
+ } \
+ } \
+ \
+ static void atfu_init_tcs(std::vector< atf::tests::tc * >&); \
+ \
+ int \
+ main(int argc, char** argv) \
+ { \
+ return atf::tests::run_tp(argc, argv, atfu_init_tcs); \
+ } \
+ \
+ static \
+ void \
+ atfu_init_tcs(std::vector< atf::tests::tc * >& tcs)
+
+#define ATF_ADD_TEST_CASE(tcs, tcname) \
+ do { \
+ atfu_tcptr_ ## tcname = new atfu_tc_ ## tcname(); \
+ (tcs).push_back(atfu_tcptr_ ## tcname); \
+ } while (0);
+
+#endif // !defined(ATF_CXX_MACROS_HPP)
diff --git a/unit/atf-src/atf-c++/macros_hpp_test.cpp b/unit/atf-src/atf-c++/macros_hpp_test.cpp
new file mode 100644
index 0000000..0bc27c6
--- /dev/null
+++ b/unit/atf-src/atf-c++/macros_hpp_test.cpp
@@ -0,0 +1,126 @@
+// 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.hpp>
+
+#include <stdexcept>
+
+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_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_eq_inside_if(void)
+{
+ // Make sure that ATF_REQUIRE_EQ 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_require_throw_runtime_error(void)
+{
+ // Check that we can pass std::runtime_error to ATF_REQUIRE_THROW.
+ // Earlier versions generated a warning because the macro's code also
+ // attempted to capture this exception, and thus we had a duplicate
+ // catch clause.
+ ATF_REQUIRE_THROW(std::runtime_error, (void)0);
+}
+
+void
+atf_require_throw_inside_if(void)
+{
+ // Make sure that ATF_REQUIRE_THROW can be used inside an if statement
+ // that does not have braces. Earlier versions of it generated an
+ // error because a trailing ; after a catch block was not allowed.
+ if (true)
+ ATF_REQUIRE_THROW(std::runtime_error, (void)0);
+ else
+ ATF_REQUIRE_THROW(std::runtime_error, (void)1);
+}
+
+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_TEST_CASE(TEST_MACRO_1);
+ATF_TEST_CASE_HEAD(TEST_MACRO_1) { }
+ATF_TEST_CASE_BODY(TEST_MACRO_1) { }
+void instantiate_1(void) {
+ ATF_TEST_CASE_USE(TEST_MACRO_1);
+ atf::tests::tc* the_test = new ATF_TEST_CASE_NAME(TEST_MACRO_1)();
+ delete the_test;
+}
+ATF_TEST_CASE_WITH_CLEANUP(TEST_MACRO_2);
+ATF_TEST_CASE_HEAD(TEST_MACRO_2) { }
+ATF_TEST_CASE_BODY(TEST_MACRO_2) { }
+ATF_TEST_CASE_CLEANUP(TEST_MACRO_2) { }
+void instatiate_2(void) {
+ ATF_TEST_CASE_USE(TEST_MACRO_2);
+ atf::tests::tc* the_test = new ATF_TEST_CASE_NAME(TEST_MACRO_2)();
+ delete the_test;
+}
+ATF_TEST_CASE_WITH_CLEANUP(TEST_MACRO_3);
+ATF_TEST_CASE_HEAD(TEST_MACRO_3) { }
+ATF_TEST_CASE_BODY(TEST_MACRO_3) { }
+ATF_TEST_CASE_CLEANUP(TEST_MACRO_3) { }
+void instatiate_3(void) {
+ ATF_TEST_CASE_USE(TEST_MACRO_3);
+ atf::tests::tc* the_test = new ATF_TEST_CASE_NAME(TEST_MACRO_3)();
+ delete the_test;
+}
diff --git a/unit/atf-src/atf-c++/macros_test.cpp b/unit/atf-src/atf-c++/macros_test.cpp
new file mode 100644
index 0000000..23b5e5c
--- /dev/null
+++ b/unit/atf-src/atf-c++/macros_test.cpp
@@ -0,0 +1,811 @@
+// 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.hpp"
+
+extern "C" {
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+#include <cerrno>
+#include <cstdlib>
+#include <iostream>
+#include <stdexcept>
+
+#include <atf-c++.hpp>
+
+#include "atf-c++/detail/fs.hpp"
+#include "atf-c++/detail/process.hpp"
+#include "atf-c++/detail/sanity.hpp"
+#include "atf-c++/detail/test_helpers.hpp"
+#include "atf-c++/detail/text.hpp"
+#include "atf-c++/utils.hpp"
+
+// ------------------------------------------------------------------------
+// Auxiliary functions.
+// ------------------------------------------------------------------------
+
+static
+void
+create_ctl_file(const char *name)
+{
+ ATF_REQUIRE(open(name, O_CREAT | O_WRONLY | O_TRUNC, 0644) != -1);
+}
+
+// ------------------------------------------------------------------------
+// Auxiliary test cases.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(h_pass);
+ATF_TEST_CASE_HEAD(h_pass)
+{
+ set_md_var("descr", "Helper test case");
+}
+ATF_TEST_CASE_BODY(h_pass)
+{
+ create_ctl_file("before");
+ ATF_PASS();
+ create_ctl_file("after");
+}
+
+ATF_TEST_CASE(h_fail);
+ATF_TEST_CASE_HEAD(h_fail)
+{
+ set_md_var("descr", "Helper test case");
+}
+ATF_TEST_CASE_BODY(h_fail)
+{
+ create_ctl_file("before");
+ ATF_FAIL("Failed on purpose");
+ create_ctl_file("after");
+}
+
+ATF_TEST_CASE(h_skip);
+ATF_TEST_CASE_HEAD(h_skip)
+{
+ set_md_var("descr", "Helper test case");
+}
+ATF_TEST_CASE_BODY(h_skip)
+{
+ create_ctl_file("before");
+ ATF_SKIP("Skipped on purpose");
+ create_ctl_file("after");
+}
+
+ATF_TEST_CASE(h_require);
+ATF_TEST_CASE_HEAD(h_require)
+{
+ set_md_var("descr", "Helper test case");
+}
+ATF_TEST_CASE_BODY(h_require)
+{
+ bool condition = atf::text::to_bool(get_config_var("condition"));
+
+ create_ctl_file("before");
+ ATF_REQUIRE(condition);
+ create_ctl_file("after");
+}
+
+ATF_TEST_CASE(h_require_eq);
+ATF_TEST_CASE_HEAD(h_require_eq)
+{
+ set_md_var("descr", "Helper test case");
+}
+ATF_TEST_CASE_BODY(h_require_eq)
+{
+ long v1 = atf::text::to_type< long >(get_config_var("v1"));
+ long v2 = atf::text::to_type< long >(get_config_var("v2"));
+
+ create_ctl_file("before");
+ ATF_REQUIRE_EQ(v1, v2);
+ create_ctl_file("after");
+}
+
+ATF_TEST_CASE(h_require_in);
+ATF_TEST_CASE_HEAD(h_require_in)
+{
+ set_md_var("descr", "Helper test case");
+}
+ATF_TEST_CASE_BODY(h_require_in)
+{
+ const std::string element = get_config_var("value");
+
+ std::set< std::string > collection;
+ collection.insert("foo");
+ collection.insert("bar");
+ collection.insert("baz");
+
+ create_ctl_file("before");
+ ATF_REQUIRE_IN(element, collection);
+ create_ctl_file("after");
+}
+
+ATF_TEST_CASE(h_require_match);
+ATF_TEST_CASE_HEAD(h_require_match)
+{
+ set_md_var("descr", "Helper test case");
+}
+ATF_TEST_CASE_BODY(h_require_match)
+{
+ const std::string regexp = get_config_var("regexp");
+ const std::string string = get_config_var("string");
+
+ create_ctl_file("before");
+ ATF_REQUIRE_MATCH(regexp, string);
+ create_ctl_file("after");
+}
+
+ATF_TEST_CASE(h_require_not_in);
+ATF_TEST_CASE_HEAD(h_require_not_in)
+{
+ set_md_var("descr", "Helper test case");
+}
+ATF_TEST_CASE_BODY(h_require_not_in)
+{
+ const std::string element = get_config_var("value");
+
+ std::set< std::string > collection;
+ collection.insert("foo");
+ collection.insert("bar");
+ collection.insert("baz");
+
+ create_ctl_file("before");
+ ATF_REQUIRE_NOT_IN(element, collection);
+ create_ctl_file("after");
+}
+
+ATF_TEST_CASE(h_require_throw);
+ATF_TEST_CASE_HEAD(h_require_throw)
+{
+ set_md_var("descr", "Helper test case");
+}
+ATF_TEST_CASE_BODY(h_require_throw)
+{
+ create_ctl_file("before");
+
+ if (get_config_var("what") == "throw_int")
+ ATF_REQUIRE_THROW(std::runtime_error, if (1) throw int(5));
+ else if (get_config_var("what") == "throw_rt")
+ ATF_REQUIRE_THROW(std::runtime_error,
+ if (1) throw std::runtime_error("e"));
+ else if (get_config_var("what") == "no_throw_rt")
+ ATF_REQUIRE_THROW(std::runtime_error,
+ if (0) throw std::runtime_error("e"));
+
+ create_ctl_file("after");
+}
+
+ATF_TEST_CASE(h_require_throw_re);
+ATF_TEST_CASE_HEAD(h_require_throw_re)
+{
+ set_md_var("descr", "Helper test case");
+}
+ATF_TEST_CASE_BODY(h_require_throw_re)
+{
+ create_ctl_file("before");
+
+ if (get_config_var("what") == "throw_int")
+ ATF_REQUIRE_THROW_RE(std::runtime_error, "5", if (1) throw int(5));
+ else if (get_config_var("what") == "throw_rt_match")
+ ATF_REQUIRE_THROW_RE(std::runtime_error, "foo.*baz",
+ if (1) throw std::runtime_error("a foo bar baz"));
+ else if (get_config_var("what") == "throw_rt_no_match")
+ ATF_REQUIRE_THROW_RE(std::runtime_error, "foo.*baz",
+ if (1) throw std::runtime_error("baz foo bar a"));
+ else if (get_config_var("what") == "no_throw_rt")
+ ATF_REQUIRE_THROW_RE(std::runtime_error, "e",
+ if (0) throw std::runtime_error("e"));
+
+ create_ctl_file("after");
+}
+
+static int
+errno_fail_stub(const int raised_errno)
+{
+ errno = raised_errno;
+ return -1;
+}
+
+static int
+errno_ok_stub(void)
+{
+ return 0;
+}
+
+ATF_TEST_CASE(h_check_errno);
+ATF_TEST_CASE_HEAD(h_check_errno)
+{
+ set_md_var("descr", "Helper test case");
+}
+ATF_TEST_CASE_BODY(h_check_errno)
+{
+ create_ctl_file("before");
+
+ if (get_config_var("what") == "no_error")
+ ATF_CHECK_ERRNO(-1, errno_ok_stub() == -1);
+ else if (get_config_var("what") == "errno_ok")
+ ATF_CHECK_ERRNO(2, errno_fail_stub(2) == -1);
+ else if (get_config_var("what") == "errno_fail")
+ ATF_CHECK_ERRNO(3, errno_fail_stub(4) == -1);
+ else
+ UNREACHABLE;
+
+ create_ctl_file("after");
+}
+
+ATF_TEST_CASE(h_require_errno);
+ATF_TEST_CASE_HEAD(h_require_errno)
+{
+ set_md_var("descr", "Helper test case");
+}
+ATF_TEST_CASE_BODY(h_require_errno)
+{
+ create_ctl_file("before");
+
+ if (get_config_var("what") == "no_error")
+ ATF_REQUIRE_ERRNO(-1, errno_ok_stub() == -1);
+ else if (get_config_var("what") == "errno_ok")
+ ATF_REQUIRE_ERRNO(2, errno_fail_stub(2) == -1);
+ else if (get_config_var("what") == "errno_fail")
+ ATF_REQUIRE_ERRNO(3, errno_fail_stub(4) == -1);
+ else
+ UNREACHABLE;
+
+ create_ctl_file("after");
+}
+
+// ------------------------------------------------------------------------
+// Test cases for the macros.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE(pass);
+ATF_TEST_CASE_HEAD(pass)
+{
+ set_md_var("descr", "Tests the ATF_PASS macro");
+}
+ATF_TEST_CASE_BODY(pass)
+{
+ ATF_TEST_CASE_USE(h_pass);
+ run_h_tc< ATF_TEST_CASE_NAME(h_pass) >();
+ ATF_REQUIRE(atf::utils::grep_file("^passed", "result"));
+ ATF_REQUIRE(atf::fs::exists(atf::fs::path("before")));
+ ATF_REQUIRE(!atf::fs::exists(atf::fs::path("after")));
+}
+
+ATF_TEST_CASE(fail);
+ATF_TEST_CASE_HEAD(fail)
+{
+ set_md_var("descr", "Tests the ATF_FAIL macro");
+}
+ATF_TEST_CASE_BODY(fail)
+{
+ ATF_TEST_CASE_USE(h_fail);
+ run_h_tc< ATF_TEST_CASE_NAME(h_fail) >();
+ ATF_REQUIRE(atf::utils::grep_file("^failed: Failed on purpose", "result"));
+ ATF_REQUIRE(atf::fs::exists(atf::fs::path("before")));
+ ATF_REQUIRE(!atf::fs::exists(atf::fs::path("after")));
+}
+
+ATF_TEST_CASE(skip);
+ATF_TEST_CASE_HEAD(skip)
+{
+ set_md_var("descr", "Tests the ATF_SKIP macro");
+}
+ATF_TEST_CASE_BODY(skip)
+{
+ ATF_TEST_CASE_USE(h_skip);
+ run_h_tc< ATF_TEST_CASE_NAME(h_skip) >();
+ ATF_REQUIRE(atf::utils::grep_file("^skipped: Skipped on purpose",
+ "result"));
+ ATF_REQUIRE(atf::fs::exists(atf::fs::path("before")));
+ ATF_REQUIRE(!atf::fs::exists(atf::fs::path("after")));
+}
+
+ATF_TEST_CASE(require);
+ATF_TEST_CASE_HEAD(require)
+{
+ set_md_var("descr", "Tests the ATF_REQUIRE macro");
+}
+ATF_TEST_CASE_BODY(require)
+{
+ struct test {
+ const char *cond;
+ bool ok;
+ } *t, tests[] = {
+ { "false", false },
+ { "true", true },
+ { NULL, false }
+ };
+
+ const atf::fs::path before("before");
+ const atf::fs::path after("after");
+
+ for (t = &tests[0]; t->cond != NULL; t++) {
+ atf::tests::vars_map config;
+ config["condition"] = t->cond;
+
+ std::cout << "Checking with a " << t->cond << " value\n";
+
+ ATF_TEST_CASE_USE(h_require);
+ run_h_tc< ATF_TEST_CASE_NAME(h_require) >(config);
+
+ ATF_REQUIRE(atf::fs::exists(before));
+ if (t->ok) {
+ ATF_REQUIRE(atf::utils::grep_file("^passed", "result"));
+ ATF_REQUIRE(atf::fs::exists(after));
+ } else {
+ ATF_REQUIRE(atf::utils::grep_file(
+ "^failed: .*condition not met", "result"));
+ ATF_REQUIRE(!atf::fs::exists(after));
+ }
+
+ atf::fs::remove(before);
+ if (t->ok)
+ atf::fs::remove(after);
+ }
+}
+
+ATF_TEST_CASE(require_eq);
+ATF_TEST_CASE_HEAD(require_eq)
+{
+ set_md_var("descr", "Tests the ATF_REQUIRE_EQ macro");
+}
+ATF_TEST_CASE_BODY(require_eq)
+{
+ struct test {
+ const char *v1;
+ const char *v2;
+ bool ok;
+ } *t, tests[] = {
+ { "1", "1", true },
+ { "1", "2", false },
+ { "2", "1", false },
+ { "2", "2", true },
+ { NULL, NULL, false }
+ };
+
+ const atf::fs::path before("before");
+ const atf::fs::path after("after");
+
+ for (t = &tests[0]; t->v1 != NULL; t++) {
+ atf::tests::vars_map config;
+ config["v1"] = t->v1;
+ config["v2"] = t->v2;
+
+ std::cout << "Checking with " << t->v1 << ", " << t->v2
+ << " and expecting " << (t->ok ? "true" : "false")
+ << "\n";
+
+ ATF_TEST_CASE_USE(h_require_eq);
+ run_h_tc< ATF_TEST_CASE_NAME(h_require_eq) >(config);
+
+ ATF_REQUIRE(atf::fs::exists(before));
+ if (t->ok) {
+ ATF_REQUIRE(atf::utils::grep_file("^passed", "result"));
+ ATF_REQUIRE(atf::fs::exists(after));
+ } else {
+ ATF_REQUIRE(atf::utils::grep_file("^failed: .*v1 != v2", "result"));
+ ATF_REQUIRE(!atf::fs::exists(after));
+ }
+
+ atf::fs::remove(before);
+ if (t->ok)
+ atf::fs::remove(after);
+ }
+}
+
+ATF_TEST_CASE(require_in);
+ATF_TEST_CASE_HEAD(require_in)
+{
+ set_md_var("descr", "Tests the ATF_REQUIRE_IN macro");
+}
+ATF_TEST_CASE_BODY(require_in)
+{
+ struct test {
+ const char *value;
+ bool ok;
+ } *t, tests[] = {
+ { "foo", true },
+ { "bar", true },
+ { "baz", true },
+ { "xxx", false },
+ { "fooa", false },
+ { "foo ", false },
+ { NULL, false }
+ };
+
+ const atf::fs::path before("before");
+ const atf::fs::path after("after");
+
+ for (t = &tests[0]; t->value != NULL; t++) {
+ atf::tests::vars_map config;
+ config["value"] = t->value;
+
+ ATF_TEST_CASE_USE(h_require_in);
+ run_h_tc< ATF_TEST_CASE_NAME(h_require_in) >(config);
+
+ ATF_REQUIRE(atf::fs::exists(before));
+ if (t->ok) {
+ ATF_REQUIRE(atf::utils::grep_file("^passed", "result"));
+ ATF_REQUIRE(atf::fs::exists(after));
+ } else {
+ ATF_REQUIRE(atf::utils::grep_file("^failed: ", "result"));
+ ATF_REQUIRE(!atf::fs::exists(after));
+ }
+
+ atf::fs::remove(before);
+ if (t->ok)
+ atf::fs::remove(after);
+ }
+}
+
+ATF_TEST_CASE(require_match);
+ATF_TEST_CASE_HEAD(require_match)
+{
+ set_md_var("descr", "Tests the ATF_REQUIRE_MATCH macro");
+}
+ATF_TEST_CASE_BODY(require_match)
+{
+ struct test {
+ const char *regexp;
+ const char *string;
+ bool ok;
+ } *t, tests[] = {
+ { "foo.*bar", "this is a foo, bar, baz", true },
+ { "bar.*baz", "this is a baz, bar, foo", false },
+ { NULL, NULL, false }
+ };
+
+ const atf::fs::path before("before");
+ const atf::fs::path after("after");
+
+ for (t = &tests[0]; t->regexp != NULL; t++) {
+ atf::tests::vars_map config;
+ config["regexp"] = t->regexp;
+ config["string"] = t->string;
+
+ std::cout << "Checking with " << t->regexp << ", " << t->string
+ << " and expecting " << (t->ok ? "true" : "false")
+ << "\n";
+
+ ATF_TEST_CASE_USE(h_require_match);
+ run_h_tc< ATF_TEST_CASE_NAME(h_require_match) >(config);
+
+ ATF_REQUIRE(atf::fs::exists(before));
+ if (t->ok) {
+ ATF_REQUIRE(atf::utils::grep_file("^passed", "result"));
+ ATF_REQUIRE(atf::fs::exists(after));
+ } else {
+ ATF_REQUIRE(atf::utils::grep_file("^failed: ", "result"));
+ ATF_REQUIRE(!atf::fs::exists(after));
+ }
+
+ atf::fs::remove(before);
+ if (t->ok)
+ atf::fs::remove(after);
+ }
+}
+
+ATF_TEST_CASE(require_not_in);
+ATF_TEST_CASE_HEAD(require_not_in)
+{
+ set_md_var("descr", "Tests the ATF_REQUIRE_NOT_IN macro");
+}
+ATF_TEST_CASE_BODY(require_not_in)
+{
+ struct test {
+ const char *value;
+ bool ok;
+ } *t, tests[] = {
+ { "foo", false },
+ { "bar", false },
+ { "baz", false },
+ { "xxx", true },
+ { "fooa", true },
+ { "foo ", true },
+ { NULL, false }
+ };
+
+ const atf::fs::path before("before");
+ const atf::fs::path after("after");
+
+ for (t = &tests[0]; t->value != NULL; t++) {
+ atf::tests::vars_map config;
+ config["value"] = t->value;
+
+ ATF_TEST_CASE_USE(h_require_not_in);
+ run_h_tc< ATF_TEST_CASE_NAME(h_require_not_in) >(config);
+
+ ATF_REQUIRE(atf::fs::exists(before));
+ if (t->ok) {
+ ATF_REQUIRE(atf::utils::grep_file("^passed", "result"));
+ ATF_REQUIRE(atf::fs::exists(after));
+ } else {
+ ATF_REQUIRE(atf::utils::grep_file("^failed: ", "result"));
+ ATF_REQUIRE(!atf::fs::exists(after));
+ }
+
+ atf::fs::remove(before);
+ if (t->ok)
+ atf::fs::remove(after);
+ }
+}
+
+ATF_TEST_CASE(require_throw);
+ATF_TEST_CASE_HEAD(require_throw)
+{
+ set_md_var("descr", "Tests the ATF_REQUIRE_THROW macro");
+}
+ATF_TEST_CASE_BODY(require_throw)
+{
+ struct test {
+ const char *what;
+ bool ok;
+ const char *msg;
+ } *t, tests[] = {
+ { "throw_int", false, "unexpected error" },
+ { "throw_rt", true, NULL },
+ { "no_throw_rt", false, "did not throw" },
+ { NULL, false, NULL }
+ };
+
+ const atf::fs::path before("before");
+ const atf::fs::path after("after");
+
+ for (t = &tests[0]; t->what != NULL; t++) {
+ atf::tests::vars_map config;
+ config["what"] = t->what;
+
+ std::cout << "Checking with " << t->what << " and expecting "
+ << (t->ok ? "true" : "false") << "\n";
+
+ ATF_TEST_CASE_USE(h_require_throw);
+ run_h_tc< ATF_TEST_CASE_NAME(h_require_throw) >(config);
+
+ ATF_REQUIRE(atf::fs::exists(before));
+ if (t->ok) {
+ ATF_REQUIRE(atf::utils::grep_file("^passed", "result"));
+ ATF_REQUIRE(atf::fs::exists(after));
+ } else {
+ std::cout << "Checking that message contains '" << t->msg
+ << "'\n";
+ std::string exp_result = std::string("^failed: .*") + t->msg;
+ ATF_REQUIRE(atf::utils::grep_file(exp_result.c_str(), "result"));
+ ATF_REQUIRE(!atf::fs::exists(after));
+ }
+
+ atf::fs::remove(before);
+ if (t->ok)
+ atf::fs::remove(after);
+ }
+}
+
+ATF_TEST_CASE(require_throw_re);
+ATF_TEST_CASE_HEAD(require_throw_re)
+{
+ set_md_var("descr", "Tests the ATF_REQUIRE_THROW_RE macro");
+}
+ATF_TEST_CASE_BODY(require_throw_re)
+{
+ struct test {
+ const char *what;
+ bool ok;
+ const char *msg;
+ } *t, tests[] = {
+ { "throw_int", false, "unexpected error" },
+ { "throw_rt_match", true, NULL },
+ { "throw_rt_no_match", false,
+ "threw.*runtime_error\\(baz foo bar a\\).*"
+ "does not match 'foo\\.\\*baz'" },
+ { "no_throw_rt", false, "did not throw" },
+ { NULL, false, NULL }
+ };
+
+ const atf::fs::path before("before");
+ const atf::fs::path after("after");
+
+ for (t = &tests[0]; t->what != NULL; t++) {
+ atf::tests::vars_map config;
+ config["what"] = t->what;
+
+ std::cout << "Checking with " << t->what << " and expecting "
+ << (t->ok ? "true" : "false") << "\n";
+
+ ATF_TEST_CASE_USE(h_require_throw_re);
+ run_h_tc< ATF_TEST_CASE_NAME(h_require_throw_re) >(config);
+
+ ATF_REQUIRE(atf::fs::exists(before));
+ if (t->ok) {
+ ATF_REQUIRE(atf::utils::grep_file("^passed", "result"));
+ ATF_REQUIRE(atf::fs::exists(after));
+ } else {
+ std::cout << "Checking that message contains '" << t->msg
+ << "'\n";
+ std::string exp_result = std::string("^failed: .*") + t->msg;
+ ATF_REQUIRE(atf::utils::grep_file(exp_result.c_str(), "result"));
+ ATF_REQUIRE(!atf::fs::exists(after));
+ }
+
+ atf::fs::remove(before);
+ if (t->ok)
+ atf::fs::remove(after);
+ }
+}
+
+ATF_TEST_CASE(check_errno);
+ATF_TEST_CASE_HEAD(check_errno)
+{
+ set_md_var("descr", "Tests the ATF_CHECK_ERRNO macro");
+}
+ATF_TEST_CASE_BODY(check_errno)
+{
+ struct test {
+ const char *what;
+ bool ok;
+ const char *msg;
+ } *t, tests[] = {
+ { "no_error", false,
+ "Expected true value in errno_ok_stub\\(\\) == -1" },
+ { "errno_ok", true, NULL },
+ { "errno_fail", false,
+ "Expected errno 3, got 4, in errno_fail_stub\\(4\\) == -1" },
+ { NULL, false, NULL }
+ };
+
+ const atf::fs::path before("before");
+ const atf::fs::path after("after");
+
+ for (t = &tests[0]; t->what != NULL; t++) {
+ atf::tests::vars_map config;
+ config["what"] = t->what;
+
+ ATF_TEST_CASE_USE(h_check_errno);
+ run_h_tc< ATF_TEST_CASE_NAME(h_check_errno) >(config);
+
+ ATF_REQUIRE(atf::fs::exists(before));
+ ATF_REQUIRE(atf::fs::exists(after));
+
+ if (t->ok) {
+ ATF_REQUIRE(atf::utils::grep_file("^passed", "result"));
+ } else {
+ ATF_REQUIRE(atf::utils::grep_file("^failed", "result"));
+
+ std::string exp_result = "macros_test.cpp:[0-9]+: " +
+ std::string(t->msg) + "$";
+ ATF_REQUIRE(atf::utils::grep_file(exp_result.c_str(), "stderr"));
+ }
+
+ atf::fs::remove(before);
+ atf::fs::remove(after);
+ }
+}
+
+ATF_TEST_CASE(require_errno);
+ATF_TEST_CASE_HEAD(require_errno)
+{
+ set_md_var("descr", "Tests the ATF_REQUIRE_ERRNO macro");
+}
+ATF_TEST_CASE_BODY(require_errno)
+{
+ struct test {
+ const char *what;
+ bool ok;
+ const char *msg;
+ } *t, tests[] = {
+ { "no_error", false,
+ "Expected true value in errno_ok_stub\\(\\) == -1" },
+ { "errno_ok", true, NULL },
+ { "errno_fail", false,
+ "Expected errno 3, got 4, in errno_fail_stub\\(4\\) == -1" },
+ { NULL, false, NULL }
+ };
+
+ const atf::fs::path before("before");
+ const atf::fs::path after("after");
+
+ for (t = &tests[0]; t->what != NULL; t++) {
+ atf::tests::vars_map config;
+ config["what"] = t->what;
+
+ ATF_TEST_CASE_USE(h_require_errno);
+ run_h_tc< ATF_TEST_CASE_NAME(h_require_errno) >(config);
+
+ ATF_REQUIRE(atf::fs::exists(before));
+ if (t->ok) {
+ ATF_REQUIRE(atf::utils::grep_file("^passed", "result"));
+ ATF_REQUIRE(atf::fs::exists(after));
+ } else {
+ std::string exp_result = "^failed: .*macros_test.cpp:[0-9]+: " +
+ std::string(t->msg) + "$";
+ ATF_REQUIRE(atf::utils::grep_file(exp_result.c_str(), "result"));
+
+ ATF_REQUIRE(!atf::fs::exists(after));
+ }
+
+ atf::fs::remove(before);
+ if (t->ok)
+ atf::fs::remove(after);
+ }
+}
+
+// ------------------------------------------------------------------------
+// Tests cases for the header file.
+// ------------------------------------------------------------------------
+
+BUILD_TC(use, "macros_hpp_test.cpp",
+ "Tests that the macros provided by the atf-c++/macros.hpp file "
+ "do not cause syntax errors when used",
+ "Build of macros_hpp_test.cpp failed; some macros in "
+ "atf-c++/macros.hpp are broken");
+
+ATF_TEST_CASE(detect_unused_tests);
+ATF_TEST_CASE_HEAD(detect_unused_tests)
+{
+ set_md_var("descr",
+ "Tests that defining an unused test case raises a warning (and "
+ "thus an error)");
+}
+ATF_TEST_CASE_BODY(detect_unused_tests)
+{
+ const char* validate_compiler =
+ "class test_class { public: int dummy; };\n"
+ "#define define_unused static test_class unused\n"
+ "define_unused;\n";
+
+ atf::utils::create_file("compiler_test.cpp", validate_compiler);
+ if (build_check_cxx_o("compiler_test.cpp"))
+ expect_fail("Compiler does not raise a warning on an unused "
+ "static global variable declared by a macro");
+
+ if (build_check_cxx_o_srcdir(*this, "unused_test.cpp"))
+ ATF_FAIL("Build of unused_test.cpp passed; unused test cases are "
+ "not properly detected");
+}
+
+// ------------------------------------------------------------------------
+// Main.
+// ------------------------------------------------------------------------
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ // Add the test cases for the macros.
+ ATF_ADD_TEST_CASE(tcs, pass);
+ ATF_ADD_TEST_CASE(tcs, fail);
+ ATF_ADD_TEST_CASE(tcs, skip);
+ ATF_ADD_TEST_CASE(tcs, check_errno);
+ ATF_ADD_TEST_CASE(tcs, require);
+ ATF_ADD_TEST_CASE(tcs, require_eq);
+ ATF_ADD_TEST_CASE(tcs, require_in);
+ ATF_ADD_TEST_CASE(tcs, require_match);
+ ATF_ADD_TEST_CASE(tcs, require_not_in);
+ ATF_ADD_TEST_CASE(tcs, require_throw);
+ ATF_ADD_TEST_CASE(tcs, require_throw_re);
+ ATF_ADD_TEST_CASE(tcs, require_errno);
+
+ // Add the test cases for the header file.
+ ATF_ADD_TEST_CASE(tcs, use);
+ ATF_ADD_TEST_CASE(tcs, detect_unused_tests);
+}
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..af541f6
--- /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 atfc++.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=cxx atf-c++
+ cxx=$(cat stdout)
+ echo "Compiler is: ${cxx}"
+ atf_require_prog ${cxx}
+
+ cat >tp.cpp <<EOF
+#include <iostream>
+
+#include <atf-c++.hpp>
+
+ATF_TEST_CASE(tc);
+ATF_TEST_CASE_HEAD(tc) {
+ set_md_var("descr", "A test case");
+}
+ATF_TEST_CASE_BODY(tc) {
+ std::cout << "Running\n";
+}
+
+ATF_INIT_TEST_CASES(tcs) {
+ ATF_ADD_TEST_CASE(tcs, tc);
+}
+EOF
+
+ atf_check -s eq:0 -o save:stdout -e empty pkg-config --cflags atf-c++
+ cxxflags=$(cat stdout)
+ echo "CXXFLAGS are: ${cxxflags}"
+
+ 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 ${cxx} ${cxxflags} -o tp.o -c tp.cpp
+ atf_check -s eq:0 -o empty -e empty ${cxx} ${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++/tests.cpp b/unit/atf-src/atf-c++/tests.cpp
new file mode 100644
index 0000000..ede9609
--- /dev/null
+++ b/unit/atf-src/atf-c++/tests.cpp
@@ -0,0 +1,658 @@
+// 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++/tests.hpp"
+
+#if defined(HAVE_CONFIG_H)
+#include "config.h"
+#endif
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+}
+
+#include <algorithm>
+#include <cctype>
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <memory>
+#include <sstream>
+#include <stdexcept>
+#include <vector>
+
+extern "C" {
+#include "atf-c/error.h"
+#include "atf-c/tc.h"
+#include "atf-c/utils.h"
+}
+
+#include "atf-c++/detail/application.hpp"
+#include "atf-c++/detail/auto_array.hpp"
+#include "atf-c++/detail/env.hpp"
+#include "atf-c++/detail/exceptions.hpp"
+#include "atf-c++/detail/fs.hpp"
+#include "atf-c++/detail/sanity.hpp"
+#include "atf-c++/detail/text.hpp"
+
+#if defined(HAVE_GNU_GETOPT)
+# define GETOPT_POSIX "+"
+#else
+# define GETOPT_POSIX ""
+#endif
+
+namespace impl = atf::tests;
+namespace detail = atf::tests::detail;
+#define IMPL_NAME "atf::tests"
+
+using atf::application::usage_error;
+
+// ------------------------------------------------------------------------
+// The "atf_tp_writer" class.
+// ------------------------------------------------------------------------
+
+detail::atf_tp_writer::atf_tp_writer(std::ostream& os) :
+ m_os(os),
+ m_is_first(true)
+{
+ m_os << "Content-Type: application/X-atf-tp; version=\"1\"\n\n";
+}
+
+void
+detail::atf_tp_writer::start_tc(const std::string& ident)
+{
+ if (!m_is_first)
+ m_os << "\n";
+ m_os << "ident: " << ident << "\n";
+ m_os.flush();
+}
+
+void
+detail::atf_tp_writer::end_tc(void)
+{
+ if (m_is_first)
+ m_is_first = false;
+}
+
+void
+detail::atf_tp_writer::tc_meta_data(const std::string& name,
+ const std::string& value)
+{
+ PRE(name != "ident");
+ m_os << name << ": " << value << "\n";
+ m_os.flush();
+}
+
+// ------------------------------------------------------------------------
+// Free helper functions.
+// ------------------------------------------------------------------------
+
+std::string Program_Name;
+
+static void
+set_program_name(const char* argv0)
+{
+ const std::string program_name = atf::fs::path(argv0).leaf_name();
+ // 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 (program_name.substr(0, 3) == "lt-")
+ Program_Name = program_name.substr(3);
+ else
+ Program_Name = program_name;
+}
+
+bool
+detail::match(const std::string& regexp, const std::string& str)
+{
+ return atf::text::match(str, regexp);
+}
+
+// ------------------------------------------------------------------------
+// The "tc" class.
+// ------------------------------------------------------------------------
+
+static std::map< atf_tc_t*, impl::tc* > wraps;
+static std::map< const atf_tc_t*, const impl::tc* > cwraps;
+
+struct impl::tc_impl {
+private:
+ // Non-copyable.
+ tc_impl(const tc_impl&);
+ tc_impl& operator=(const tc_impl&);
+
+public:
+ std::string m_ident;
+ atf_tc_t m_tc;
+ bool m_has_cleanup;
+
+ tc_impl(const std::string& ident, const bool has_cleanup) :
+ m_ident(ident),
+ m_has_cleanup(has_cleanup)
+ {
+ }
+
+ static void
+ wrap_head(atf_tc_t *tc)
+ {
+ std::map< atf_tc_t*, impl::tc* >::iterator iter = wraps.find(tc);
+ INV(iter != wraps.end());
+ (*iter).second->head();
+ }
+
+ static void
+ wrap_body(const atf_tc_t *tc)
+ {
+ std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
+ cwraps.find(tc);
+ INV(iter != cwraps.end());
+ (*iter).second->body();
+ }
+
+ static void
+ wrap_cleanup(const atf_tc_t *tc)
+ {
+ std::map< const atf_tc_t*, const impl::tc* >::const_iterator iter =
+ cwraps.find(tc);
+ INV(iter != cwraps.end());
+ (*iter).second->cleanup();
+ }
+};
+
+impl::tc::tc(const std::string& ident, const bool has_cleanup) :
+ pimpl(new tc_impl(ident, has_cleanup))
+{
+}
+
+impl::tc::~tc(void)
+{
+ cwraps.erase(&pimpl->m_tc);
+ wraps.erase(&pimpl->m_tc);
+
+ atf_tc_fini(&pimpl->m_tc);
+}
+
+void
+impl::tc::init(const vars_map& config)
+{
+ atf_error_t err;
+
+ auto_array< const char * > array(new const char*[(config.size() * 2) + 1]);
+ const char **ptr = array.get();
+ for (vars_map::const_iterator iter = config.begin();
+ iter != config.end(); iter++) {
+ *ptr = (*iter).first.c_str();
+ *(ptr + 1) = (*iter).second.c_str();
+ ptr += 2;
+ }
+ *ptr = NULL;
+
+ wraps[&pimpl->m_tc] = this;
+ cwraps[&pimpl->m_tc] = this;
+
+ err = atf_tc_init(&pimpl->m_tc, pimpl->m_ident.c_str(), pimpl->wrap_head,
+ pimpl->wrap_body, pimpl->m_has_cleanup ? pimpl->wrap_cleanup : NULL,
+ array.get());
+ if (atf_is_error(err))
+ throw_atf_error(err);
+}
+
+bool
+impl::tc::has_config_var(const std::string& var)
+ const
+{
+ return atf_tc_has_config_var(&pimpl->m_tc, var.c_str());
+}
+
+bool
+impl::tc::has_md_var(const std::string& var)
+ const
+{
+ return atf_tc_has_md_var(&pimpl->m_tc, var.c_str());
+}
+
+const std::string
+impl::tc::get_config_var(const std::string& var)
+ const
+{
+ return atf_tc_get_config_var(&pimpl->m_tc, var.c_str());
+}
+
+const std::string
+impl::tc::get_config_var(const std::string& var, const std::string& defval)
+ const
+{
+ return atf_tc_get_config_var_wd(&pimpl->m_tc, var.c_str(), defval.c_str());
+}
+
+const std::string
+impl::tc::get_md_var(const std::string& var)
+ const
+{
+ return atf_tc_get_md_var(&pimpl->m_tc, var.c_str());
+}
+
+const impl::vars_map
+impl::tc::get_md_vars(void)
+ const
+{
+ vars_map vars;
+
+ char **array = atf_tc_get_md_vars(&pimpl->m_tc);
+ try {
+ char **ptr;
+ for (ptr = array; *ptr != NULL; ptr += 2)
+ vars[*ptr] = *(ptr + 1);
+ } catch (...) {
+ atf_utils_free_charpp(array);
+ throw;
+ }
+
+ return vars;
+}
+
+void
+impl::tc::set_md_var(const std::string& var, const std::string& val)
+{
+ atf_error_t err = atf_tc_set_md_var(&pimpl->m_tc, var.c_str(), val.c_str());
+ if (atf_is_error(err))
+ throw_atf_error(err);
+}
+
+void
+impl::tc::run(const std::string& resfile)
+ const
+{
+ atf_error_t err = atf_tc_run(&pimpl->m_tc, resfile.c_str());
+ if (atf_is_error(err))
+ throw_atf_error(err);
+}
+
+void
+impl::tc::run_cleanup(void)
+ const
+{
+ atf_error_t err = atf_tc_cleanup(&pimpl->m_tc);
+ if (atf_is_error(err))
+ throw_atf_error(err);
+}
+
+void
+impl::tc::head(void)
+{
+}
+
+void
+impl::tc::cleanup(void)
+ const
+{
+}
+
+void
+impl::tc::require_prog(const std::string& prog)
+ const
+{
+ atf_tc_require_prog(prog.c_str());
+}
+
+void
+impl::tc::pass(void)
+{
+ atf_tc_pass();
+}
+
+void
+impl::tc::fail(const std::string& reason)
+{
+ atf_tc_fail("%s", reason.c_str());
+}
+
+void
+impl::tc::fail_nonfatal(const std::string& reason)
+{
+ atf_tc_fail_nonfatal("%s", reason.c_str());
+}
+
+void
+impl::tc::skip(const std::string& reason)
+{
+ atf_tc_skip("%s", reason.c_str());
+}
+
+void
+impl::tc::check_errno(const char* file, const int line, const int exp_errno,
+ const char* expr_str, const bool result)
+{
+ atf_tc_check_errno(file, line, exp_errno, expr_str, result);
+}
+
+void
+impl::tc::require_errno(const char* file, const int line, const int exp_errno,
+ const char* expr_str, const bool result)
+{
+ atf_tc_require_errno(file, line, exp_errno, expr_str, result);
+}
+
+void
+impl::tc::expect_pass(void)
+{
+ atf_tc_expect_pass();
+}
+
+void
+impl::tc::expect_fail(const std::string& reason)
+{
+ atf_tc_expect_fail("%s", reason.c_str());
+}
+
+void
+impl::tc::expect_exit(const int exitcode, const std::string& reason)
+{
+ atf_tc_expect_exit(exitcode, "%s", reason.c_str());
+}
+
+void
+impl::tc::expect_signal(const int signo, const std::string& reason)
+{
+ atf_tc_expect_signal(signo, "%s", reason.c_str());
+}
+
+void
+impl::tc::expect_death(const std::string& reason)
+{
+ atf_tc_expect_death("%s", reason.c_str());
+}
+
+void
+impl::tc::expect_timeout(const std::string& reason)
+{
+ atf_tc_expect_timeout("%s", reason.c_str());
+}
+
+// ------------------------------------------------------------------------
+// Test program main code.
+// ------------------------------------------------------------------------
+
+namespace {
+
+typedef std::vector< impl::tc * > tc_vector;
+
+enum tc_part { BODY, CLEANUP };
+
+static void
+parse_vflag(const std::string& str, atf::tests::vars_map& vars)
+{
+ if (str.empty())
+ throw std::runtime_error("-v requires a non-empty argument");
+
+ std::vector< std::string > ws = atf::text::split(str, "=");
+ if (ws.size() == 1 && str[str.length() - 1] == '=') {
+ vars[ws[0]] = "";
+ } else {
+ if (ws.size() != 2)
+ throw std::runtime_error("-v requires an argument of the form "
+ "var=value");
+
+ vars[ws[0]] = ws[1];
+ }
+}
+
+static atf::fs::path
+handle_srcdir(const char* argv0, const std::string& srcdir_arg)
+{
+ atf::fs::path srcdir(".");
+
+ if (srcdir_arg.empty()) {
+ srcdir = atf::fs::path(argv0).branch_path();
+ if (srcdir.leaf_name() == ".libs")
+ srcdir = srcdir.branch_path();
+ } else
+ srcdir = atf::fs::path(srcdir_arg);
+
+ if (!atf::fs::exists(srcdir / Program_Name))
+ throw usage_error("Cannot find the test program in the source "
+ "directory `%s'", srcdir.c_str());
+
+ if (!srcdir.is_absolute())
+ srcdir = srcdir.to_absolute();
+
+ return srcdir;
+}
+
+static void
+init_tcs(void (*add_tcs)(tc_vector&), tc_vector& tcs,
+ const atf::tests::vars_map& vars)
+{
+ add_tcs(tcs);
+ for (tc_vector::iterator iter = tcs.begin(); iter != tcs.end(); iter++) {
+ impl::tc* tc = *iter;
+
+ tc->init(vars);
+ }
+}
+
+static int
+list_tcs(const tc_vector& tcs)
+{
+ detail::atf_tp_writer writer(std::cout);
+
+ for (tc_vector::const_iterator iter = tcs.begin();
+ iter != tcs.end(); iter++) {
+ const impl::vars_map vars = (*iter)->get_md_vars();
+
+ {
+ impl::vars_map::const_iterator iter2 = vars.find("ident");
+ INV(iter2 != vars.end());
+ writer.start_tc((*iter2).second);
+ }
+
+ for (impl::vars_map::const_iterator iter2 = vars.begin();
+ iter2 != vars.end(); iter2++) {
+ const std::string& key = (*iter2).first;
+ if (key != "ident")
+ writer.tc_meta_data(key, (*iter2).second);
+ }
+
+ writer.end_tc();
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static impl::tc*
+find_tc(tc_vector tcs, const std::string& name)
+{
+ std::vector< std::string > ids;
+ for (tc_vector::iterator iter = tcs.begin();
+ iter != tcs.end(); iter++) {
+ impl::tc* tc = *iter;
+
+ if (tc->get_md_var("ident") == name)
+ return tc;
+ }
+ throw usage_error("Unknown test case `%s'", name.c_str());
+}
+
+static std::pair< std::string, tc_part >
+process_tcarg(const std::string& tcarg)
+{
+ const std::string::size_type pos = tcarg.find(':');
+ if (pos == std::string::npos) {
+ return std::make_pair(tcarg, BODY);
+ } else {
+ const std::string tcname = tcarg.substr(0, pos);
+
+ const std::string partname = tcarg.substr(pos + 1);
+ if (partname == "body")
+ return std::make_pair(tcname, BODY);
+ else if (partname == "cleanup")
+ return std::make_pair(tcname, CLEANUP);
+ else {
+ throw usage_error("Invalid test case part `%s'", partname.c_str());
+ }
+ }
+}
+
+static int
+run_tc(tc_vector& tcs, const std::string& tcarg, const atf::fs::path& resfile)
+{
+ const std::pair< std::string, tc_part > fields = process_tcarg(tcarg);
+
+ impl::tc* tc = find_tc(tcs, fields.first);
+
+ if (!atf::env::has("__RUNNING_INSIDE_ATF_RUN") || atf::env::get(
+ "__RUNNING_INSIDE_ATF_RUN") != "internal-yes-value")
+ {
+ std::cerr << Program_Name << ": WARNING: Running test cases outside "
+ "of kyua(1) is unsupported\n";
+ std::cerr << Program_Name << ": WARNING: No isolation nor timeout "
+ "control is being applied; you may get unexpected failures; see "
+ "atf-test-case(4)\n";
+ }
+
+ switch (fields.second) {
+ case BODY:
+ tc->run(resfile.str());
+ break;
+ case CLEANUP:
+ tc->run_cleanup();
+ break;
+ default:
+ UNREACHABLE;
+ }
+ return EXIT_SUCCESS;
+}
+
+static int
+safe_main(int argc, char** argv, void (*add_tcs)(tc_vector&))
+{
+ const char* argv0 = argv[0];
+
+ bool lflag = false;
+ atf::fs::path resfile("/dev/stdout");
+ std::string srcdir_arg;
+ atf::tests::vars_map vars;
+
+ int ch;
+ int old_opterr;
+
+ old_opterr = opterr;
+ ::opterr = 0;
+ while ((ch = ::getopt(argc, argv, GETOPT_POSIX ":lr:s:v:")) != -1) {
+ switch (ch) {
+ case 'l':
+ lflag = true;
+ break;
+
+ case 'r':
+ resfile = atf::fs::path(::optarg);
+ break;
+
+ case 's':
+ srcdir_arg = ::optarg;
+ break;
+
+ case 'v':
+ parse_vflag(::optarg, vars);
+ break;
+
+ case ':':
+ throw usage_error("Option -%c requires an argument.", ::optopt);
+ break;
+
+ case '?':
+ default:
+ throw 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
+
+ vars["srcdir"] = handle_srcdir(argv0, srcdir_arg).str();
+
+ int errcode;
+
+ tc_vector tcs;
+ if (lflag) {
+ if (argc > 0)
+ throw usage_error("Cannot provide test case names with -l");
+
+ init_tcs(add_tcs, tcs, vars);
+ errcode = list_tcs(tcs);
+ } else {
+ if (argc == 0)
+ throw usage_error("Must provide a test case name");
+ else if (argc > 1)
+ throw usage_error("Cannot provide more than one test case name");
+ INV(argc == 1);
+
+ init_tcs(add_tcs, tcs, vars);
+ errcode = run_tc(tcs, argv[0], resfile);
+ }
+ for (tc_vector::iterator iter = tcs.begin(); iter != tcs.end(); iter++) {
+ impl::tc* tc = *iter;
+
+ delete tc;
+ }
+
+ return errcode;
+}
+
+} // anonymous namespace
+
+namespace atf {
+ namespace tests {
+ int run_tp(int, char**, void (*)(tc_vector&));
+ }
+}
+
+int
+impl::run_tp(int argc, char** argv, void (*add_tcs)(tc_vector&))
+{
+ try {
+ set_program_name(argv[0]);
+ return ::safe_main(argc, argv, add_tcs);
+ } catch (const usage_error& e) {
+ std::cerr
+ << Program_Name << ": ERROR: " << e.what() << '\n'
+ << Program_Name << ": See atf-test-program(1) for usage details.\n";
+ return EXIT_FAILURE;
+ }
+}
diff --git a/unit/atf-src/atf-c++/tests.hpp b/unit/atf-src/atf-c++/tests.hpp
new file mode 100644
index 0000000..ce2fb1d
--- /dev/null
+++ b/unit/atf-src/atf-c++/tests.hpp
@@ -0,0 +1,125 @@
+// 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_CXX_TESTS_HPP)
+#define ATF_CXX_TESTS_HPP
+
+#include <map>
+#include <memory>
+#include <string>
+
+extern "C" {
+#include <atf-c/defs.h>
+}
+
+namespace atf {
+namespace tests {
+
+namespace detail {
+
+class atf_tp_writer {
+ std::ostream& m_os;
+
+ bool m_is_first;
+
+public:
+ atf_tp_writer(std::ostream&);
+
+ void start_tc(const std::string&);
+ void end_tc(void);
+ void tc_meta_data(const std::string&, const std::string&);
+};
+
+bool match(const std::string&, const std::string&);
+
+} // namespace
+
+// ------------------------------------------------------------------------
+// The "vars_map" class.
+// ------------------------------------------------------------------------
+
+typedef std::map< std::string, std::string > vars_map;
+
+// ------------------------------------------------------------------------
+// The "tc" class.
+// ------------------------------------------------------------------------
+
+struct tc_impl;
+
+class tc {
+ // Non-copyable.
+ tc(const tc&);
+ tc& operator=(const tc&);
+
+ std::auto_ptr< tc_impl > pimpl;
+
+protected:
+ virtual void head(void);
+ virtual void body(void) const = 0;
+ virtual void cleanup(void) const;
+
+ void require_prog(const std::string&) const;
+
+ friend struct tc_impl;
+
+public:
+ tc(const std::string&, const bool);
+ virtual ~tc(void);
+
+ void init(const vars_map&);
+
+ const std::string get_config_var(const std::string&) const;
+ const std::string get_config_var(const std::string&, const std::string&)
+ const;
+ const std::string get_md_var(const std::string&) const;
+ const vars_map get_md_vars(void) const;
+ bool has_config_var(const std::string&) const;
+ bool has_md_var(const std::string&) const;
+ void set_md_var(const std::string&, const std::string&);
+
+ void run(const std::string&) const;
+ void run_cleanup(void) const;
+
+ // To be called from the child process only.
+ static void pass(void) ATF_DEFS_ATTRIBUTE_NORETURN;
+ static void fail(const std::string&) ATF_DEFS_ATTRIBUTE_NORETURN;
+ static void fail_nonfatal(const std::string&);
+ static void skip(const std::string&) ATF_DEFS_ATTRIBUTE_NORETURN;
+ static void check_errno(const char*, const int, const int, const char*,
+ const bool);
+ static void require_errno(const char*, const int, const int, const char*,
+ const bool);
+ static void expect_pass(void);
+ static void expect_fail(const std::string&);
+ static void expect_exit(const int, const std::string&);
+ static void expect_signal(const int, const std::string&);
+ static void expect_death(const std::string&);
+ static void expect_timeout(const std::string&);
+};
+
+} // namespace tests
+} // namespace atf
+
+#endif // !defined(ATF_CXX_TESTS_HPP)
diff --git a/unit/atf-src/atf-c++/tests_test.cpp b/unit/atf-src/atf-c++/tests_test.cpp
new file mode 100644
index 0000000..ed9fef0
--- /dev/null
+++ b/unit/atf-src/atf-c++/tests_test.cpp
@@ -0,0 +1,190 @@
+// 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++/tests.hpp"
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+#include <fstream>
+#include <iostream>
+#include <sstream>
+
+#include <atf-c++.hpp>
+
+#include "atf-c++/detail/text.hpp"
+
+// ------------------------------------------------------------------------
+// Tests for the "atf_tp_writer" class.
+// ------------------------------------------------------------------------
+
+static
+void
+print_indented(const std::string& str)
+{
+ std::vector< std::string > ws = atf::text::split(str, "\n");
+ for (std::vector< std::string >::const_iterator iter = ws.begin();
+ iter != ws.end(); iter++)
+ std::cout << ">>" << *iter << "<<\n";
+}
+
+// XXX Should this string handling and verbosity level be part of the
+// ATF_REQUIRE_EQ macro? It may be hard to predict sometimes that a
+// string can have newlines in it, and so the error message generated
+// at the moment will be bogus if there are some.
+static
+void
+check_equal(const atf::tests::tc& tc, const std::string& str,
+ const std::string& exp)
+{
+ if (str != exp) {
+ std::cout << "String equality check failed.\n"
+ "Adding >> and << to delimit the string boundaries below.\n";
+ std::cout << "GOT:\n";
+ print_indented(str);
+ std::cout << "EXPECTED:\n";
+ print_indented(exp);
+ tc.fail("Constructed string differs from the expected one");
+ }
+}
+
+ATF_TEST_CASE(atf_tp_writer);
+ATF_TEST_CASE_HEAD(atf_tp_writer)
+{
+ set_md_var("descr", "Verifies the application/X-atf-tp writer");
+}
+ATF_TEST_CASE_BODY(atf_tp_writer)
+{
+ std::ostringstream expss;
+ std::ostringstream ss;
+
+#define RESET \
+ expss.str(""); \
+ ss.str("")
+
+#define CHECK \
+ check_equal(*this, ss.str(), expss.str())
+
+ {
+ RESET;
+
+ atf::tests::detail::atf_tp_writer w(ss);
+ expss << "Content-Type: application/X-atf-tp; version=\"1\"\n\n";
+ CHECK;
+ }
+
+ {
+ RESET;
+
+ atf::tests::detail::atf_tp_writer w(ss);
+ expss << "Content-Type: application/X-atf-tp; version=\"1\"\n\n";
+ CHECK;
+
+ w.start_tc("test1");
+ expss << "ident: test1\n";
+ CHECK;
+
+ w.end_tc();
+ CHECK;
+ }
+
+ {
+ RESET;
+
+ atf::tests::detail::atf_tp_writer w(ss);
+ expss << "Content-Type: application/X-atf-tp; version=\"1\"\n\n";
+ CHECK;
+
+ w.start_tc("test1");
+ expss << "ident: test1\n";
+ CHECK;
+
+ w.end_tc();
+ CHECK;
+
+ w.start_tc("test2");
+ expss << "\nident: test2\n";
+ CHECK;
+
+ w.end_tc();
+ CHECK;
+ }
+
+ {
+ RESET;
+
+ atf::tests::detail::atf_tp_writer w(ss);
+ expss << "Content-Type: application/X-atf-tp; version=\"1\"\n\n";
+ CHECK;
+
+ w.start_tc("test1");
+ expss << "ident: test1\n";
+ CHECK;
+
+ w.tc_meta_data("descr", "the description");
+ expss << "descr: the description\n";
+ CHECK;
+
+ w.end_tc();
+ CHECK;
+
+ w.start_tc("test2");
+ expss << "\nident: test2\n";
+ CHECK;
+
+ w.tc_meta_data("descr", "second test case");
+ expss << "descr: second test case\n";
+ CHECK;
+
+ w.tc_meta_data("require.progs", "/bin/cp");
+ expss << "require.progs: /bin/cp\n";
+ CHECK;
+
+ w.tc_meta_data("X-custom", "foo bar baz");
+ expss << "X-custom: foo bar baz\n";
+ CHECK;
+
+ w.end_tc();
+ CHECK;
+ }
+
+#undef CHECK
+#undef RESET
+}
+
+// ------------------------------------------------------------------------
+// Main.
+// ------------------------------------------------------------------------
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ // Add tests for the "atf_tp_writer" class.
+ ATF_ADD_TEST_CASE(tcs, atf_tp_writer);
+}
diff --git a/unit/atf-src/atf-c++/unused_test.cpp b/unit/atf-src/atf-c++/unused_test.cpp
new file mode 100644
index 0000000..1a577e1
--- /dev/null
+++ b/unit/atf-src/atf-c++/unused_test.cpp
@@ -0,0 +1,48 @@
+// 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++.hpp>
+
+ATF_TEST_CASE(this_is_used);
+ATF_TEST_CASE_HEAD(this_is_used)
+{
+}
+ATF_TEST_CASE_BODY(this_is_used)
+{
+}
+
+ATF_TEST_CASE(this_is_unused);
+ATF_TEST_CASE_HEAD(this_is_unused)
+{
+}
+ATF_TEST_CASE_BODY(this_is_unused)
+{
+}
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ ATF_ADD_TEST_CASE(tcs, this_is_used);
+ //ATF_ADD_TEST_CASE(tcs, this_is_unused);
+}
diff --git a/unit/atf-src/atf-c++/utils.cpp b/unit/atf-src/atf-c++/utils.cpp
new file mode 100644
index 0000000..a6ab08f
--- /dev/null
+++ b/unit/atf-src/atf-c++/utils.cpp
@@ -0,0 +1,100 @@
+// 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++/utils.hpp"
+
+extern "C" {
+#include "atf-c/utils.h"
+}
+
+#include <cstdlib>
+#include <iostream>
+
+void
+atf::utils::cat_file(const std::string& path, const std::string& prefix)
+{
+ atf_utils_cat_file(path.c_str(), prefix.c_str());
+}
+
+void
+atf::utils::copy_file(const std::string& source, const std::string& destination)
+{
+ atf_utils_copy_file(source.c_str(), destination.c_str());
+}
+
+bool
+atf::utils::compare_file(const std::string& path, const std::string& contents)
+{
+ return atf_utils_compare_file(path.c_str(), contents.c_str());
+}
+
+void
+atf::utils::create_file(const std::string& path, const std::string& contents)
+{
+ atf_utils_create_file(path.c_str(), "%s", contents.c_str());
+}
+
+bool
+atf::utils::file_exists(const std::string& path)
+{
+ return atf_utils_file_exists(path.c_str());
+}
+
+pid_t
+atf::utils::fork(void)
+{
+ std::cout.flush();
+ std::cerr.flush();
+ return atf_utils_fork();
+}
+
+bool
+atf::utils::grep_file(const std::string& regex, const std::string& path)
+{
+ return atf_utils_grep_file("%s", path.c_str(), regex.c_str());
+}
+
+bool
+atf::utils::grep_string(const std::string& regex, const std::string& str)
+{
+ return atf_utils_grep_string("%s", str.c_str(), regex.c_str());
+}
+
+void
+atf::utils::redirect(const int fd, const std::string& path)
+{
+ if (fd == STDOUT_FILENO)
+ std::cout.flush();
+ else if (fd == STDERR_FILENO)
+ std::cerr.flush();
+ atf_utils_redirect(fd, path.c_str());
+}
+
+void
+atf::utils::wait(const pid_t pid, const int exitstatus,
+ const std::string& expout, const std::string& experr)
+{
+ atf_utils_wait(pid, exitstatus, expout.c_str(), experr.c_str());
+}
diff --git a/unit/atf-src/atf-c++/utils.hpp b/unit/atf-src/atf-c++/utils.hpp
new file mode 100644
index 0000000..8f5c5e3
--- /dev/null
+++ b/unit/atf-src/atf-c++/utils.hpp
@@ -0,0 +1,64 @@
+// 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_CXX_UTILS_HPP)
+#define ATF_CXX_UTILS_HPP
+
+extern "C" {
+#include <unistd.h>
+}
+
+#include <string>
+
+namespace atf {
+namespace utils {
+
+void cat_file(const std::string&, const std::string&);
+bool compare_file(const std::string&, const std::string&);
+void copy_file(const std::string&, const std::string&);
+void create_file(const std::string&, const std::string&);
+bool file_exists(const std::string&);
+pid_t fork(void);
+bool grep_file(const std::string&, const std::string&);
+bool grep_string(const std::string&, const std::string&);
+void redirect(const int, const std::string&);
+void wait(const pid_t, const int, const std::string&, const std::string&);
+
+template< typename Collection >
+bool
+grep_collection(const std::string& regexp, const Collection& collection)
+{
+ for (typename Collection::const_iterator iter = collection.begin();
+ iter != collection.end(); ++iter) {
+ if (grep_string(regexp, *iter))
+ return true;
+ }
+ return false;
+}
+
+} // namespace utils
+} // namespace atf
+
+#endif // !defined(ATF_CXX_UTILS_HPP)
diff --git a/unit/atf-src/atf-c++/utils_test.cpp b/unit/atf-src/atf-c++/utils_test.cpp
new file mode 100644
index 0000000..34e0709
--- /dev/null
+++ b/unit/atf-src/atf-c++/utils_test.cpp
@@ -0,0 +1,509 @@
+// 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++/utils.hpp"
+
+extern "C" {
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+#include <cstdlib>
+#include <iostream>
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <atf-c++.hpp>
+
+static std::string
+read_file(const std::string& path)
+{
+ char buffer[1024];
+
+ const int fd = open(path.c_str(), O_RDONLY);
+ if (fd == -1)
+ ATF_FAIL("Cannot open " + path);
+ const ssize_t length = read(fd, buffer, sizeof(buffer) - 1);
+ close(fd);
+ ATF_REQUIRE(length != -1);
+ if (length == sizeof(buffer) - 1)
+ ATF_FAIL("Internal buffer not long enough to read temporary file");
+ ((char *)buffer)[length] = '\0';
+
+ return buffer;
+}
+
+// ------------------------------------------------------------------------
+// Tests cases for the free functions.
+// ------------------------------------------------------------------------
+
+ATF_TEST_CASE_WITHOUT_HEAD(cat_file__empty);
+ATF_TEST_CASE_BODY(cat_file__empty)
+{
+ atf::utils::create_file("file.txt", "");
+ atf::utils::redirect(STDOUT_FILENO, "captured.txt");
+ atf::utils::cat_file("file.txt", "PREFIX");
+ std::cout.flush();
+ close(STDOUT_FILENO);
+
+ ATF_REQUIRE_EQ("", read_file("captured.txt"));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(cat_file__one_line);
+ATF_TEST_CASE_BODY(cat_file__one_line)
+{
+ 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");
+ std::cout.flush();
+ close(STDOUT_FILENO);
+
+ ATF_REQUIRE_EQ("PREFIXThis is a single line\n", read_file("captured.txt"));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(cat_file__several_lines);
+ATF_TEST_CASE_BODY(cat_file__several_lines)
+{
+ 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", ">");
+ std::cout.flush();
+ close(STDOUT_FILENO);
+
+ ATF_REQUIRE_EQ(">First\n>Second line\n>And third\n",
+ read_file("captured.txt"));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(cat_file__no_newline_eof);
+ATF_TEST_CASE_BODY(cat_file__no_newline_eof)
+{
+ atf::utils::create_file("file.txt", "Foo\n bar baz");
+ atf::utils::redirect(STDOUT_FILENO, "captured.txt");
+ atf::utils::cat_file("file.txt", "PREFIX");
+ std::cout.flush();
+ close(STDOUT_FILENO);
+
+ ATF_REQUIRE_EQ("PREFIXFoo\nPREFIX bar baz", read_file("captured.txt"));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(compare_file__empty__match);
+ATF_TEST_CASE_BODY(compare_file__empty__match)
+{
+ atf::utils::create_file("test.txt", "");
+ ATF_REQUIRE(atf::utils::compare_file("test.txt", ""));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(compare_file__empty__not_match);
+ATF_TEST_CASE_BODY(compare_file__empty__not_match)
+{
+ atf::utils::create_file("test.txt", "");
+ 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_TEST_CASE_WITHOUT_HEAD(compare_file__short__match);
+ATF_TEST_CASE_BODY(compare_file__short__match)
+{
+ 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_TEST_CASE_WITHOUT_HEAD(compare_file__short__not_match);
+ATF_TEST_CASE_BODY(compare_file__short__not_match)
+{
+ 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_TEST_CASE_WITHOUT_HEAD(compare_file__long__match);
+ATF_TEST_CASE_BODY(compare_file__long__match)
+{
+ 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", long_contents);
+
+ ATF_REQUIRE(atf::utils::compare_file("test.txt", long_contents));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(compare_file__long__not_match);
+ATF_TEST_CASE_BODY(compare_file__long__not_match)
+{
+ 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", 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_TEST_CASE_WITHOUT_HEAD(copy_file__empty);
+ATF_TEST_CASE_BODY(copy_file__empty)
+{
+ atf::utils::create_file("src.txt", "");
+ 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_TEST_CASE_WITHOUT_HEAD(copy_file__some_contents);
+ATF_TEST_CASE_BODY(copy_file__some_contents)
+{
+ 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_TEST_CASE_WITHOUT_HEAD(create_file);
+ATF_TEST_CASE_BODY(create_file)
+{
+ atf::utils::create_file("test.txt", "This is a %d test");
+
+ ATF_REQUIRE_EQ("This is a %d test", read_file("test.txt"));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(file_exists);
+ATF_TEST_CASE_BODY(file_exists)
+{
+ 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_TEST_CASE_WITHOUT_HEAD(fork);
+ATF_TEST_CASE_BODY(fork)
+{
+ std::cout << "Should not get into child\n";
+ std::cerr << "Should not get into child\n";
+ pid_t pid = atf::utils::fork();
+ if (pid == 0) {
+ std::cout << "Child stdout\n";
+ std::cerr << "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));
+
+ std::ostringstream out_name;
+ out_name << "atf_utils_fork_" << pid << "_out.txt";
+ std::ostringstream err_name;
+ err_name << "atf_utils_fork_" << pid << "_err.txt";
+
+ ATF_REQUIRE_EQ("Child stdout\n", read_file(out_name.str()));
+ ATF_REQUIRE_EQ("Child stderr\n", read_file(err_name.str()));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(grep_collection__set);
+ATF_TEST_CASE_BODY(grep_collection__set)
+{
+ std::set< std::string > strings;
+ strings.insert("First");
+ strings.insert("Second");
+
+ ATF_REQUIRE( atf::utils::grep_collection("irs", strings));
+ ATF_REQUIRE( atf::utils::grep_collection("cond", strings));
+ ATF_REQUIRE(!atf::utils::grep_collection("Third", strings));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(grep_collection__vector);
+ATF_TEST_CASE_BODY(grep_collection__vector)
+{
+ std::vector< std::string > strings;
+ strings.push_back("First");
+ strings.push_back("Second");
+
+ ATF_REQUIRE( atf::utils::grep_collection("irs", strings));
+ ATF_REQUIRE( atf::utils::grep_collection("cond", strings));
+ ATF_REQUIRE(!atf::utils::grep_collection("Third", strings));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(grep_file);
+ATF_TEST_CASE_BODY(grep_file)
+{
+ atf::utils::create_file("test.txt", "line1\nthe second line\naaaabbbb\n");
+
+ ATF_REQUIRE(atf::utils::grep_file("line1", "test.txt"));
+ ATF_REQUIRE(atf::utils::grep_file("second line", "test.txt"));
+ ATF_REQUIRE(atf::utils::grep_file("aa.*bb", "test.txt"));
+ ATF_REQUIRE(!atf::utils::grep_file("foo", "test.txt"));
+ ATF_REQUIRE(!atf::utils::grep_file("bar", "test.txt"));
+ ATF_REQUIRE(!atf::utils::grep_file("aaaaa", "test.txt"));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(grep_string);
+ATF_TEST_CASE_BODY(grep_string)
+{
+ const char *str = "a string - aaaabbbb";
+ ATF_REQUIRE(atf::utils::grep_string("a string", str));
+ ATF_REQUIRE(atf::utils::grep_string("^a string", str));
+ ATF_REQUIRE(atf::utils::grep_string("aaaabbbb$", str));
+ ATF_REQUIRE(atf::utils::grep_string("aa.*bb", str));
+ ATF_REQUIRE(!atf::utils::grep_string("foo", str));
+ ATF_REQUIRE(!atf::utils::grep_string("bar", str));
+ ATF_REQUIRE(!atf::utils::grep_string("aaaaa", str));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(redirect__stdout);
+ATF_TEST_CASE_BODY(redirect__stdout)
+{
+ std::cout << "Buffer this";
+ atf::utils::redirect(STDOUT_FILENO, "captured.txt");
+ std::cout << "The printed message";
+ std::cout.flush();
+
+ ATF_REQUIRE_EQ("The printed message", read_file("captured.txt"));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(redirect__stderr);
+ATF_TEST_CASE_BODY(redirect__stderr)
+{
+ std::cerr << "Buffer this";
+ atf::utils::redirect(STDERR_FILENO, "captured.txt");
+ std::cerr << "The printed message";
+ std::cerr.flush();
+
+ ATF_REQUIRE_EQ("The printed message", read_file("captured.txt"));
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(redirect__other);
+ATF_TEST_CASE_BODY(redirect__other)
+{
+ const std::string message = "Foo bar\nbaz\n";
+ atf::utils::redirect(15, "captured.txt");
+ ATF_REQUIRE(write(15, message.c_str(), message.length()) != -1);
+ close(15);
+
+ ATF_REQUIRE_EQ(message, read_file("captured.txt"));
+}
+
+static void
+fork_and_wait(const int exitstatus, const char* expout, const char* experr)
+{
+ const pid_t pid = atf::utils::fork();
+ if (pid == 0) {
+ std::cout << "Some output\n";
+ std::cerr << "Some error\n";
+ exit(123);
+ }
+ atf::utils::wait(pid, exitstatus, expout, experr);
+ exit(EXIT_SUCCESS);
+}
+
+ATF_TEST_CASE_WITHOUT_HEAD(wait__ok);
+ATF_TEST_CASE_BODY(wait__ok)
+{
+ 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_TEST_CASE_WITHOUT_HEAD(wait__ok_nested);
+ATF_TEST_CASE_BODY(wait__ok_nested)
+{
+ 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) {
+ std::cerr.flush();
+ std::cout << "Child output\n";
+ std::cout.flush();
+ std::cerr << "Child error\n";
+ std::exit(50);
+ } else {
+ std::cout << "Parent output\n";
+ std::cerr << "Parent error\n";
+ atf::utils::wait(child, 50, "Child output\n", "Child error\n");
+ std::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_TEST_CASE_WITHOUT_HEAD(wait__invalid_exitstatus);
+ATF_TEST_CASE_BODY(wait__invalid_exitstatus)
+{
+ 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_TEST_CASE_WITHOUT_HEAD(wait__invalid_stdout);
+ATF_TEST_CASE_BODY(wait__invalid_stdout)
+{
+ 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_TEST_CASE_WITHOUT_HEAD(wait__invalid_stderr);
+ATF_TEST_CASE_BODY(wait__invalid_stderr)
+{
+ 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_TEST_CASE_WITHOUT_HEAD(wait__save_stdout);
+ATF_TEST_CASE_BODY(wait__save_stdout)
+{
+ 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_TEST_CASE_WITHOUT_HEAD(wait__save_stderr);
+ATF_TEST_CASE_BODY(wait__save_stderr)
+{
+ 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"));
+ }
+}
+
+// ------------------------------------------------------------------------
+// Main.
+// ------------------------------------------------------------------------
+
+ATF_INIT_TEST_CASES(tcs)
+{
+ // Add the test for the free functions.
+ ATF_ADD_TEST_CASE(tcs, cat_file__empty);
+ ATF_ADD_TEST_CASE(tcs, cat_file__one_line);
+ ATF_ADD_TEST_CASE(tcs, cat_file__several_lines);
+ ATF_ADD_TEST_CASE(tcs, cat_file__no_newline_eof);
+
+ ATF_ADD_TEST_CASE(tcs, compare_file__empty__match);
+ ATF_ADD_TEST_CASE(tcs, compare_file__empty__not_match);
+ ATF_ADD_TEST_CASE(tcs, compare_file__short__match);
+ ATF_ADD_TEST_CASE(tcs, compare_file__short__not_match);
+ ATF_ADD_TEST_CASE(tcs, compare_file__long__match);
+ ATF_ADD_TEST_CASE(tcs, compare_file__long__not_match);
+
+ ATF_ADD_TEST_CASE(tcs, copy_file__empty);
+ ATF_ADD_TEST_CASE(tcs, copy_file__some_contents);
+
+ ATF_ADD_TEST_CASE(tcs, create_file);
+
+ ATF_ADD_TEST_CASE(tcs, file_exists);
+
+ ATF_ADD_TEST_CASE(tcs, fork);
+
+ ATF_ADD_TEST_CASE(tcs, grep_collection__set);
+ ATF_ADD_TEST_CASE(tcs, grep_collection__vector);
+ ATF_ADD_TEST_CASE(tcs, grep_file);
+ ATF_ADD_TEST_CASE(tcs, grep_string);
+
+ ATF_ADD_TEST_CASE(tcs, redirect__stdout);
+ ATF_ADD_TEST_CASE(tcs, redirect__stderr);
+ ATF_ADD_TEST_CASE(tcs, redirect__other);
+
+ ATF_ADD_TEST_CASE(tcs, wait__ok);
+ ATF_ADD_TEST_CASE(tcs, wait__ok_nested);
+ ATF_ADD_TEST_CASE(tcs, wait__invalid_exitstatus);
+ ATF_ADD_TEST_CASE(tcs, wait__invalid_stdout);
+ ATF_ADD_TEST_CASE(tcs, wait__invalid_stderr);
+ ATF_ADD_TEST_CASE(tcs, wait__save_stdout);
+ ATF_ADD_TEST_CASE(tcs, wait__save_stderr);
+}