diff options
Diffstat (limited to 'contrib/atf/atf-c++')
60 files changed, 13203 insertions, 0 deletions
diff --git a/contrib/atf/atf-c++/Atffile b/contrib/atf/atf-c++/Atffile new file mode 100644 index 0000000..ba11f21 --- /dev/null +++ b/contrib/atf/atf-c++/Atffile @@ -0,0 +1,14 @@ +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: config_test +tp: macros_test +tp: pkg_config_test +tp: tests_test +tp: utils_test diff --git a/contrib/atf/atf-c++/Kyuafile b/contrib/atf/atf-c++/Kyuafile new file mode 100644 index 0000000..6df836f --- /dev/null +++ b/contrib/atf/atf-c++/Kyuafile @@ -0,0 +1,14 @@ +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="config_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/contrib/atf/atf-c++/Makefile.am.inc b/contrib/atf/atf-c++/Makefile.am.inc new file mode 100644 index 0000000..23d497d --- /dev/null +++ b/contrib/atf/atf-c++/Makefile.am.inc @@ -0,0 +1,111 @@ +# +# Automated Testing Framework (atf) +# +# 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++/config.cpp \ + atf-c++/config.hpp \ + atf-c++/macros.hpp \ + atf-c++/tests.cpp \ + atf-c++/tests.hpp \ + atf-c++/utils.hpp +libatf_c___la_LDFLAGS = -version-info 0:0:0 + +include_HEADERS += atf-c++.hpp +atf_c___HEADERS = atf-c++/build.hpp \ + atf-c++/check.hpp \ + atf-c++/config.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++-api.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 + 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++/config_test +atf_c___config_test_SOURCES = atf-c++/config_test.cpp +atf_c___config_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 + test -d atf-c++ || mkdir -p atf-c++ + @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/contrib/atf/atf-c++/atf-c++-api.3 b/contrib/atf/atf-c++/atf-c++-api.3 new file mode 100644 index 0000000..5d6618b --- /dev/null +++ b/contrib/atf/atf-c++/atf-c++-api.3 @@ -0,0 +1,420 @@ +.\" +.\" Automated Testing Framework (atf) +.\" +.\" 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 January 21, 2012 +.Dt ATF-C++-API 3 +.Os +.Sh NAME +.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 , +.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 "exp_errno" "bool_expression" +.Fn ATF_FAIL "reason" +.Fn ATF_INIT_TEST_CASES "tcs" +.Fn ATF_PASS +.Fn ATF_REQUIRE "expression" +.Fn ATF_REQUIRE_EQ "expression_1" "expression_2" +.Fn ATF_REQUIRE_ERRNO "exp_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" +.Sh DESCRIPTION +ATF provides a mostly-macro-based programming interface to implement test +programs in C or C++. +This interface is backed by a C++ implementation, but this fact is +hidden from the developer as much as possible through the use of +macros to simplify programming. +However, the use of C++ is not hidden everywhere and while you can +implement test cases without knowing anything at all about the object model +underneath the provided calls, you might need some minimum notions of the +language in very specific circumstances. +.Pp +C++-based test programs always 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 +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 , +.Xr atf-run 1 +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 , +.Xr atf-run 1 +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. +.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_EQ +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. +.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("descr", "Sample tests for the addition operator"); +} +ATF_TEST_CASE_BODY(addition) +{ + ATF_REQUIRE_EQ(0 + 0, 0); + ATF_REQUIRE_EQ(0 + 1, 1); + ATF_REQUIRE_EQ(1 + 0, 1); + + ATF_REQUIRE_EQ(1 + 1, 2); + + ATF_REQUIRE_EQ(100 + 200, 300); +} + +ATF_TEST_CASE(open_failure); +ATF_TEST_CASE_HEAD(open_failure) +{ + set("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("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 , +.Xr atf 7 diff --git a/contrib/atf/atf-c++/atf-c++.m4 b/contrib/atf/atf-c++/atf-c++.m4 new file mode 100644 index 0000000..0763d04 --- /dev/null +++ b/contrib/atf/atf-c++/atf-c++.m4 @@ -0,0 +1,48 @@ +dnl +dnl Automated Testing Framework (atf) +dnl +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 + +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/contrib/atf/atf-c++/atf-c++.pc.in b/contrib/atf/atf-c++/atf-c++.pc.in new file mode 100644 index 0000000..f366bb0 --- /dev/null +++ b/contrib/atf/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/contrib/atf/atf-c++/atf_c++_test.cpp b/contrib/atf/atf-c++/atf_c++_test.cpp new file mode 100644 index 0000000..c09e4b1 --- /dev/null +++ b/contrib/atf/atf-c++/atf_c++_test.cpp @@ -0,0 +1,48 @@ +// +// Automated Testing Framework (atf) +// +// 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 "macros.hpp" + +#include "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/contrib/atf/atf-c++/build.cpp b/contrib/atf/atf-c++/build.cpp new file mode 100644 index 0000000..9ce134c --- /dev/null +++ b/contrib/atf/atf-c++/build.cpp @@ -0,0 +1,119 @@ +// +// Automated Testing Framework (atf) +// +// 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. +// + +extern "C" { +#include "atf-c/build.h" +#include "atf-c/error.h" +#include "atf-c/utils.h" +} + +#include "build.hpp" + +#include "detail/exceptions.hpp" +#include "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/contrib/atf/atf-c++/build.hpp b/contrib/atf/atf-c++/build.hpp new file mode 100644 index 0000000..5a291b4 --- /dev/null +++ b/contrib/atf/atf-c++/build.hpp @@ -0,0 +1,57 @@ +// +// Automated Testing Framework (atf) +// +// 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/contrib/atf/atf-c++/build_test.cpp b/contrib/atf/atf-c++/build_test.cpp new file mode 100644 index 0000000..6852905 --- /dev/null +++ b/contrib/atf/atf-c++/build_test.cpp @@ -0,0 +1,247 @@ +// +// Automated Testing Framework (atf) +// +// 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 <cstring> +#include <iostream> + +#include "../atf-c/h_build.h" + +#include "build.hpp" +#include "config.hpp" +#include "macros.hpp" + +#include "detail/env.hpp" +#include "detail/process.hpp" +#include "detail/test_helpers.hpp" + +// ------------------------------------------------------------------------ +// Auxiliary functions. +// ------------------------------------------------------------------------ + +namespace atf { + namespace config { + void __reinit(void); + } +} + +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::config::__reinit(); + + 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::config::__reinit(); + + 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::config::__reinit(); + + 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); + } +} + +// ------------------------------------------------------------------------ +// Tests cases for the header file. +// ------------------------------------------------------------------------ + +HEADER_TC(include, "atf-c++/build.hpp"); + +// ------------------------------------------------------------------------ +// 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); + + // Add the test cases for the header file. + ATF_ADD_TEST_CASE(tcs, include); +} diff --git a/contrib/atf/atf-c++/check.cpp b/contrib/atf/atf-c++/check.cpp new file mode 100644 index 0000000..b099b07 --- /dev/null +++ b/contrib/atf/atf-c++/check.cpp @@ -0,0 +1,158 @@ +// +// Automated Testing Framework (atf) +// +// 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 <cstring> + +extern "C" { +#include "atf-c/build.h" +#include "atf-c/error.h" +} + +#include "check.hpp" + +#include "detail/exceptions.hpp" +#include "detail/process.hpp" +#include "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/contrib/atf/atf-c++/check.hpp b/contrib/atf/atf-c++/check.hpp new file mode 100644 index 0000000..055dd4f --- /dev/null +++ b/contrib/atf/atf-c++/check.hpp @@ -0,0 +1,133 @@ +// +// Automated Testing Framework (atf) +// +// 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> + +#include <atf-c++/utils.hpp> + +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 : utils::noncopyable { + //! + //! \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/contrib/atf/atf-c++/check_test.cpp b/contrib/atf/atf-c++/check_test.cpp new file mode 100644 index 0000000..fd528e9 --- /dev/null +++ b/contrib/atf/atf-c++/check_test.cpp @@ -0,0 +1,408 @@ +// +// Automated Testing Framework (atf) +// +// 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. +// + +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 "check.hpp" +#include "config.hpp" +#include "utils.hpp" + +#include "detail/fs.hpp" +#include "detail/process.hpp" +#include "detail/test_helpers.hpp" +#include "detail/text.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).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).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(grep_file("stdout", "-o test.o")); + ATF_REQUIRE(grep_file("stdout", "-c test.c")); + + ATF_TEST_CASE_USE(h_build_c_o_fail); + run_h_tc< ATF_TEST_CASE_NAME(h_build_c_o_fail) >(); + ATF_REQUIRE(grep_file("stdout", "-o test.o")); + ATF_REQUIRE(grep_file("stdout", "-c test.c")); + ATF_REQUIRE(grep_file("stderr", "test.c")); + ATF_REQUIRE(grep_file("stderr", "UNDEFINED_SYMBOL")); +} + +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(grep_file("stdout", "-o.*test.p")); + ATF_REQUIRE(grep_file("stdout", "test.c")); + ATF_REQUIRE(grep_file("test.p", "foo bar")); + + ATF_TEST_CASE_USE(h_build_cpp_fail); + run_h_tc< ATF_TEST_CASE_NAME(h_build_cpp_fail) >(); + ATF_REQUIRE(grep_file("stdout", "-o test.p")); + ATF_REQUIRE(grep_file("stdout", "test.c")); + ATF_REQUIRE(grep_file("stderr", "test.c")); + ATF_REQUIRE(grep_file("stderr", "non-existent.h")); +} + +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(grep_file("stdout", "-o test.o")); + ATF_REQUIRE(grep_file("stdout", "-c test.cpp")); + + ATF_TEST_CASE_USE(h_build_cxx_o_fail); + run_h_tc< ATF_TEST_CASE_NAME(h_build_cxx_o_fail) >(); + ATF_REQUIRE(grep_file("stdout", "-o test.o")); + ATF_REQUIRE(grep_file("stdout", "-c test.cpp")); + ATF_REQUIRE(grep_file("stderr", "test.cpp")); + ATF_REQUIRE(grep_file("stderr", "UNDEFINED_SYMBOL")); +} + +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(atf::config::get("atf_workdir") + "/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); +} + +// ------------------------------------------------------------------------ +// Tests cases for the header file. +// ------------------------------------------------------------------------ + +HEADER_TC(include, "atf-c++/check.hpp"); + +// ------------------------------------------------------------------------ +// 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); + + // Add the test cases for the header file. + ATF_ADD_TEST_CASE(tcs, include); +} diff --git a/contrib/atf/atf-c++/config.cpp b/contrib/atf/atf-c++/config.cpp new file mode 100644 index 0000000..7b7d641 --- /dev/null +++ b/contrib/atf/atf-c++/config.cpp @@ -0,0 +1,123 @@ +// +// Automated Testing Framework (atf) +// +// 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 <map> + +extern "C" { +#include "atf-c/config.h" +} + +#include "config.hpp" + +#include "detail/env.hpp" +#include "detail/sanity.hpp" + +static std::map< std::string, std::string > m_variables; + +// +// Adds all predefined standard build-time variables to the m_variables +// map, considering the values a user may have provided in the environment. +// +// Can only be called once during the program's lifetime. +// +static +void +init_variables(void) +{ + PRE(m_variables.empty()); + + m_variables["atf_arch"] = atf_config_get("atf_arch"); + m_variables["atf_build_cc"] = atf_config_get("atf_build_cc"); + m_variables["atf_build_cflags"] = atf_config_get("atf_build_cflags"); + m_variables["atf_build_cpp"] = atf_config_get("atf_build_cpp"); + m_variables["atf_build_cppflags"] = atf_config_get("atf_build_cppflags"); + m_variables["atf_build_cxx"] = atf_config_get("atf_build_cxx"); + m_variables["atf_build_cxxflags"] = atf_config_get("atf_build_cxxflags"); + m_variables["atf_confdir"] = atf_config_get("atf_confdir"); + m_variables["atf_includedir"] = atf_config_get("atf_includedir"); + m_variables["atf_libdir"] = atf_config_get("atf_libdir"); + m_variables["atf_libexecdir"] = atf_config_get("atf_libexecdir"); + m_variables["atf_machine"] = atf_config_get("atf_machine"); + m_variables["atf_pkgdatadir"] = atf_config_get("atf_pkgdatadir"); + m_variables["atf_shell"] = atf_config_get("atf_shell"); + m_variables["atf_workdir"] = atf_config_get("atf_workdir"); + + POST(!m_variables.empty()); +} + +const std::string& +atf::config::get(const std::string& varname) +{ + if (m_variables.empty()) + init_variables(); + + PRE(has(varname)); + return m_variables[varname]; +} + +const std::map< std::string, std::string >& +atf::config::get_all(void) +{ + if (m_variables.empty()) + init_variables(); + + return m_variables; +} + +bool +atf::config::has(const std::string& varname) +{ + if (m_variables.empty()) + init_variables(); + + return m_variables.find(varname) != m_variables.end(); +} + +extern "C" { +void __atf_config_reinit(void); +} + +namespace atf { +namespace config { +// +// Auxiliary function for the t_config test program so that it can +// revert the configuration's global status to an empty state and +// do new tests from there on. +// +// Ideally this shouldn't be part of the production library... but +// this is so small that it does not matter. +// +void +__reinit(void) +{ + __atf_config_reinit(); + m_variables.clear(); +} +} // namespace config +} // namespace atf diff --git a/contrib/atf/atf-c++/config.hpp b/contrib/atf/atf-c++/config.hpp new file mode 100644 index 0000000..e11b9bb --- /dev/null +++ b/contrib/atf/atf-c++/config.hpp @@ -0,0 +1,75 @@ +// +// Automated Testing Framework (atf) +// +// 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_CONFIG_HPP_) +#define _ATF_CXX_CONFIG_HPP_ + +#include <map> +#include <string> + +namespace atf { + +namespace config { + +//! +//! \brief Gets a build-time configuration variable's value. +//! +//! Given the name of a build-time configuration variable, returns its +//! textual value. The user is free to override these by setting their +//! corresponding environment variables. Therefore always use this +//! interface to get the value of these variables. +//! +//! \pre The variable must exist. +//! +const std::string& get(const std::string&); + +//! +//! \brief Returns all the build-time configuration variables. +//! +//! Returns a name to value map containing all build-time configuration +//! variables. +//! +const std::map< std::string, std::string >& get_all(void); + +//! +//! \brief Checks whether a build-time configuration variable exists. +//! +//! Given the name of a build-time configuration variable, checks +//! whether it is defined and returns a boolean indicating this +//! condition. The program only has to use this function to sanity-check +//! a variable name provided by the user. Otherwise it can assume that +//! the variables are defined. +//! +bool has(const std::string&); + +} // namespace config + +} // namespace atf + +#endif // !defined(_ATF_CXX_CONFIG_HPP_) diff --git a/contrib/atf/atf-c++/config_test.cpp b/contrib/atf/atf-c++/config_test.cpp new file mode 100644 index 0000000..a3cd2bb --- /dev/null +++ b/contrib/atf/atf-c++/config_test.cpp @@ -0,0 +1,231 @@ +// +// Automated Testing Framework (atf) +// +// 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 <cstring> +#include <iostream> + +#include "config.hpp" +#include "macros.hpp" + +#include "detail/env.hpp" +#include "detail/exceptions.hpp" +#include "detail/test_helpers.hpp" + +static const char *test_value = "env-value"; + +static struct varnames { + const char *lc; + const char *uc; + bool can_be_empty; +} all_vars[] = { + { "atf_arch", "ATF_ARCH", false }, + { "atf_build_cc", "ATF_BUILD_CC", false }, + { "atf_build_cflags", "ATF_BUILD_CFLAGS", true }, + { "atf_build_cpp", "ATF_BUILD_CPP", false }, + { "atf_build_cppflags", "ATF_BUILD_CPPFLAGS", true }, + { "atf_build_cxx", "ATF_BUILD_CXX", false }, + { "atf_build_cxxflags", "ATF_BUILD_CXXFLAGS", true }, + { "atf_confdir", "ATF_CONFDIR", false }, + { "atf_includedir", "ATF_INCLUDEDIR", false }, + { "atf_libdir", "ATF_LIBDIR", false }, + { "atf_libexecdir", "ATF_LIBEXECDIR", false }, + { "atf_machine", "ATF_MACHINE", false }, + { "atf_pkgdatadir", "ATF_PKGDATADIR", false }, + { "atf_shell", "ATF_SHELL", false }, + { "atf_workdir", "ATF_WORKDIR", false }, + { NULL, NULL, false } +}; + +// ------------------------------------------------------------------------ +// Auxiliary functions. +// ------------------------------------------------------------------------ + +namespace atf { + namespace config { + void __reinit(void); + } +} + +static +void +set_env_var(const char* name, const char* val) +{ + try { + atf::env::set(name, val); + } catch (const atf::system_error&) { + ATF_FAIL(std::string("set_env_var(") + name + ", " + val + + ") failed"); + } +} + +static +void +unset_env_var(const char* name) +{ + try { + atf::env::unset(name); + } catch (const atf::system_error&) { + ATF_FAIL(std::string("unset_env_var(") + name + ") failed"); + } +} + +static +size_t +all_vars_count(void) +{ + size_t count = 0; + for (const struct varnames* v = all_vars; v->lc != NULL; v++) + count++; + return count; +} + +static +void +unset_all(void) +{ + for (const struct varnames* v = all_vars; v->lc != NULL; v++) + unset_env_var(v->uc); +} + +static +void +compare_one(const char* var, const char* expvalue) +{ + std::cout << "Checking that " << var << " is set to " << expvalue << "\n"; + + for (const struct varnames* v = all_vars; v->lc != NULL; v++) { + if (std::strcmp(v->lc, var) == 0) + ATF_REQUIRE_EQ(atf::config::get(v->lc), test_value); + else + ATF_REQUIRE(atf::config::get(v->lc) != test_value); + } +} + +// ------------------------------------------------------------------------ +// Test cases for the free functions. +// ------------------------------------------------------------------------ + +ATF_TEST_CASE(get); +ATF_TEST_CASE_HEAD(get) +{ + set_md_var("descr", "Tests the config::get function"); +} +ATF_TEST_CASE_BODY(get) +{ + // Unset all known environment variables and make sure the built-in + // values do not match the bogus value we will use for testing. + unset_all(); + atf::config::__reinit(); + for (const struct varnames* v = all_vars; v->lc != NULL; v++) + ATF_REQUIRE(atf::config::get(v->lc) != test_value); + + // Test the behavior of empty values. + for (const struct varnames* v = all_vars; v->lc != NULL; v++) { + unset_all(); + if (!atf::config::get(v->lc).empty()) { + set_env_var(v->uc, ""); + atf::config::__reinit(); + if (v->can_be_empty) + ATF_REQUIRE(atf::config::get(v->lc).empty()); + else + ATF_REQUIRE(!atf::config::get(v->lc).empty()); + } + } + + // Check if the ATF_ARCH variable is recognized. + for (const struct varnames* v = all_vars; v->lc != NULL; v++) { + unset_all(); + set_env_var(v->uc, test_value); + atf::config::__reinit(); + compare_one(v->lc, test_value); + } +} + +ATF_TEST_CASE(get_all); +ATF_TEST_CASE_HEAD(get_all) +{ + set_md_var("descr", "Tests the config::get_all function"); +} +ATF_TEST_CASE_BODY(get_all) +{ + atf::config::__reinit(); + + // Check that the valid variables, and only those, are returned. + std::map< std::string, std::string > vars = atf::config::get_all(); + ATF_REQUIRE_EQ(vars.size(), all_vars_count()); + for (const struct varnames* v = all_vars; v->lc != NULL; v++) + ATF_REQUIRE(vars.find(v->lc) != vars.end()); +} + +ATF_TEST_CASE(has); +ATF_TEST_CASE_HEAD(has) +{ + set_md_var("descr", "Tests the config::has function"); +} +ATF_TEST_CASE_BODY(has) +{ + atf::config::__reinit(); + + // Check for all the variables that must exist. + for (const struct varnames* v = all_vars; v->lc != NULL; v++) + ATF_REQUIRE(atf::config::has(v->lc)); + + // Same as above, but using uppercase (which is incorrect). + for (const struct varnames* v = all_vars; v->lc != NULL; v++) + ATF_REQUIRE(!atf::config::has(v->uc)); + + // Check for some other variables that cannot exist. + ATF_REQUIRE(!atf::config::has("foo")); + ATF_REQUIRE(!atf::config::has("BAR")); + ATF_REQUIRE(!atf::config::has("atf_foo")); + ATF_REQUIRE(!atf::config::has("ATF_BAR")); + ATF_REQUIRE(!atf::config::has("atf_shel")); + ATF_REQUIRE(!atf::config::has("atf_shells")); +} + +// ------------------------------------------------------------------------ +// Tests cases for the header file. +// ------------------------------------------------------------------------ + +HEADER_TC(include, "atf-c++/config.hpp"); + +// ------------------------------------------------------------------------ +// Main. +// ------------------------------------------------------------------------ + +ATF_INIT_TEST_CASES(tcs) +{ + // Add the test cases for the free functions. + ATF_ADD_TEST_CASE(tcs, has); + ATF_ADD_TEST_CASE(tcs, get); + ATF_ADD_TEST_CASE(tcs, get_all); + + // Add the test cases for the header file. + ATF_ADD_TEST_CASE(tcs, include); +} diff --git a/contrib/atf/atf-c++/detail/Atffile b/contrib/atf/atf-c++/detail/Atffile new file mode 100644 index 0000000..ead6ec3 --- /dev/null +++ b/contrib/atf/atf-c++/detail/Atffile @@ -0,0 +1,13 @@ +Content-Type: application/X-atf-atffile; version="1" + +prop: test-suite = atf + +tp: application_test +tp: env_test +tp: exceptions_test +tp: expand_test +tp: fs_test +tp: parser_test +tp: sanity_test +tp: text_test +tp: ui_test diff --git a/contrib/atf/atf-c++/detail/Kyuafile b/contrib/atf/atf-c++/detail/Kyuafile new file mode 100644 index 0000000..472c122 --- /dev/null +++ b/contrib/atf/atf-c++/detail/Kyuafile @@ -0,0 +1,13 @@ +syntax("kyuafile", 1) + +test_suite("atf") + +atf_test_program{name="application_test"} +atf_test_program{name="env_test"} +atf_test_program{name="exceptions_test"} +atf_test_program{name="expand_test"} +atf_test_program{name="fs_test"} +atf_test_program{name="parser_test"} +atf_test_program{name="sanity_test"} +atf_test_program{name="text_test"} +atf_test_program{name="ui_test"} diff --git a/contrib/atf/atf-c++/detail/Makefile.am.inc b/contrib/atf/atf-c++/detail/Makefile.am.inc new file mode 100644 index 0000000..fcadd77 --- /dev/null +++ b/contrib/atf/atf-c++/detail/Makefile.am.inc @@ -0,0 +1,99 @@ +# +# Automated Testing Framework (atf) +# +# 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/env.cpp \ + atf-c++/detail/env.hpp \ + atf-c++/detail/exceptions.cpp \ + atf-c++/detail/exceptions.hpp \ + atf-c++/detail/expand.cpp \ + atf-c++/detail/expand.hpp \ + atf-c++/detail/fs.cpp \ + atf-c++/detail/fs.hpp \ + atf-c++/detail/parser.cpp \ + atf-c++/detail/parser.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 \ + atf-c++/detail/ui.cpp \ + atf-c++/detail/ui.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 + +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/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/expand_test +atf_c___detail_expand_test_SOURCES = atf-c++/detail/expand_test.cpp +atf_c___detail_expand_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/parser_test +atf_c___detail_parser_test_SOURCES = atf-c++/detail/parser_test.cpp +atf_c___detail_parser_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/sanity_test +atf_c___detail_sanity_test_SOURCES = atf-c++/detail/sanity_test.cpp +atf_c___detail_sanity_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/ui_test +atf_c___detail_ui_test_SOURCES = atf-c++/detail/ui_test.cpp +atf_c___detail_ui_test_LDADD = atf-c++/detail/libtest_helpers.la $(ATF_CXX_LIBS) + +# vim: syntax=make:noexpandtab:shiftwidth=8:softtabstop=8 diff --git a/contrib/atf/atf-c++/detail/application.cpp b/contrib/atf/atf-c++/detail/application.cpp new file mode 100644 index 0000000..878b010 --- /dev/null +++ b/contrib/atf/atf-c++/detail/application.cpp @@ -0,0 +1,345 @@ +// +// Automated Testing Framework (atf) +// +// 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(HAVE_CONFIG_H) +#include "bconfig.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 "application.hpp" +#include "sanity.hpp" +#include "ui.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, + const std::string& global_manpage, + const bool use_ui) : + m_hflag(false), + m_argc(-1), + m_argv(NULL), + m_prog_name(NULL), + m_description(description), + m_manpage(manpage), + m_global_manpage(global_manpage), + m_use_ui(use_ui) +{ +} + +impl::app::~app(void) +{ +} + +bool +impl::app::inited(void) +{ + return m_argc != -1; +} + +impl::app::options_set +impl::app::options(void) +{ + options_set opts = specific_options(); + if (m_use_ui) { + opts.insert(option('h', "", "Shows this help message")); + } + return opts; +} + +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 'h': + INV(m_use_ui); + m_hflag = true; + break; + + 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 +} + +void +impl::app::usage(std::ostream& os) +{ + PRE(inited()); + + std::string args = specific_args(); + if (!args.empty()) + args = " " + args; + os << ui::format_text_with_tag(std::string(m_prog_name) + " [options]" + + args, "Usage: ", false) << "\n\n" + << ui::format_text(m_description) << "\n\n"; + + options_set opts = options(); + INV(!opts.empty()); + os << "Available options:\n"; + size_t coldesc = 0; + for (options_set::const_iterator iter = opts.begin(); + iter != opts.end(); iter++) { + const option& opt = (*iter); + + if (opt.m_argument.length() + 1 > coldesc) + coldesc = opt.m_argument.length() + 1; + } + for (options_set::const_iterator iter = opts.begin(); + iter != opts.end(); iter++) { + const option& opt = (*iter); + + std::string tag = std::string(" -") + opt.m_character; + if (opt.m_argument.empty()) + tag += " "; + else + tag += " " + opt.m_argument + " "; + os << ui::format_text_with_tag(opt.m_description, tag, false, + coldesc + 10) << "\n"; + } + os << "\n"; + + std::string gmp; + if (!m_global_manpage.empty()) + gmp = " and " + m_global_manpage; + os << ui::format_text("For more details please see " + m_manpage + + gmp + ".") + << "\n"; +} + +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 { + int oldargc = m_argc; + + process_options(); + + if (m_hflag) { + INV(m_use_ui); + if (oldargc != 2) + throw usage_error("-h must be given alone."); + + usage(std::cout); + errcode = EXIT_SUCCESS; + } else + errcode = main(); + } catch (const usage_error& e) { + if (m_use_ui) { + std::cerr << ui::format_error(m_prog_name, e.what()) << "\n" + << ui::format_info(m_prog_name, std::string("Type `") + + m_prog_name + " -h' for more details.") + << "\n"; + } else { + 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) { + if (m_use_ui) { + std::cerr << ui::format_error(m_prog_name, std::string(e.what())) + << "\n"; + } else { + std::cerr << m_prog_name << ": ERROR: " << e.what() << "\n"; + } + errcode = EXIT_FAILURE; + } catch (const std::exception& e) { + if (m_use_ui) { + std::cerr << ui::format_error(m_prog_name, std::string("Caught " + "unexpected error: ") + e.what() + "\n" + bug) << "\n"; + } else { + std::cerr << m_prog_name << ": ERROR: Caught unexpected error: " + << e.what() << "\n"; + } + errcode = EXIT_FAILURE; + } catch (...) { + if (m_use_ui) { + std::cerr << ui::format_error(m_prog_name, std::string("Caught " + "unknown error\n") + bug) << "\n"; + } else { + std::cerr << m_prog_name << ": ERROR: Caught unknown error\n"; + } + errcode = EXIT_FAILURE; + } + return errcode; +} diff --git a/contrib/atf/atf-c++/detail/application.hpp b/contrib/atf/atf-c++/detail/application.hpp new file mode 100644 index 0000000..9d1f242 --- /dev/null +++ b/contrib/atf/atf-c++/detail/application.hpp @@ -0,0 +1,115 @@ +// +// Automated Testing Framework (atf) +// +// 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_APPLICATION_HPP_) +#define _ATF_CXX_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 { + bool m_hflag; + + 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, m_global_manpage; + const bool m_use_ui; + + 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&, const std::string&, + bool = true); + virtual ~app(void); + + int run(int, char* const*); +}; + +} // namespace application +} // namespace atf + +#endif // !defined(_ATF_CXX_APPLICATION_HPP_) diff --git a/contrib/atf/atf-c++/detail/application_test.cpp b/contrib/atf/atf-c++/detail/application_test.cpp new file mode 100644 index 0000000..2a788bf --- /dev/null +++ b/contrib/atf/atf-c++/detail/application_test.cpp @@ -0,0 +1,94 @@ +// +// Automated Testing Framework (atf) +// +// 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. +// + +extern "C" { +#include <unistd.h> +} + +#include "application.hpp" + +#include "../macros.hpp" + +class getopt_app : public atf::application::app { +public: + getopt_app(void) : app("description", "manpage", "other") {} + + 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/contrib/atf/atf-c++/detail/env.cpp b/contrib/atf/atf-c++/detail/env.cpp new file mode 100644 index 0000000..5ca7f09 --- /dev/null +++ b/contrib/atf/atf-c++/detail/env.cpp @@ -0,0 +1,73 @@ +// +// Automated Testing Framework (atf) +// +// 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. +// + +extern "C" { +#include "../../atf-c/error.h" + +#include "../../atf-c/detail/env.h" +} + +#include "env.hpp" +#include "exceptions.hpp" +#include "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()); +} + +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/contrib/atf/atf-c++/detail/env.hpp b/contrib/atf/atf-c++/detail/env.hpp new file mode 100644 index 0000000..afdf69b --- /dev/null +++ b/contrib/atf/atf-c++/detail/env.hpp @@ -0,0 +1,84 @@ +// +// Automated Testing Framework (atf) +// +// 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_ENV_HPP_) +#define _ATF_CXX_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 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_ENV_HPP_) diff --git a/contrib/atf/atf-c++/detail/env_test.cpp b/contrib/atf/atf-c++/detail/env_test.cpp new file mode 100644 index 0000000..a7b681d --- /dev/null +++ b/contrib/atf/atf-c++/detail/env_test.cpp @@ -0,0 +1,91 @@ +// +// Automated Testing Framework (atf) +// +// 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 "../macros.hpp" + +#include "env.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(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, set); + ATF_ADD_TEST_CASE(tcs, unset); +} diff --git a/contrib/atf/atf-c++/detail/exceptions.cpp b/contrib/atf/atf-c++/detail/exceptions.cpp new file mode 100644 index 0000000..79c5b48 --- /dev/null +++ b/contrib/atf/atf-c++/detail/exceptions.cpp @@ -0,0 +1,157 @@ +// +// Automated Testing Framework (atf) +// +// 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(HAVE_CONFIG_H) +#include "bconfig.h" +#endif + +#include <cstdarg> +#include <cstdio> +#include <cstring> +#include <new> + +extern "C" { +#include "../../atf-c/error.h" +}; + +#include "exceptions.hpp" +#include "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/contrib/atf/atf-c++/detail/exceptions.hpp b/contrib/atf/atf-c++/detail/exceptions.hpp new file mode 100644 index 0000000..f655a84 --- /dev/null +++ b/contrib/atf/atf-c++/detail/exceptions.hpp @@ -0,0 +1,99 @@ +// +// Automated Testing Framework (atf) +// +// 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_EXCEPTIONS_HPP_) +#define _ATF_CXX_EXCEPTIONS_HPP_ + +#include <stdexcept> +#include <string> + +extern "C" { +struct atf_error; +} + +namespace atf { + +template< class T > +class not_found_error : + public std::runtime_error +{ + T m_value; + +public: + not_found_error(const std::string& message, const T& value) throw(); + + virtual ~not_found_error(void) throw(); + + const T& get_value(void) const throw(); +}; + +template< class T > +inline +not_found_error< T >::not_found_error(const std::string& message, + const T& value) + throw() : + std::runtime_error(message), + m_value(value) +{ +} + +template< class T > +inline +not_found_error< T >::~not_found_error(void) + throw() +{ +} + +template< class T > +inline +const T& +not_found_error< T >::get_value(void) + const + throw() +{ + return m_value; +} + +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_EXCEPTIONS_HPP_) diff --git a/contrib/atf/atf-c++/detail/exceptions_test.cpp b/contrib/atf/atf-c++/detail/exceptions_test.cpp new file mode 100644 index 0000000..821c192 --- /dev/null +++ b/contrib/atf/atf-c++/detail/exceptions_test.cpp @@ -0,0 +1,148 @@ +// +// Automated Testing Framework (atf) +// +// 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. +// + +extern "C" { +#include "../../atf-c/error.h" +} + +#include <cstdio> +#include <new> + +#include "../macros.hpp" + +#include "exceptions.hpp" +#include "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/contrib/atf/atf-c++/detail/expand.cpp b/contrib/atf/atf-c++/detail/expand.cpp new file mode 100644 index 0000000..f6f9b68 --- /dev/null +++ b/contrib/atf/atf-c++/detail/expand.cpp @@ -0,0 +1,81 @@ +// +// Automated Testing Framework (atf) +// +// 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 <stdexcept> + +#include "expand.hpp" +#include "text.hpp" + +namespace impl = atf::expand; +#define IMPL_NAME "atf::expand" + +// ------------------------------------------------------------------------ +// Auxiliary functions. +// ------------------------------------------------------------------------ + +namespace { + +std::string +glob_to_regex(const std::string& glob) +{ + std::string regex; + regex.reserve(glob.length() * 2); + + regex += '^'; + for (std::string::const_iterator iter = glob.begin(); iter != glob.end(); + iter++) { + switch (*iter) { + case '*': regex += ".*"; break; + case '?': regex += "."; break; + default: regex += *iter; + } + } + regex += '$'; + + return regex; +} + +} // anonymous namespace + +// ------------------------------------------------------------------------ +// Free functions. +// ------------------------------------------------------------------------ + +bool +impl::is_glob(const std::string& glob) +{ + // NOTE: Keep this in sync with glob_to_regex! + return glob.find_first_of("*?") != std::string::npos; +} + +bool +impl::matches_glob(const std::string& glob, const std::string& candidate) +{ + return atf::text::match(candidate, glob_to_regex(glob)); +} diff --git a/contrib/atf/atf-c++/detail/expand.hpp b/contrib/atf/atf-c++/detail/expand.hpp new file mode 100644 index 0000000..7f4071e --- /dev/null +++ b/contrib/atf/atf-c++/detail/expand.hpp @@ -0,0 +1,82 @@ +// +// Automated Testing Framework (atf) +// +// 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_EXPAND_HPP_) +#define _ATF_CXX_EXPAND_HPP_ + +#include <string> +#include <vector> + +namespace atf { +namespace expand { + +// ------------------------------------------------------------------------ +// Free functions. +// ------------------------------------------------------------------------ + +//! +//! \brief Checks if the given string is a glob pattern. +//! +//! Returns true if the given string is a glob pattern; i.e. if it contains +//! any character that will be expanded by expand_glob. +//! +bool is_glob(const std::string&); + +//! +//! \brief Checks if a given string matches a glob pattern. +//! +//! Given a glob pattern and a string, checks whether the former matches +//! the latter. Returns a boolean indicating this condition. +//! +bool matches_glob(const std::string&, const std::string&); + +//! +//! \brief Expands a glob pattern among multiple candidates. +//! +//! Given a glob pattern and a set of candidate strings, checks which of +//! those strings match the glob pattern and returns them. +//! +template< class T > +std::vector< std::string > expand_glob(const std::string& glob, + const T& candidates) +{ + std::vector< std::string > exps; + + for (typename T::const_iterator iter = candidates.begin(); + iter != candidates.end(); iter++) + if (matches_glob(glob, *iter)) + exps.push_back(*iter); + + return exps; +} + +} // namespace expand +} // namespace atf + +#endif // !defined(_ATF_CXX_EXPAND_HPP_) diff --git a/contrib/atf/atf-c++/detail/expand_test.cpp b/contrib/atf/atf-c++/detail/expand_test.cpp new file mode 100644 index 0000000..222ab3a --- /dev/null +++ b/contrib/atf/atf-c++/detail/expand_test.cpp @@ -0,0 +1,272 @@ +// +// Automated Testing Framework (atf) +// +// 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 <cstring> + +#include "../macros.hpp" + +#include "expand.hpp" + +// XXX Many of the tests here are duplicated in atf-c/t_expand. Should +// find a way to easily share them, or maybe remove the ones here. + +// ------------------------------------------------------------------------ +// Test cases for the free functions. +// ------------------------------------------------------------------------ + +ATF_TEST_CASE(is_glob); +ATF_TEST_CASE_HEAD(is_glob) +{ + set_md_var("descr", "Tests the is_glob function."); +} +ATF_TEST_CASE_BODY(is_glob) +{ + using atf::expand::is_glob; + + ATF_REQUIRE(!is_glob("")); + ATF_REQUIRE(!is_glob("a")); + ATF_REQUIRE(!is_glob("foo")); + + ATF_REQUIRE( is_glob("*")); + ATF_REQUIRE( is_glob("a*")); + ATF_REQUIRE( is_glob("*a")); + ATF_REQUIRE( is_glob("a*b")); + + ATF_REQUIRE( is_glob("?")); + ATF_REQUIRE( is_glob("a?")); + ATF_REQUIRE( is_glob("?a")); + ATF_REQUIRE( is_glob("a?b")); +} + +ATF_TEST_CASE(matches_glob_plain); +ATF_TEST_CASE_HEAD(matches_glob_plain) +{ + set_md_var("descr", "Tests the matches_glob function by using plain " + "text strings as patterns only."); +} +ATF_TEST_CASE_BODY(matches_glob_plain) +{ + using atf::expand::matches_glob; + + ATF_REQUIRE( matches_glob("", "")); + ATF_REQUIRE(!matches_glob("a", "")); + ATF_REQUIRE(!matches_glob("", "a")); + + ATF_REQUIRE( matches_glob("ab", "ab")); + ATF_REQUIRE(!matches_glob("abc", "ab")); + ATF_REQUIRE(!matches_glob("ab", "abc")); +} + +ATF_TEST_CASE(matches_glob_star); +ATF_TEST_CASE_HEAD(matches_glob_star) +{ + set_md_var("descr", "Tests the matches_glob function by using the '*' " + "meta-character as part of the pattern."); +} +ATF_TEST_CASE_BODY(matches_glob_star) +{ + using atf::expand::matches_glob; + + ATF_REQUIRE( matches_glob("*", "")); + ATF_REQUIRE( matches_glob("*", "a")); + ATF_REQUIRE( matches_glob("*", "ab")); + + ATF_REQUIRE(!matches_glob("a*", "")); + ATF_REQUIRE( matches_glob("a*", "a")); + ATF_REQUIRE( matches_glob("a*", "aa")); + ATF_REQUIRE( matches_glob("a*", "ab")); + ATF_REQUIRE( matches_glob("a*", "abc")); + ATF_REQUIRE(!matches_glob("a*", "ba")); + + ATF_REQUIRE( matches_glob("*a", "a")); + ATF_REQUIRE( matches_glob("*a", "ba")); + ATF_REQUIRE(!matches_glob("*a", "bc")); + ATF_REQUIRE(!matches_glob("*a", "bac")); + + ATF_REQUIRE( matches_glob("*ab", "ab")); + ATF_REQUIRE( matches_glob("*ab", "aab")); + ATF_REQUIRE( matches_glob("*ab", "aaab")); + ATF_REQUIRE( matches_glob("*ab", "bab")); + ATF_REQUIRE(!matches_glob("*ab", "bcb")); + ATF_REQUIRE(!matches_glob("*ab", "bacb")); + + ATF_REQUIRE( matches_glob("a*b", "ab")); + ATF_REQUIRE( matches_glob("a*b", "acb")); + ATF_REQUIRE( matches_glob("a*b", "acdeb")); + ATF_REQUIRE(!matches_glob("a*b", "acdebz")); + ATF_REQUIRE(!matches_glob("a*b", "zacdeb")); +} + +ATF_TEST_CASE(matches_glob_question); +ATF_TEST_CASE_HEAD(matches_glob_question) +{ + set_md_var("descr", "Tests the matches_glob function by using the '?' " + "meta-character as part of the pattern."); +} +ATF_TEST_CASE_BODY(matches_glob_question) +{ + using atf::expand::matches_glob; + + ATF_REQUIRE(!matches_glob("?", "")); + ATF_REQUIRE( matches_glob("?", "a")); + ATF_REQUIRE(!matches_glob("?", "ab")); + + ATF_REQUIRE( matches_glob("?", "b")); + ATF_REQUIRE( matches_glob("?", "c")); + + ATF_REQUIRE( matches_glob("a?", "ab")); + ATF_REQUIRE( matches_glob("a?", "ac")); + ATF_REQUIRE(!matches_glob("a?", "ca")); + + ATF_REQUIRE( matches_glob("???", "abc")); + ATF_REQUIRE( matches_glob("???", "def")); + ATF_REQUIRE(!matches_glob("???", "a")); + ATF_REQUIRE(!matches_glob("???", "ab")); + ATF_REQUIRE(!matches_glob("???", "abcd")); +} + +ATF_TEST_CASE(expand_glob_base); +ATF_TEST_CASE_HEAD(expand_glob_base) +{ + set_md_var("descr", "Tests the expand_glob function with random " + "patterns."); +} +ATF_TEST_CASE_BODY(expand_glob_base) +{ + using atf::expand::expand_glob; + + std::vector< std::string > candidates; + candidates.push_back("foo"); + candidates.push_back("bar"); + candidates.push_back("baz"); + candidates.push_back("foobar"); + candidates.push_back("foobarbaz"); + candidates.push_back("foobarbazfoo"); + + std::vector< std::string > exps; + + exps = expand_glob("foo", candidates); + ATF_REQUIRE_EQ(exps.size(), 1); + ATF_REQUIRE(exps[0] == "foo"); + + exps = expand_glob("bar", candidates); + ATF_REQUIRE_EQ(exps.size(), 1); + ATF_REQUIRE(exps[0] == "bar"); + + exps = expand_glob("foo*", candidates); + ATF_REQUIRE_EQ(exps.size(), 4); + ATF_REQUIRE(exps[0] == "foo"); + ATF_REQUIRE(exps[1] == "foobar"); + ATF_REQUIRE(exps[2] == "foobarbaz"); + ATF_REQUIRE(exps[3] == "foobarbazfoo"); + + exps = expand_glob("*foo", candidates); + ATF_REQUIRE_EQ(exps.size(), 2); + ATF_REQUIRE(exps[0] == "foo"); + ATF_REQUIRE(exps[1] == "foobarbazfoo"); + + exps = expand_glob("*foo*", candidates); + ATF_REQUIRE_EQ(exps.size(), 4); + ATF_REQUIRE(exps[0] == "foo"); + ATF_REQUIRE(exps[1] == "foobar"); + ATF_REQUIRE(exps[2] == "foobarbaz"); + ATF_REQUIRE(exps[3] == "foobarbazfoo"); + + exps = expand_glob("ba", candidates); + ATF_REQUIRE_EQ(exps.size(), 0); + + exps = expand_glob("ba*", candidates); + ATF_REQUIRE_EQ(exps.size(), 2); + ATF_REQUIRE(exps[0] == "bar"); + ATF_REQUIRE(exps[1] == "baz"); + + exps = expand_glob("*ba", candidates); + ATF_REQUIRE_EQ(exps.size(), 0); + + exps = expand_glob("*ba*", candidates); + ATF_REQUIRE_EQ(exps.size(), 5); + ATF_REQUIRE(exps[0] == "bar"); + ATF_REQUIRE(exps[1] == "baz"); + ATF_REQUIRE(exps[2] == "foobar"); + ATF_REQUIRE(exps[3] == "foobarbaz"); + ATF_REQUIRE(exps[4] == "foobarbazfoo"); +} + +ATF_TEST_CASE(expand_glob_tps); +ATF_TEST_CASE_HEAD(expand_glob_tps) +{ + set_md_var("descr", "Tests the expand_glob function with patterns that " + "match typical test program names. This is just a subcase " + "of expand_base, but it is nice to make sure that it really " + "works."); +} +ATF_TEST_CASE_BODY(expand_glob_tps) +{ + using atf::expand::expand_glob; + + std::vector< std::string > candidates; + candidates.push_back("Atffile"); + candidates.push_back("h_foo"); + candidates.push_back("t_foo"); + candidates.push_back("t_bar"); + candidates.push_back("t_baz"); + candidates.push_back("foo_helper"); + candidates.push_back("foo_test"); + candidates.push_back("bar_test"); + candidates.push_back("baz_test"); + + std::vector< std::string > exps; + + exps = expand_glob("t_*", candidates); + ATF_REQUIRE_EQ(exps.size(), 3); + ATF_REQUIRE(exps[0] == "t_foo"); + ATF_REQUIRE(exps[1] == "t_bar"); + ATF_REQUIRE(exps[2] == "t_baz"); + + exps = expand_glob("*_test", candidates); + ATF_REQUIRE_EQ(exps.size(), 3); + ATF_REQUIRE(exps[0] == "foo_test"); + ATF_REQUIRE(exps[1] == "bar_test"); + ATF_REQUIRE(exps[2] == "baz_test"); +} + +// ------------------------------------------------------------------------ +// Main. +// ------------------------------------------------------------------------ + +ATF_INIT_TEST_CASES(tcs) +{ + // Add the tests for the free functions. + ATF_ADD_TEST_CASE(tcs, is_glob); + ATF_ADD_TEST_CASE(tcs, matches_glob_plain); + ATF_ADD_TEST_CASE(tcs, matches_glob_star); + ATF_ADD_TEST_CASE(tcs, matches_glob_question); + ATF_ADD_TEST_CASE(tcs, expand_glob_base); + ATF_ADD_TEST_CASE(tcs, expand_glob_tps); +} diff --git a/contrib/atf/atf-c++/detail/fs.cpp b/contrib/atf/atf-c++/detail/fs.cpp new file mode 100644 index 0000000..3517e26 --- /dev/null +++ b/contrib/atf/atf-c++/detail/fs.cpp @@ -0,0 +1,517 @@ +// +// Automated Testing Framework (atf) +// +// 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(HAVE_CONFIG_H) +#include "bconfig.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 "../utils.hpp" + +#include "exceptions.hpp" +#include "env.hpp" +#include "fs.hpp" +#include "process.hpp" +#include "sanity.hpp" +#include "text.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/contrib/atf/atf-c++/detail/fs.hpp b/contrib/atf/atf-c++/detail/fs.hpp new file mode 100644 index 0000000..4ffb39b --- /dev/null +++ b/contrib/atf/atf-c++/detail/fs.hpp @@ -0,0 +1,391 @@ +// +// Automated Testing Framework (atf) +// +// 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_FS_HPP_) +#define _ATF_CXX_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_FS_HPP_) diff --git a/contrib/atf/atf-c++/detail/fs_test.cpp b/contrib/atf/atf-c++/detail/fs_test.cpp new file mode 100644 index 0000000..6cf9bf6 --- /dev/null +++ b/contrib/atf/atf-c++/detail/fs_test.cpp @@ -0,0 +1,545 @@ +// +// Automated Testing Framework (atf) +// +// 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. +// + +extern "C" { +#include <sys/types.h> +#include <sys/stat.h> +} + +#include <fstream> +#include <cerrno> +#include <cstdio> + +#include "../macros.hpp" + +#include "exceptions.hpp" +#include "fs.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/contrib/atf/atf-c++/detail/parser.cpp b/contrib/atf/atf-c++/detail/parser.cpp new file mode 100644 index 0000000..7e7f680 --- /dev/null +++ b/contrib/atf/atf-c++/detail/parser.cpp @@ -0,0 +1,384 @@ +// +// Automated Testing Framework (atf) +// +// 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 <sstream> + +#include "parser.hpp" +#include "sanity.hpp" +#include "text.hpp" + +namespace impl = atf::parser; +#define IMPL_NAME "atf::parser" + +// ------------------------------------------------------------------------ +// The "parse_error" class. +// ------------------------------------------------------------------------ + +impl::parse_error::parse_error(size_t line, std::string msg) : + std::runtime_error(msg), + std::pair< size_t, std::string >(line, msg) +{ +} + +impl::parse_error::~parse_error(void) + throw() +{ +} + +const char* +impl::parse_error::what(void) + const throw() +{ + try { + std::ostringstream oss; + oss << "LONELY PARSE ERROR: " << first << ": " << second; + m_msg = oss.str(); + return m_msg.c_str(); + } catch (...) { + return "Could not format message for parsing error."; + } +} + +impl::parse_error::operator std::string(void) + const +{ + return atf::text::to_string(first) + ": " + second; +} + +// ------------------------------------------------------------------------ +// The "parse_errors" class. +// ------------------------------------------------------------------------ + +impl::parse_errors::parse_errors(void) : + std::runtime_error("No parsing errors yet") +{ + m_msg.clear(); +} + +impl::parse_errors::~parse_errors(void) + throw() +{ +} + +const char* +impl::parse_errors::what(void) + const throw() +{ + try { + m_msg = atf::text::join(*this, "\n"); + return m_msg.c_str(); + } catch (...) { + return "Could not format messages for parsing errors."; + } +} + +// ------------------------------------------------------------------------ +// The "format_error" class. +// ------------------------------------------------------------------------ + +impl::format_error::format_error(const std::string& w) : + std::runtime_error(w.c_str()) +{ +} + +// ------------------------------------------------------------------------ +// The "token" class. +// ------------------------------------------------------------------------ + +impl::token::token(void) : + m_inited(false) +{ +} + +impl::token::token(size_t p_line, + const token_type& p_type, + const std::string& p_text) : + m_inited(true), + m_line(p_line), + m_type(p_type), + m_text(p_text) +{ +} + +size_t +impl::token::lineno(void) + const +{ + return m_line; +} + +const impl::token_type& +impl::token::type(void) + const +{ + return m_type; +} + +const std::string& +impl::token::text(void) + const +{ + return m_text; +} + +impl::token::operator bool(void) + const +{ + return m_inited; +} + +bool +impl::token::operator!(void) + const +{ + return !m_inited; +} + +// ------------------------------------------------------------------------ +// The "header_entry" class. +// ------------------------------------------------------------------------ + +impl::header_entry::header_entry(void) +{ +} + +impl::header_entry::header_entry(const std::string& n, const std::string& v, + attrs_map as) : + m_name(n), + m_value(v), + m_attrs(as) +{ +} + +const std::string& +impl::header_entry::name(void) const +{ + return m_name; +} + +const std::string& +impl::header_entry::value(void) const +{ + return m_value; +} + +const impl::attrs_map& +impl::header_entry::attrs(void) const +{ + return m_attrs; +} + +bool +impl::header_entry::has_attr(const std::string& n) const +{ + return m_attrs.find(n) != m_attrs.end(); +} + +const std::string& +impl::header_entry::get_attr(const std::string& n) const +{ + attrs_map::const_iterator iter = m_attrs.find(n); + PRE(iter != m_attrs.end()); + return (*iter).second; +} + +// ------------------------------------------------------------------------ +// The header tokenizer. +// ------------------------------------------------------------------------ + +namespace header { + +static const impl::token_type eof_type = 0; +static const impl::token_type nl_type = 1; +static const impl::token_type text_type = 2; +static const impl::token_type colon_type = 3; +static const impl::token_type semicolon_type = 4; +static const impl::token_type dblquote_type = 5; +static const impl::token_type equal_type = 6; + +class tokenizer : public impl::tokenizer< std::istream > { +public: + tokenizer(std::istream& is, size_t curline) : + impl::tokenizer< std::istream > + (is, true, eof_type, nl_type, text_type, curline) + { + add_delim(';', semicolon_type); + add_delim(':', colon_type); + add_delim('=', equal_type); + add_quote('"', dblquote_type); + } +}; + +static +impl::parser< header::tokenizer >& +read(impl::parser< header::tokenizer >& p, impl::header_entry& he) +{ + using namespace header; + + impl::token t = p.expect(text_type, nl_type, "a header name"); + if (t.type() == nl_type) { + he = impl::header_entry(); + return p; + } + std::string hdr_name = t.text(); + + t = p.expect(colon_type, "`:'"); + + t = p.expect(text_type, "a textual value"); + std::string hdr_value = t.text(); + + impl::attrs_map attrs; + + for (;;) { + t = p.expect(eof_type, semicolon_type, nl_type, + "eof, `;' or new line"); + if (t.type() == eof_type || t.type() == nl_type) + break; + + t = p.expect(text_type, "an attribute name"); + std::string attr_name = t.text(); + + t = p.expect(equal_type, "`='"); + + t = p.expect(text_type, "word or quoted string"); + std::string attr_value = t.text(); + attrs[attr_name] = attr_value; + } + + he = impl::header_entry(hdr_name, hdr_value, attrs); + + return p; +} + +static +std::ostream& +write(std::ostream& os, const impl::header_entry& he) +{ + std::string line = he.name() + ": " + he.value(); + impl::attrs_map as = he.attrs(); + for (impl::attrs_map::const_iterator iter = as.begin(); iter != as.end(); + iter++) { + PRE((*iter).second.find('\"') == std::string::npos); + line += "; " + (*iter).first + "=\"" + (*iter).second + "\""; + } + + os << line << "\n"; + + return os; +} + +} // namespace header + +// ------------------------------------------------------------------------ +// Free functions. +// ------------------------------------------------------------------------ + +std::pair< size_t, impl::headers_map > +impl::read_headers(std::istream& is, size_t curline) +{ + using impl::format_error; + + headers_map hm; + + // + // Grammar + // + // header = entry+ nl + // entry = line nl + // line = text colon text + // (semicolon (text equal (text | dblquote string dblquote)))* + // string = quoted_string + // + + header::tokenizer tkz(is, curline); + impl::parser< header::tokenizer > p(tkz); + + bool first = true; + for (;;) { + try { + header_entry he; + if (!header::read(p, he).good() || he.name().empty()) + break; + + if (first && he.name() != "Content-Type") + throw format_error("Could not determine content type"); + else + first = false; + + hm[he.name()] = he; + } catch (const impl::parse_error& pe) { + p.add_error(pe); + p.reset(header::nl_type); + } + } + + if (!is.good()) + throw format_error("Unexpected end of stream"); + + return std::pair< size_t, headers_map >(tkz.lineno(), hm); +} + +void +impl::write_headers(const impl::headers_map& hm, std::ostream& os) +{ + PRE(!hm.empty()); + headers_map::const_iterator ct = hm.find("Content-Type"); + PRE(ct != hm.end()); + header::write(os, (*ct).second); + for (headers_map::const_iterator iter = hm.begin(); iter != hm.end(); + iter++) { + if ((*iter).first != "Content-Type") + header::write(os, (*iter).second); + } + os << "\n"; +} + +void +impl::validate_content_type(const impl::headers_map& hm, const std::string& fmt, + int version) +{ + using impl::format_error; + + headers_map::const_iterator iter = hm.find("Content-Type"); + if (iter == hm.end()) + throw format_error("Could not determine content type"); + + const header_entry& he = (*iter).second; + if (he.value() != fmt) + throw format_error("Mismatched content type: expected `" + fmt + + "' but got `" + he.value() + "'"); + + if (!he.has_attr("version")) + throw format_error("Could not determine version"); + const std::string& vstr = atf::text::to_string(version); + if (he.get_attr("version") != vstr) + throw format_error("Mismatched version: expected `" + + vstr + "' but got `" + + he.get_attr("version") + "'"); +} diff --git a/contrib/atf/atf-c++/detail/parser.hpp b/contrib/atf/atf-c++/detail/parser.hpp new file mode 100644 index 0000000..f1595f5 --- /dev/null +++ b/contrib/atf/atf-c++/detail/parser.hpp @@ -0,0 +1,607 @@ +// +// Automated Testing Framework (atf) +// +// 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_PARSER_HPP_) +#define _ATF_CXX_PARSER_HPP_ + +#include <istream> +#include <map> +#include <ostream> +#include <stdexcept> +#include <string> +#include <utility> +#include <vector> + +namespace atf { +namespace parser { + +// ------------------------------------------------------------------------ +// The "parse_error" class. +// ------------------------------------------------------------------------ + +class parse_error : public std::runtime_error, + public std::pair< size_t, std::string > { + mutable std::string m_msg; + +public: + parse_error(size_t, std::string); + ~parse_error(void) throw(); + + const char* what(void) const throw(); + + operator std::string(void) const; +}; + +// ------------------------------------------------------------------------ +// The "parse_errors" class. +// ------------------------------------------------------------------------ + +class parse_errors : public std::runtime_error, + public std::vector< parse_error > { + std::vector< parse_error > m_errors; + mutable std::string m_msg; + +public: + parse_errors(void); + ~parse_errors(void) throw(); + + const char* what(void) const throw(); +}; + +// ------------------------------------------------------------------------ +// The "format_error" class. +// ------------------------------------------------------------------------ + +class format_error : public std::runtime_error { +public: + format_error(const std::string&); +}; + +// ------------------------------------------------------------------------ +// The "token" class. +// ------------------------------------------------------------------------ + +typedef int token_type; + +//! +//! \brief Representation of a read token. +//! +//! A pair that contains the information of a token read from a stream. +//! It contains the token's type and its associated data, if any. +//! +struct token { + bool m_inited; + size_t m_line; + token_type m_type; + std::string m_text; + +public: + token(void); + token(size_t, const token_type&, const std::string& = ""); + + size_t lineno(void) const; + const token_type& type(void) const; + const std::string& text(void) const; + + operator bool(void) const; + bool operator!(void) const; +}; + +// ------------------------------------------------------------------------ +// The "tokenizer" class. +// ------------------------------------------------------------------------ + +//! +//! \brief A stream tokenizer. +//! +//! This template implements an extremely simple, line-oriented stream +//! tokenizer. It is only able to recognize one character-long delimiters, +//! random-length keywords, skip whitespace and, anything that does not +//! match these rules is supposed to be a word. +//! +//! Parameter IS: The input stream's type. +//! +template< class IS > +class tokenizer { + IS& m_is; + size_t m_lineno; + token m_la; + + bool m_skipws; + token_type m_eof_type, m_nl_type, m_text_type; + + std::map< char, token_type > m_delims_map; + std::string m_delims_str; + + char m_quotech; + token_type m_quotetype; + + std::map< std::string, token_type > m_keywords_map; + + token_type alloc_type(void); + + template< class TKZ > + friend + class parser; + +public: + tokenizer(IS&, bool, const token_type&, const token_type&, + const token_type&, size_t = 1); + + size_t lineno(void) const; + + void add_delim(char, const token_type&); + void add_keyword(const std::string&, const token_type&); + void add_quote(char, const token_type&); + + token next(void); + std::string rest_of_line(void); +}; + +template< class IS > +tokenizer< IS >::tokenizer(IS& p_is, + bool p_skipws, + const token_type& p_eof_type, + const token_type& p_nl_type, + const token_type& p_text_type, + size_t p_lineno) : + m_is(p_is), + m_lineno(p_lineno), + m_skipws(p_skipws), + m_eof_type(p_eof_type), + m_nl_type(p_nl_type), + m_text_type(p_text_type), + m_quotech(-1) +{ +} + +template< class IS > +size_t +tokenizer< IS >::lineno(void) + const +{ + return m_lineno; +} + +template< class IS > +void +tokenizer< IS >::add_delim(char delim, const token_type& type) +{ + m_delims_map[delim] = type; + m_delims_str += delim; +} + +template< class IS > +void +tokenizer< IS >::add_keyword(const std::string& keyword, + const token_type& type) +{ + m_keywords_map[keyword] = type; +} + +template< class IS > +void +tokenizer< IS >::add_quote(char ch, const token_type& type) +{ + m_quotech = ch; + m_quotetype = type; +} + +template< class IS > +token +tokenizer< IS >::next(void) +{ + if (m_la) { + token t = m_la; + m_la = token(); + if (t.type() == m_nl_type) + m_lineno++; + return t; + } + + char ch; + std::string text; + + bool done = false, quoted = false; + token t(m_lineno, m_eof_type, "<<EOF>>"); + while (!done && m_is.get(ch).good()) { + if (ch == m_quotech) { + if (text.empty()) { + bool escaped = false; + while (!done && m_is.get(ch).good()) { + if (!escaped) { + if (ch == '\\') + escaped = true; + else if (ch == '\n') { + m_la = token(m_lineno, m_nl_type, "<<NEWLINE>>"); + throw parse_error(t.lineno(), + "Missing double quotes before " + "end of line"); + } else if (ch == m_quotech) + done = true; + else + text += ch; + } else { + text += ch; + escaped = false; + } + } + if (!m_is.good()) + throw parse_error(t.lineno(), + "Missing double quotes before " + "end of file"); + t = token(m_lineno, m_text_type, text); + quoted = true; + } else { + m_is.unget(); + done = true; + } + } else { + typename std::map< char, token_type >::const_iterator idelim; + idelim = m_delims_map.find(ch); + if (idelim != m_delims_map.end()) { + done = true; + if (text.empty()) + t = token(m_lineno, (*idelim).second, + std::string("") + ch); + else + m_is.unget(); + } else if (ch == '\n') { + done = true; + if (text.empty()) + t = token(m_lineno, m_nl_type, "<<NEWLINE>>"); + else + m_is.unget(); + } else if (m_skipws && (ch == ' ' || ch == '\t')) { + if (!text.empty()) + done = true; + } else + text += ch; + } + } + + if (!quoted && !text.empty()) { + typename std::map< std::string, token_type >::const_iterator ikw; + ikw = m_keywords_map.find(text); + if (ikw != m_keywords_map.end()) + t = token(m_lineno, (*ikw).second, text); + else + t = token(m_lineno, m_text_type, text); + } + + if (t.type() == m_nl_type) + m_lineno++; + + return t; +} + +template< class IS > +std::string +tokenizer< IS >::rest_of_line(void) +{ + std::string str; + while (m_is.good() && m_is.peek() != '\n') + str += m_is.get(); + return str; +} + +// ------------------------------------------------------------------------ +// The "parser" class. +// ------------------------------------------------------------------------ + +template< class TKZ > +class parser { + TKZ& m_tkz; + token m_last; + parse_errors m_errors; + bool m_thrown; + +public: + parser(TKZ& tkz); + ~parser(void); + + bool good(void) const; + void add_error(const parse_error&); + bool has_errors(void) const; + + token next(void); + std::string rest_of_line(void); + token reset(const token_type&); + + token + expect(const token_type&, + const std::string&); + + token + expect(const token_type&, + const token_type&, + const std::string&); + + token + expect(const token_type&, + const token_type&, + const token_type&, + const std::string&); + + token + expect(const token_type&, + const token_type&, + const token_type&, + const token_type&, + const std::string&); + + token + expect(const token_type&, + const token_type&, + const token_type&, + const token_type&, + const token_type&, + const token_type&, + const token_type&, + const std::string&); + + token + expect(const token_type&, + const token_type&, + const token_type&, + const token_type&, + const token_type&, + const token_type&, + const token_type&, + const token_type&, + const std::string&); +}; + +template< class TKZ > +parser< TKZ >::parser(TKZ& tkz) : + m_tkz(tkz), + m_thrown(false) +{ +} + +template< class TKZ > +parser< TKZ >::~parser(void) +{ + if (!m_errors.empty() && !m_thrown) + throw m_errors; +} + +template< class TKZ > +bool +parser< TKZ >::good(void) + const +{ + return m_tkz.m_is.good(); +} + +template< class TKZ > +void +parser< TKZ >::add_error(const parse_error& pe) +{ + m_errors.push_back(pe); +} + +template< class TKZ > +bool +parser< TKZ >::has_errors(void) + const +{ + return !m_errors.empty(); +} + +template< class TKZ > +token +parser< TKZ >::next(void) +{ + token t = m_tkz.next(); + + m_last = t; + + if (t.type() == m_tkz.m_eof_type) { + if (!m_errors.empty()) { + m_thrown = true; + throw m_errors; + } + } + + return t; +} + +template< class TKZ > +std::string +parser< TKZ >::rest_of_line(void) +{ + return m_tkz.rest_of_line(); +} + +template< class TKZ > +token +parser< TKZ >::reset(const token_type& stop) +{ + token t = m_last; + + while (t.type() != m_tkz.m_eof_type && t.type() != stop) + t = next(); + + return t; +} + +template< class TKZ > +token +parser< TKZ >::expect(const token_type& t1, + const std::string& textual) +{ + token t = next(); + + if (t.type() != t1) + throw parse_error(t.lineno(), + "Unexpected token `" + t.text() + + "'; expected " + textual); + + return t; +} + +template< class TKZ > +token +parser< TKZ >::expect(const token_type& t1, + const token_type& t2, + const std::string& textual) +{ + token t = next(); + + if (t.type() != t1 && t.type() != t2) + throw parse_error(t.lineno(), + "Unexpected token `" + t.text() + + "'; expected " + textual); + + return t; +} + +template< class TKZ > +token +parser< TKZ >::expect(const token_type& t1, + const token_type& t2, + const token_type& t3, + const std::string& textual) +{ + token t = next(); + + if (t.type() != t1 && t.type() != t2 && t.type() != t3) + throw parse_error(t.lineno(), + "Unexpected token `" + t.text() + + "'; expected " + textual); + + return t; +} + +template< class TKZ > +token +parser< TKZ >::expect(const token_type& t1, + const token_type& t2, + const token_type& t3, + const token_type& t4, + const std::string& textual) +{ + token t = next(); + + if (t.type() != t1 && t.type() != t2 && t.type() != t3 && + t.type() != t4) + throw parse_error(t.lineno(), + "Unexpected token `" + t.text() + + "'; expected " + textual); + + return t; +} + +template< class TKZ > +token +parser< TKZ >::expect(const token_type& t1, + const token_type& t2, + const token_type& t3, + const token_type& t4, + const token_type& t5, + const token_type& t6, + const token_type& t7, + const std::string& textual) +{ + token t = next(); + + if (t.type() != t1 && t.type() != t2 && t.type() != t3 && + t.type() != t4 && t.type() != t5 && t.type() != t6 && + t.type() != t7) + throw parse_error(t.lineno(), + "Unexpected token `" + t.text() + + "'; expected " + textual); + + return t; +} + +template< class TKZ > +token +parser< TKZ >::expect(const token_type& t1, + const token_type& t2, + const token_type& t3, + const token_type& t4, + const token_type& t5, + const token_type& t6, + const token_type& t7, + const token_type& t8, + const std::string& textual) +{ + token t = next(); + + if (t.type() != t1 && t.type() != t2 && t.type() != t3 && + t.type() != t4 && t.type() != t5 && t.type() != t6 && + t.type() != t7 && t.type() != t8) + throw parse_error(t.lineno(), + "Unexpected token `" + t.text() + + "'; expected " + textual); + + return t; +} + +#define ATF_PARSER_CALLBACK(parser, func) \ + do { \ + if (!(parser).has_errors()) \ + func; \ + } while (false) + +// ------------------------------------------------------------------------ +// Header parsing. +// ------------------------------------------------------------------------ + +typedef std::map< std::string, std::string > attrs_map; + +class header_entry { + std::string m_name; + std::string m_value; + attrs_map m_attrs; + +public: + header_entry(void); + header_entry(const std::string&, const std::string&, + attrs_map = attrs_map()); + + const std::string& name(void) const; + const std::string& value(void) const; + const attrs_map& attrs(void) const; + bool has_attr(const std::string&) const; + const std::string& get_attr(const std::string&) const; +}; + +typedef std::map< std::string, header_entry > headers_map; + +std::pair< size_t, headers_map > read_headers(std::istream&, size_t); +void write_headers(const headers_map&, std::ostream&); +void validate_content_type(const headers_map&, const std::string&, int); + +} // namespace parser +} // namespace atf + +#endif // !defined(_ATF_CXX_PARSER_HPP_) diff --git a/contrib/atf/atf-c++/detail/parser_test.cpp b/contrib/atf/atf-c++/detail/parser_test.cpp new file mode 100644 index 0000000..491c014 --- /dev/null +++ b/contrib/atf/atf-c++/detail/parser_test.cpp @@ -0,0 +1,1043 @@ +// +// Automated Testing Framework (atf) +// +// 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 <sstream> + +#include "../macros.hpp" + +#include "parser.hpp" +#include "test_helpers.hpp" + +// ------------------------------------------------------------------------ +// Tests for the "parse_error" class. +// ------------------------------------------------------------------------ + +ATF_TEST_CASE(parse_error_to_string); +ATF_TEST_CASE_HEAD(parse_error_to_string) +{ + set_md_var("descr", "Tests the parse_error conversion to strings"); +} +ATF_TEST_CASE_BODY(parse_error_to_string) +{ + using atf::parser::parse_error; + + const parse_error e(123, "This is the message"); + ATF_REQUIRE_EQ("123: This is the message", std::string(e)); +} + +// ------------------------------------------------------------------------ +// Tests for the "parse_errors" class. +// ------------------------------------------------------------------------ + +ATF_TEST_CASE(parse_errors_what); +ATF_TEST_CASE_HEAD(parse_errors_what) +{ + set_md_var("descr", "Tests the parse_errors description"); +} +ATF_TEST_CASE_BODY(parse_errors_what) +{ + using atf::parser::parse_error; + using atf::parser::parse_errors; + + parse_errors es; + es.push_back(parse_error(2, "Second error")); + es.push_back(parse_error(1, "First error")); + + ATF_REQUIRE_EQ("2: Second error\n1: First error", std::string(es.what())); +} + +// ------------------------------------------------------------------------ +// Tests for the "token" class. +// ------------------------------------------------------------------------ + +ATF_TEST_CASE(token_getters); +ATF_TEST_CASE_HEAD(token_getters) +{ + set_md_var("descr", "Tests the token getters"); +} +ATF_TEST_CASE_BODY(token_getters) +{ + using atf::parser::token; + + { + token t(10, 0); + ATF_REQUIRE_EQ(t.lineno(), 10); + ATF_REQUIRE_EQ(t.type(), 0); + ATF_REQUIRE(t.text().empty()); + } + + { + token t(10, 0, "foo"); + ATF_REQUIRE_EQ(t.lineno(), 10); + ATF_REQUIRE_EQ(t.type(), 0); + ATF_REQUIRE_EQ(t.text(), "foo"); + } + + { + token t(20, 1); + ATF_REQUIRE_EQ(t.lineno(), 20); + ATF_REQUIRE_EQ(t.type(), 1); + ATF_REQUIRE(t.text().empty()); + } + + { + token t(20, 1, "bar"); + ATF_REQUIRE_EQ(t.lineno(), 20); + ATF_REQUIRE_EQ(t.type(), 1); + ATF_REQUIRE_EQ(t.text(), "bar"); + } +} + +// ------------------------------------------------------------------------ +// Tests for the "tokenizer" class. +// ------------------------------------------------------------------------ + +#define EXPECT(tkz, ttype, ttext) \ + do { \ + atf::parser::token t = tkz.next(); \ + ATF_REQUIRE(t.type() == ttype); \ + ATF_REQUIRE_EQ(t.text(), ttext); \ + } while (false); + +namespace minimal { + + static const atf::parser::token_type eof_type = 0; + static const atf::parser::token_type nl_type = 1; + static const atf::parser::token_type word_type = 2; + + class tokenizer : public atf::parser::tokenizer< std::istream > { + public: + tokenizer(std::istream& is, bool skipws) : + atf::parser::tokenizer< std::istream > + (is, skipws, eof_type, nl_type, word_type) + { + } + }; + +} + +namespace delims { + + static const atf::parser::token_type eof_type = 0; + static const atf::parser::token_type nl_type = 1; + static const atf::parser::token_type word_type = 2; + static const atf::parser::token_type plus_type = 3; + static const atf::parser::token_type minus_type = 4; + static const atf::parser::token_type equal_type = 5; + + class tokenizer : public atf::parser::tokenizer< std::istream > { + public: + tokenizer(std::istream& is, bool skipws) : + atf::parser::tokenizer< std::istream > + (is, skipws, eof_type, nl_type, word_type) + { + add_delim('+', plus_type); + add_delim('-', minus_type); + add_delim('=', equal_type); + } + }; + +} + +namespace keywords { + + static const atf::parser::token_type eof_type = 0; + static const atf::parser::token_type nl_type = 1; + static const atf::parser::token_type word_type = 2; + static const atf::parser::token_type var_type = 3; + static const atf::parser::token_type loop_type = 4; + static const atf::parser::token_type endloop_type = 5; + + class tokenizer : public atf::parser::tokenizer< std::istream > { + public: + tokenizer(std::istream& is, bool skipws) : + atf::parser::tokenizer< std::istream > + (is, skipws, eof_type, nl_type, word_type) + { + add_keyword("var", var_type); + add_keyword("loop", loop_type); + add_keyword("endloop", endloop_type); + } + }; + +} + +namespace quotes { + + static const atf::parser::token_type eof_type = 0; + static const atf::parser::token_type nl_type = 1; + static const atf::parser::token_type word_type = 2; + static const atf::parser::token_type dblquote_type = 3; + + class tokenizer : public atf::parser::tokenizer< std::istream > { + public: + tokenizer(std::istream& is, bool skipws) : + atf::parser::tokenizer< std::istream > + (is, skipws, eof_type, nl_type, word_type) + { + add_quote('"', dblquote_type); + } + }; + +} + +ATF_TEST_CASE(tokenizer_minimal_nows); +ATF_TEST_CASE_HEAD(tokenizer_minimal_nows) +{ + set_md_var("descr", "Tests the tokenizer class using a minimal parser " + "and not skipping whitespace"); +} +ATF_TEST_CASE_BODY(tokenizer_minimal_nows) +{ + using namespace minimal; + + { + std::istringstream iss(""); + tokenizer mt(iss, false); + + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("\n"); + tokenizer mt(iss, false); + + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("\n\n\n"); + tokenizer mt(iss, false); + + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("line 1"); + tokenizer mt(iss, false); + + EXPECT(mt, word_type, "line 1"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("line 1\n"); + tokenizer mt(iss, false); + + EXPECT(mt, word_type, "line 1"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("line 1\nline 2"); + tokenizer mt(iss, false); + + EXPECT(mt, word_type, "line 1"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, word_type, "line 2"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("line 1\nline 2\nline 3\n"); + tokenizer mt(iss, false); + + EXPECT(mt, word_type, "line 1"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, word_type, "line 2"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, word_type, "line 3"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } +} + +ATF_TEST_CASE(tokenizer_minimal_ws); +ATF_TEST_CASE_HEAD(tokenizer_minimal_ws) +{ + set_md_var("descr", "Tests the tokenizer class using a minimal parser " + "and skipping whitespace"); +} +ATF_TEST_CASE_BODY(tokenizer_minimal_ws) +{ + using namespace minimal; + + { + std::istringstream iss(""); + minimal::tokenizer mt(iss, true); + + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss(" \t "); + tokenizer mt(iss, true); + + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("\n"); + tokenizer mt(iss, true); + + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss(" \t \n \t "); + tokenizer mt(iss, true); + + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("\n\n\n"); + tokenizer mt(iss, true); + + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("line 1"); + tokenizer mt(iss, true); + + EXPECT(mt, word_type, "line"); + EXPECT(mt, word_type, "1"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss(" \tline\t 1\t"); + tokenizer mt(iss, true); + + EXPECT(mt, word_type, "line"); + EXPECT(mt, word_type, "1"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("line 1\n"); + tokenizer mt(iss, true); + + EXPECT(mt, word_type, "line"); + EXPECT(mt, word_type, "1"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("line 1\nline 2"); + tokenizer mt(iss, true); + + EXPECT(mt, word_type, "line"); + EXPECT(mt, word_type, "1"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, word_type, "line"); + EXPECT(mt, word_type, "2"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("line 1\nline 2\nline 3\n"); + tokenizer mt(iss, true); + + EXPECT(mt, word_type, "line"); + EXPECT(mt, word_type, "1"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, word_type, "line"); + EXPECT(mt, word_type, "2"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, word_type, "line"); + EXPECT(mt, word_type, "3"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss(" \t line \t 1\n\tline\t2\n line 3 \n"); + tokenizer mt(iss, true); + + EXPECT(mt, word_type, "line"); + EXPECT(mt, word_type, "1"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, word_type, "line"); + EXPECT(mt, word_type, "2"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, word_type, "line"); + EXPECT(mt, word_type, "3"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } +} + +ATF_TEST_CASE(tokenizer_delims_nows); +ATF_TEST_CASE_HEAD(tokenizer_delims_nows) +{ + set_md_var("descr", "Tests the tokenizer class using a parser with some " + "additional delimiters and not skipping whitespace"); +} +ATF_TEST_CASE_BODY(tokenizer_delims_nows) +{ + using namespace delims; + + { + std::istringstream iss("+-="); + tokenizer mt(iss, false); + + EXPECT(mt, plus_type, "+"); + EXPECT(mt, minus_type, "-"); + EXPECT(mt, equal_type, "="); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("+++"); + tokenizer mt(iss, false); + + EXPECT(mt, plus_type, "+"); + EXPECT(mt, plus_type, "+"); + EXPECT(mt, plus_type, "+"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("\n+\n++\n"); + tokenizer mt(iss, false); + + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, plus_type, "+"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, plus_type, "+"); + EXPECT(mt, plus_type, "+"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("foo+bar=baz"); + tokenizer mt(iss, false); + + EXPECT(mt, word_type, "foo"); + EXPECT(mt, plus_type, "+"); + EXPECT(mt, word_type, "bar"); + EXPECT(mt, equal_type, "="); + EXPECT(mt, word_type, "baz"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss(" foo\t+\tbar = baz "); + tokenizer mt(iss, false); + + EXPECT(mt, word_type, " foo\t"); + EXPECT(mt, plus_type, "+"); + EXPECT(mt, word_type, "\tbar "); + EXPECT(mt, equal_type, "="); + EXPECT(mt, word_type, " baz "); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } +} + +ATF_TEST_CASE(tokenizer_delims_ws); +ATF_TEST_CASE_HEAD(tokenizer_delims_ws) +{ + set_md_var("descr", "Tests the tokenizer class using a parser with some " + "additional delimiters and skipping whitespace"); +} +ATF_TEST_CASE_BODY(tokenizer_delims_ws) +{ + using namespace delims; + + { + std::istringstream iss(" foo\t+\tbar = baz "); + tokenizer mt(iss, true); + + EXPECT(mt, word_type, "foo"); + EXPECT(mt, plus_type, "+"); + EXPECT(mt, word_type, "bar"); + EXPECT(mt, equal_type, "="); + EXPECT(mt, word_type, "baz"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } +} + +ATF_TEST_CASE(tokenizer_keywords_nows); +ATF_TEST_CASE_HEAD(tokenizer_keywords_nows) +{ + set_md_var("descr", "Tests the tokenizer class using a parser with some " + "additional keywords and not skipping whitespace"); +} +ATF_TEST_CASE_BODY(tokenizer_keywords_nows) +{ + using namespace keywords; + + { + std::istringstream iss("var"); + tokenizer mt(iss, false); + + EXPECT(mt, var_type, "var"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("va"); + tokenizer mt(iss, false); + + EXPECT(mt, word_type, "va"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("vara"); + tokenizer mt(iss, false); + + EXPECT(mt, word_type, "vara"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("var "); + tokenizer mt(iss, false); + + EXPECT(mt, word_type, "var "); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("var\nloop\nendloop"); + tokenizer mt(iss, false); + + EXPECT(mt, var_type, "var"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, loop_type, "loop"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, endloop_type, "endloop"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } +} + +ATF_TEST_CASE(tokenizer_keywords_ws); +ATF_TEST_CASE_HEAD(tokenizer_keywords_ws) +{ + set_md_var("descr", "Tests the tokenizer class using a parser with some " + "additional keywords and not skipping whitespace"); +} +ATF_TEST_CASE_BODY(tokenizer_keywords_ws) +{ + using namespace keywords; + + { + std::istringstream iss("var "); + tokenizer mt(iss, true); + + EXPECT(mt, var_type, "var"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss(" var \n\tloop\t\n \tendloop \t"); + tokenizer mt(iss, true); + + EXPECT(mt, var_type, "var"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, loop_type, "loop"); + EXPECT(mt, nl_type, "<<NEWLINE>>"); + EXPECT(mt, endloop_type, "endloop"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("var loop endloop"); + tokenizer mt(iss, true); + + EXPECT(mt, var_type, "var"); + EXPECT(mt, loop_type, "loop"); + EXPECT(mt, endloop_type, "endloop"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } +} + +ATF_TEST_CASE(tokenizer_quotes_nows); +ATF_TEST_CASE_HEAD(tokenizer_quotes_nows) +{ + set_md_var("descr", "Tests the tokenizer class using a parser with " + "quoted strings and not skipping whitespace"); +} +ATF_TEST_CASE_BODY(tokenizer_quotes_nows) +{ + using namespace quotes; + + { + std::istringstream iss("var"); + tokenizer mt(iss, false); + + EXPECT(mt, word_type, "var"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("\"var\""); + tokenizer mt(iss, false); + + EXPECT(mt, word_type, "var"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("var1\"var2\""); + tokenizer mt(iss, false); + + EXPECT(mt, word_type, "var1"); + EXPECT(mt, word_type, "var2"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss("var1\" var2 \""); + tokenizer mt(iss, false); + + EXPECT(mt, word_type, "var1"); + EXPECT(mt, word_type, " var2 "); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } +} + +ATF_TEST_CASE(tokenizer_quotes_ws); +ATF_TEST_CASE_HEAD(tokenizer_quotes_ws) +{ + set_md_var("descr", "Tests the tokenizer class using a parser with " + "quoted strings and skipping whitespace"); +} +ATF_TEST_CASE_BODY(tokenizer_quotes_ws) +{ + using namespace quotes; + + { + std::istringstream iss(" var "); + tokenizer mt(iss, true); + + EXPECT(mt, word_type, "var"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss(" \"var\" "); + tokenizer mt(iss, true); + + EXPECT(mt, word_type, "var"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss(" var1 \"var2\" "); + tokenizer mt(iss, true); + + EXPECT(mt, word_type, "var1"); + EXPECT(mt, word_type, "var2"); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } + + { + std::istringstream iss(" var1 \" var2 \" "); + tokenizer mt(iss, true); + + EXPECT(mt, word_type, "var1"); + EXPECT(mt, word_type, " var2 "); + EXPECT(mt, eof_type, "<<EOF>>"); + EXPECT(mt, eof_type, "<<EOF>>"); + } +} + +// ------------------------------------------------------------------------ +// Tests for the headers parser. +// ------------------------------------------------------------------------ + +class header_reader { + std::istream& m_is; + +public: + header_reader(std::istream& is) : + m_is(is) + { + } + + void + read(void) + { + std::pair< size_t, atf::parser::headers_map > hml = + atf::parser::read_headers(m_is, 1); + atf::parser::validate_content_type(hml.second, + "application/X-atf-headers-test", 1234); + } + + std::vector< std::string > m_calls; +}; + +ATF_TEST_CASE_WITHOUT_HEAD(headers_1); +ATF_TEST_CASE_BODY(headers_1) +{ + const char* input = + "" + ; + + const char* exp_calls[] = { + NULL + }; + + const char* exp_errors[] = { + "1: Unexpected token `<<EOF>>'; expected a header name", + NULL + }; + + do_parser_test< header_reader >(input, exp_calls, exp_errors); +} + +ATF_TEST_CASE_WITHOUT_HEAD(headers_2); +ATF_TEST_CASE_BODY(headers_2) +{ + const char* input = + "Content-Type\n" + ; + + const char* exp_calls[] = { + NULL + }; + + const char* exp_errors[] = { + "1: Unexpected token `<<NEWLINE>>'; expected `:'", + NULL + }; + + do_parser_test< header_reader >(input, exp_calls, exp_errors); +} + +ATF_TEST_CASE_WITHOUT_HEAD(headers_3); +ATF_TEST_CASE_BODY(headers_3) +{ + const char* input = + "Content-Type:\n" + ; + + const char* exp_calls[] = { + NULL + }; + + const char* exp_errors[] = { + "1: Unexpected token `<<NEWLINE>>'; expected a textual value", + NULL + }; + + do_parser_test< header_reader >(input, exp_calls, exp_errors); +} + +ATF_TEST_CASE_WITHOUT_HEAD(headers_4); +ATF_TEST_CASE_BODY(headers_4) +{ + const char* input = + "Content-Type: application/X-atf-headers-test\n" + ; + + const char* exp_calls[] = { + NULL + }; + + const char* exp_errors[] = { + "2: Unexpected token `<<EOF>>'; expected a header name", + NULL + }; + + do_parser_test< header_reader >(input, exp_calls, exp_errors); +} + +ATF_TEST_CASE_WITHOUT_HEAD(headers_5); +ATF_TEST_CASE_BODY(headers_5) +{ + const char* input = + "Content-Type: application/X-atf-headers-test;\n" + ; + + const char* exp_calls[] = { + NULL + }; + + const char* exp_errors[] = { + "1: Unexpected token `<<NEWLINE>>'; expected an attribute name", + NULL + }; + + do_parser_test< header_reader >(input, exp_calls, exp_errors); +} + +ATF_TEST_CASE_WITHOUT_HEAD(headers_6); +ATF_TEST_CASE_BODY(headers_6) +{ + const char* input = + "Content-Type: application/X-atf-headers-test; version\n" + ; + + const char* exp_calls[] = { + NULL + }; + + const char* exp_errors[] = { + "1: Unexpected token `<<NEWLINE>>'; expected `='", + NULL + }; + + do_parser_test< header_reader >(input, exp_calls, exp_errors); +} + +ATF_TEST_CASE_WITHOUT_HEAD(headers_7); +ATF_TEST_CASE_BODY(headers_7) +{ + const char* input = + "Content-Type: application/X-atf-headers-test; version=\n" + ; + + const char* exp_calls[] = { + NULL + }; + + const char* exp_errors[] = { + "1: Unexpected token `<<NEWLINE>>'; expected word or quoted string", + NULL + }; + + do_parser_test< header_reader >(input, exp_calls, exp_errors); +} + +ATF_TEST_CASE_WITHOUT_HEAD(headers_8); +ATF_TEST_CASE_BODY(headers_8) +{ + const char* input = + "Content-Type: application/X-atf-headers-test; version=\"1234\n" + ; + + const char* exp_calls[] = { + NULL + }; + + const char* exp_errors[] = { + "1: Missing double quotes before end of line", + NULL + }; + + do_parser_test< header_reader >(input, exp_calls, exp_errors); +} + +ATF_TEST_CASE_WITHOUT_HEAD(headers_9); +ATF_TEST_CASE_BODY(headers_9) +{ + const char* input = + "Content-Type: application/X-atf-headers-test; version=1234\"\n" + ; + + const char* exp_calls[] = { + NULL + }; + + const char* exp_errors[] = { + "1: Missing double quotes before end of line", + NULL + }; + + do_parser_test< header_reader >(input, exp_calls, exp_errors); +} + +ATF_TEST_CASE_WITHOUT_HEAD(headers_10); +ATF_TEST_CASE_BODY(headers_10) +{ + const char* input = + "Content-Type: application/X-atf-headers-test; version=1234\n" + ; + + const char* exp_calls[] = { + NULL + }; + + const char* exp_errors[] = { + "2: Unexpected token `<<EOF>>'; expected a header name", + NULL + }; + + do_parser_test< header_reader >(input, exp_calls, exp_errors); +} + +ATF_TEST_CASE_WITHOUT_HEAD(headers_11); +ATF_TEST_CASE_BODY(headers_11) +{ + const char* input = + "Content-Type: application/X-atf-headers-test; version=\"1234\"\n" + ; + + const char* exp_calls[] = { + NULL + }; + + const char* exp_errors[] = { + "2: Unexpected token `<<EOF>>'; expected a header name", + NULL + }; + + do_parser_test< header_reader >(input, exp_calls, exp_errors); +} + +ATF_TEST_CASE_WITHOUT_HEAD(headers_12); +ATF_TEST_CASE_BODY(headers_12) +{ + const char* input = + "Content-Type: application/X-atf-headers-test; version=\"1234\"\n" + "a b\n" + "a-b:\n" + "a-b: foo;\n" + "a-b: foo; var\n" + "a-b: foo; var=\n" + "a-b: foo; var=\"a\n" + "a-b: foo; var=a\"\n" + "a-b: foo; var=\"a\";\n" + "a-b: foo; var=\"a\"; second\n" + "a-b: foo; var=\"a\"; second=\n" + "a-b: foo; var=\"a\"; second=\"b\n" + "a-b: foo; var=\"a\"; second=b\"\n" + "a-b: foo; var=\"a\"; second=\"b\"\n" + ; + + const char* exp_calls[] = { + NULL + }; + + const char* exp_errors[] = { + "2: Unexpected token `b'; expected `:'", + "3: Unexpected token `<<NEWLINE>>'; expected a textual value", + "4: Unexpected token `<<NEWLINE>>'; expected an attribute name", + "5: Unexpected token `<<NEWLINE>>'; expected `='", + "6: Unexpected token `<<NEWLINE>>'; expected word or quoted string", + "7: Missing double quotes before end of line", + "8: Missing double quotes before end of line", + "9: Unexpected token `<<NEWLINE>>'; expected an attribute name", + "10: Unexpected token `<<NEWLINE>>'; expected `='", + "11: Unexpected token `<<NEWLINE>>'; expected word or quoted string", + "12: Missing double quotes before end of line", + "13: Missing double quotes before end of line", + NULL + }; + + do_parser_test< header_reader >(input, exp_calls, exp_errors); +} + +// ------------------------------------------------------------------------ +// Main. +// ------------------------------------------------------------------------ + +ATF_INIT_TEST_CASES(tcs) +{ + // Add test cases for the "parse_error" class. + ATF_ADD_TEST_CASE(tcs, parse_error_to_string); + + // Add test cases for the "parse_errors" class. + ATF_ADD_TEST_CASE(tcs, parse_errors_what); + + // Add test cases for the "token" class. + ATF_ADD_TEST_CASE(tcs, token_getters); + + // Add test cases for the "tokenizer" class. + ATF_ADD_TEST_CASE(tcs, tokenizer_minimal_nows); + ATF_ADD_TEST_CASE(tcs, tokenizer_minimal_ws); + ATF_ADD_TEST_CASE(tcs, tokenizer_delims_nows); + ATF_ADD_TEST_CASE(tcs, tokenizer_delims_ws); + ATF_ADD_TEST_CASE(tcs, tokenizer_keywords_nows); + ATF_ADD_TEST_CASE(tcs, tokenizer_keywords_ws); + ATF_ADD_TEST_CASE(tcs, tokenizer_quotes_nows); + ATF_ADD_TEST_CASE(tcs, tokenizer_quotes_ws); + + // Add the tests for the headers parser. + + // Add the test cases for the header file. + ATF_ADD_TEST_CASE(tcs, headers_1); + ATF_ADD_TEST_CASE(tcs, headers_2); + ATF_ADD_TEST_CASE(tcs, headers_3); + ATF_ADD_TEST_CASE(tcs, headers_4); + ATF_ADD_TEST_CASE(tcs, headers_5); + ATF_ADD_TEST_CASE(tcs, headers_6); + ATF_ADD_TEST_CASE(tcs, headers_7); + ATF_ADD_TEST_CASE(tcs, headers_8); + ATF_ADD_TEST_CASE(tcs, headers_9); + ATF_ADD_TEST_CASE(tcs, headers_10); + ATF_ADD_TEST_CASE(tcs, headers_11); + ATF_ADD_TEST_CASE(tcs, headers_12); +} diff --git a/contrib/atf/atf-c++/detail/process.cpp b/contrib/atf/atf-c++/detail/process.cpp new file mode 100644 index 0000000..deb1158 --- /dev/null +++ b/contrib/atf/atf-c++/detail/process.cpp @@ -0,0 +1,355 @@ +// +// Automated Testing Framework (atf) +// +// 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. +// + +extern "C" { +#include <signal.h> + +#include "../../atf-c/error.h" + +#include "../../atf-c/detail/process.h" +} + +#include <iostream> + +#include "exceptions.hpp" +#include "process.hpp" +#include "sanity.hpp" + +namespace detail = atf::process::detail; +namespace impl = atf::process; +#define IMPL_NAME "atf::process" + +// ------------------------------------------------------------------------ +// Auxiliary functions. +// ------------------------------------------------------------------------ + +template< class C > +atf::utils::auto_array< const char* > +collection_to_argv(const C& c) +{ + atf::utils::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) +{ + // This is a weird hack to ensure that the output of the parent process + // is flushed before executing a child which prevents, for example, the + // output of the atf-run hooks to appear before the output of atf-run + // itself. + // + // 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/contrib/atf/atf-c++/detail/process.hpp b/contrib/atf/atf-c++/detail/process.hpp new file mode 100644 index 0000000..6e33e00 --- /dev/null +++ b/contrib/atf/atf-c++/detail/process.hpp @@ -0,0 +1,280 @@ +// +// Automated Testing Framework (atf) +// +// 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_PROCESS_HPP_) +#define _ATF_CXX_PROCESS_HPP_ + +extern "C" { +#include <sys/types.h> + +#include "../../atf-c/error.h" + +#include "../../atf-c/detail/process.h" +} + +#include <string> +#include <vector> + +#include "exceptions.hpp" +#include "fs.hpp" + +#include "../utils.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. + utils::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_PROCESS_HPP_) diff --git a/contrib/atf/atf-c++/detail/process_test.cpp b/contrib/atf/atf-c++/detail/process_test.cpp new file mode 100644 index 0000000..d13ab94 --- /dev/null +++ b/contrib/atf/atf-c++/detail/process_test.cpp @@ -0,0 +1,357 @@ +// +// Automated Testing Framework (atf) +// +// 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 <cstdlib> +#include <cstring> + +#include "../macros.hpp" + +#include "process.hpp" +#include "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).leaf_name()); + argv.push_back(helper_name); + + return exec(get_process_helpers_path(tc), + 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/contrib/atf/atf-c++/detail/sanity.hpp b/contrib/atf/atf-c++/detail/sanity.hpp new file mode 100644 index 0000000..6021a6e --- /dev/null +++ b/contrib/atf/atf-c++/detail/sanity.hpp @@ -0,0 +1,37 @@ +// +// Automated Testing Framework (atf) +// +// 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_SANITY_HPP_) +#define _ATF_CXX_SANITY_HPP_ + +extern "C" { +#include "../../atf-c/detail/sanity.h" +} + +#endif // !defined(_ATF_CXX_SANITY_HPP_) diff --git a/contrib/atf/atf-c++/detail/sanity_test.cpp b/contrib/atf/atf-c++/detail/sanity_test.cpp new file mode 100644 index 0000000..8d3a07e --- /dev/null +++ b/contrib/atf/atf-c++/detail/sanity_test.cpp @@ -0,0 +1,41 @@ +// +// Automated Testing Framework (atf) +// +// 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 "../macros.hpp" + +ATF_TEST_CASE_WITHOUT_HEAD(nothing); +ATF_TEST_CASE_BODY(nothing) +{ + // TODO +} + +ATF_INIT_TEST_CASES(tcs) +{ + ATF_ADD_TEST_CASE(tcs, nothing); +} diff --git a/contrib/atf/atf-c++/detail/test_helpers.cpp b/contrib/atf/atf-c++/detail/test_helpers.cpp new file mode 100644 index 0000000..42bd711 --- /dev/null +++ b/contrib/atf/atf-c++/detail/test_helpers.cpp @@ -0,0 +1,160 @@ +// +// Automated Testing Framework (atf) +// +// 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. +// + +extern "C" { +#include <regex.h> +} + +#include <fstream> +#include <iostream> +#include <string> +#include <vector> + +#include "../check.hpp" +#include "../config.hpp" +#include "../macros.hpp" + +#include "fs.hpp" +#include "process.hpp" +#include "test_helpers.hpp" + +void +build_check_cxx_o_aux(const atf::fs::path& sfile, const char* failmsg, + const bool expect_pass) +{ + std::vector< std::string > optargs; + optargs.push_back("-I" + atf::config::get("atf_includedir")); + optargs.push_back("-Wall"); + optargs.push_back("-Werror"); + + const bool result = atf::check::build_cxx_o( + sfile.str(), "test.o", atf::process::argv_array(optargs)); + if ((expect_pass && !result) || (!expect_pass && result)) + ATF_FAIL(failmsg); +} + +void +build_check_cxx_o(const atf::tests::tc& tc, const char* sfile, + const char* failmsg, const bool expect_pass) +{ + const atf::fs::path sfilepath = + atf::fs::path(tc.get_config_var("srcdir")) / sfile; + build_check_cxx_o_aux(sfilepath, failmsg, expect_pass); +} + +void +header_check(const char *hdrname) +{ + std::ofstream srcfile("test.c"); + ATF_REQUIRE(srcfile); + srcfile << "#include <" << hdrname << ">\n"; + srcfile.close(); + + const std::string failmsg = std::string("Header check failed; ") + + hdrname + " is not self-contained"; + build_check_cxx_o_aux(atf::fs::path("test.c"), failmsg.c_str(), true); +} + +atf::fs::path +get_process_helpers_path(const atf::tests::tc& tc) +{ + return atf::fs::path(tc.get_config_var("srcdir")) / + ".." / "atf-c" / "detail" / "process_helpers"; +} + +bool +grep_file(const char* name, const char* regex) +{ + std::ifstream is(name); + ATF_REQUIRE(is); + + bool found = false; + + std::string line; + std::getline(is, line); + while (!found && is.good()) { + if (grep_string(line, regex)) + found = true; + else + std::getline(is, line); + } + + return found; +} + +bool +grep_string(const std::string& str, const char* regex) +{ + int res; + regex_t preg; + + std::cout << "Looking for '" << regex << "' in '" << str << "'\n"; + ATF_REQUIRE(::regcomp(&preg, regex, REG_EXTENDED) == 0); + + res = ::regexec(&preg, str.c_str(), 0, NULL, 0); + ATF_REQUIRE(res == 0 || res == REG_NOMATCH); + + ::regfree(&preg); + + return res == 0; +} + +void +test_helpers_detail::check_equal(const char* expected[], + const string_vector& actual) +{ + const char** expected_iter = expected; + string_vector::const_iterator actual_iter = actual.begin(); + + bool equals = true; + while (equals && *expected_iter != NULL && actual_iter != actual.end()) { + if (*expected_iter != *actual_iter) { + equals = false; + } else { + expected_iter++; + actual_iter++; + } + } + if (equals && ((*expected_iter == NULL && actual_iter != actual.end()) || + (*expected_iter != NULL && actual_iter == actual.end()))) + equals = false; + + if (!equals) { + std::cerr << "EXPECTED:\n"; + for (expected_iter = expected; *expected_iter != NULL; expected_iter++) + std::cerr << *expected_iter << "\n"; + + std::cerr << "ACTUAL:\n"; + for (actual_iter = actual.begin(); actual_iter != actual.end(); + actual_iter++) + std::cerr << *actual_iter << "\n"; + + ATF_FAIL("Expected results differ to actual values"); + } +} diff --git a/contrib/atf/atf-c++/detail/test_helpers.hpp b/contrib/atf/atf-c++/detail/test_helpers.hpp new file mode 100644 index 0000000..059a0a5 --- /dev/null +++ b/contrib/atf/atf-c++/detail/test_helpers.hpp @@ -0,0 +1,166 @@ +// +// Automated Testing Framework (atf) +// +// 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(TESTS_ATF_ATF_CXX_TEST_HELPERS_H) +# error "Cannot include test_helpers.hpp more than once." +#else +# define TESTS_ATF_ATF_CXX_TEST_HELPERS_H +#endif + +#include <cstdlib> +#include <iostream> +#include <sstream> +#include <utility> + +#include "../macros.hpp" +#include "../tests.hpp" +#include "parser.hpp" +#include "process.hpp" +#include "text.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) \ + { \ + build_check_cxx_o(*this, sfile, failmsg, true); \ + } + +#define BUILD_TC_FAIL(name, sfile, descr, failmsg) \ + ATF_TEST_CASE(name); \ + ATF_TEST_CASE_HEAD(name) \ + { \ + set_md_var("descr", descr); \ + } \ + ATF_TEST_CASE_BODY(name) \ + { \ + build_check_cxx_o(*this, sfile, failmsg, false); \ + } + +namespace atf { +namespace tests { +class tc; +} +} + +void header_check(const char*); +void build_check_cxx_o(const atf::tests::tc&, const char*, const char*, bool); +atf::fs::path get_process_helpers_path(const atf::tests::tc&); +bool grep_file(const char*, const char*); +bool grep_string(const std::string&, const char*); + +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()); +} + +namespace test_helpers_detail { + +typedef std::vector< std::string > string_vector; + +template< class Reader > +std::pair< string_vector, string_vector > +do_read(const char* input) +{ + string_vector errors; + + std::istringstream is(input); + Reader reader(is); + try { + reader.read(); + } catch (const atf::parser::parse_errors& pes) { + for (std::vector< atf::parser::parse_error >::const_iterator iter = + pes.begin(); iter != pes.end(); iter++) + errors.push_back(*iter); + } catch (const atf::parser::parse_error& pe) { + ATF_FAIL("Raised a lonely parse error: " + + atf::text::to_string(pe.first) + ": " + pe.second); + } + + return std::make_pair(reader.m_calls, errors); +} + +void check_equal(const char*[], const string_vector&); + +} // namespace test_helpers_detail + +template< class Reader > +void +do_parser_test(const char* input, const char* exp_calls[], + const char* exp_errors[]) +{ + const std::pair< test_helpers_detail::string_vector, + test_helpers_detail::string_vector > + actual = test_helpers_detail::do_read< Reader >(input); + test_helpers_detail::check_equal(exp_calls, actual.first); + test_helpers_detail::check_equal(exp_errors, actual.second); +} diff --git a/contrib/atf/atf-c++/detail/text.cpp b/contrib/atf/atf-c++/detail/text.cpp new file mode 100644 index 0000000..66eebf0 --- /dev/null +++ b/contrib/atf/atf-c++/detail/text.cpp @@ -0,0 +1,160 @@ +// +// Automated Testing Framework (atf) +// +// 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. +// + +extern "C" { +#include <regex.h> +} + +#include <cctype> +#include <cstring> + +extern "C" { +#include "../../atf-c/error.h" + +#include "../../atf-c/detail/text.h" +} + +#include "exceptions.hpp" +#include "text.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/contrib/atf/atf-c++/detail/text.hpp b/contrib/atf/atf-c++/detail/text.hpp new file mode 100644 index 0000000..6a1b027 --- /dev/null +++ b/contrib/atf/atf-c++/detail/text.hpp @@ -0,0 +1,153 @@ +// +// Automated Testing Framework (atf) +// +// 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_TEXT_HPP_) +#define _ATF_CXX_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_TEXT_HPP_) diff --git a/contrib/atf/atf-c++/detail/text_test.cpp b/contrib/atf/atf-c++/detail/text_test.cpp new file mode 100644 index 0000000..b7c0ba1 --- /dev/null +++ b/contrib/atf/atf-c++/detail/text_test.cpp @@ -0,0 +1,390 @@ +// +// Automated Testing Framework (atf) +// +// 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 <cstring> +#include <set> +#include <vector> + +#include "../macros.hpp" + +#include "text.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/contrib/atf/atf-c++/detail/ui.cpp b/contrib/atf/atf-c++/detail/ui.cpp new file mode 100644 index 0000000..07bde4f --- /dev/null +++ b/contrib/atf/atf-c++/detail/ui.cpp @@ -0,0 +1,173 @@ +// +// Automated Testing Framework (atf) +// +// 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. +// + +extern "C" { +#include <sys/ioctl.h> + +#include <termios.h> +#include <unistd.h> +} + +#include <sstream> + +#include "env.hpp" +#include "text.hpp" +#include "sanity.hpp" +#include "text.hpp" +#include "ui.hpp" + +namespace impl = atf::ui; +#define IMPL_NAME "atf::ui" + +static +size_t +terminal_width(void) +{ + static bool done = false; + static size_t width = 0; + + if (!done) { + if (atf::env::has("COLUMNS")) { + const std::string cols = atf::env::get("COLUMNS"); + if (cols.length() > 0) { + width = atf::text::to_type< size_t >(cols); + } + } else { + struct winsize ws; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) + width = ws.ws_col; + } + + if (width >= 80) + width -= 5; + + done = true; + } + + return width; +} + +static +std::string +format_paragraph(const std::string& text, + const std::string& tag, + const bool first, + const bool repeat, + const size_t col) +{ + PRE(text.find('\n') == std::string::npos); + + const std::string pad(col - tag.length(), ' '); + const std::string fullpad(col, ' '); + + std::string formatted; + if (first || repeat) + formatted = tag + pad; + else + formatted = fullpad; + INV(formatted.length() == col); + size_t curcol = col; + + const size_t maxcol = terminal_width(); + + std::vector< std::string > words = atf::text::split(text, " "); + for (std::vector< std::string >::const_iterator iter = words.begin(); + iter != words.end(); iter++) { + const std::string& word = *iter; + + if (iter != words.begin() && maxcol > 0 && + curcol + word.length() + 1 > maxcol) { + if (repeat) + formatted += '\n' + tag + pad; + else + formatted += '\n' + fullpad; + curcol = col; + } else if (iter != words.begin()) { + formatted += ' '; + curcol++; + } + + formatted += word; + curcol += word.length(); + } + + return formatted; +} + +std::string +impl::format_error(const std::string& prog_name, const std::string& error) +{ + return format_text_with_tag("ERROR: " + error, prog_name + ": ", true); +} + +std::string +impl::format_info(const std::string& prog_name, const std::string& msg) +{ + return format_text_with_tag(msg, prog_name + ": ", true); +} + +std::string +impl::format_text(const std::string& text) +{ + return format_text_with_tag(text, "", false, 0); +} + +std::string +impl::format_text_with_tag(const std::string& text, const std::string& tag, + bool repeat, size_t col) +{ + PRE(col == 0 || col >= tag.length()); + if (col == 0) + col = tag.length(); + + std::string formatted; + + std::vector< std::string > lines = atf::text::split(text, "\n"); + for (std::vector< std::string >::const_iterator iter = lines.begin(); + iter != lines.end(); iter++) { + const std::string& line = *iter; + + formatted += format_paragraph(line, tag, iter == lines.begin(), + repeat, col); + if (iter + 1 != lines.end()) { + if (repeat) + formatted += "\n" + tag + "\n"; + else + formatted += "\n\n"; + } + } + + return formatted; +} + +std::string +impl::format_warning(const std::string& prog_name, const std::string& error) +{ + return format_text_with_tag("WARNING: " + error, prog_name + ": ", true); +} diff --git a/contrib/atf/atf-c++/detail/ui.hpp b/contrib/atf/atf-c++/detail/ui.hpp new file mode 100644 index 0000000..1c81c56 --- /dev/null +++ b/contrib/atf/atf-c++/detail/ui.hpp @@ -0,0 +1,105 @@ +// +// Automated Testing Framework (atf) +// +// 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_UI_HPP_) +#define _ATF_CXX_UI_HPP_ + +#include <string> + +namespace atf { +namespace ui { + +//! +//! \brief Formats an error message to fit on screen. +//! +//! Given the program's name and an error message, properly formats it to +//! fit on screen. +//! +//! The program's name is not stored globally to prevent the usage of this +//! function from inside the library. Making it a explicit parameter +//! restricts its usage to the frontend. +//! +std::string format_error(const std::string&, const std::string&); + +//! +//! \brief Formats an informational message to fit on screen. +//! +//! Given the program's name and an informational message, properly formats +//! it to fit on screen. +//! +//! The program's name is not stored globally to prevent the usage of this +//! function from inside the library. Making it a explicit parameter +//! restricts its usage to the frontend. +//! +std::string format_info(const std::string&, const std::string&); + +//! +//! \brief Formats a block of text to fit nicely on screen. +//! +//! Given a text, which is composed of multiple paragraphs separated by +//! a single '\n' character, reformats it to fill on the current screen's +//! width with proper line wrapping. +//! +//! This is just a special case of format_text_with_tag, provided for +//! simplicity. +//! +std::string format_text(const std::string&); + +//! +//! \brief Formats a block of text to fit nicely on screen, prepending a +//! tag to it. +//! +//! Given a text, which is composed of multiple paragraphs separated by +//! a single '\n' character, reformats it to fill on the current screen's +//! width with proper line wrapping. The text is prepended with a tag; +//! i.e. a word that is printed at the beginning of the first paragraph and +//! optionally repeated at the beginning of each word. The last parameter +//! specifies the column on which the text should start, and that position +//! must be greater than the tag's length or 0, in which case it +//! automatically takes the correct value. +//! +std::string format_text_with_tag(const std::string&, const std::string&, + bool, size_t = 0); + +//! +//! \brief Formats a warning message to fit on screen. +//! +//! Given the program's name and a warning message, properly formats it to +//! fit on screen. +//! +//! The program's name is not stored globally to prevent the usage of this +//! function from inside the library. Making it a explicit parameter +//! restricts its usage to the frontend. +//! +std::string format_warning(const std::string&, const std::string&); + +} // namespace ui +} // namespace atf + +#endif // !defined(_ATF_CXX_UI_HPP_) diff --git a/contrib/atf/atf-c++/detail/ui_test.cpp b/contrib/atf/atf-c++/detail/ui_test.cpp new file mode 100644 index 0000000..918d028 --- /dev/null +++ b/contrib/atf/atf-c++/detail/ui_test.cpp @@ -0,0 +1,462 @@ +// +// Automated Testing Framework (atf) +// +// 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 <cstring> +#include <iostream> + +#include "../macros.hpp" + +#include "env.hpp" +#include "ui.hpp" + +// ------------------------------------------------------------------------ +// Test cases for the free functions. +// ------------------------------------------------------------------------ + +struct test { + const char *tc; + const char *tag; + bool repeat; + size_t col; + const char *fmt; + const char *result; +} tests[] = { + // + // wo_tag + // + + { + "wo_tag", + "", + false, + 0, + "12345", + "12345", + }, + + { + "wo_tag", + "", + false, + 0, + "12345 ", + "12345", + }, + + { + "wo_tag", + "", + false, + 0, + "12345 7890", + "12345 7890", + }, + + { + "wo_tag", + "", + false, + 0, + "12345 789012 45", + "12345 789012 45", + }, + + { + "wo_tag", + "", + false, + 0, + "12345 789012 456", + "12345 789012\n456", + }, + + { + "wo_tag", + "", + false, + 0, + "1234567890123456", + "1234567890123456", + }, + + // TODO(jmmv): Fix the code to pass this test... +// { +// "wo_tag", +// "", +// false, +// 0, +// " 2345678901234567", +// "\n2345678901234567", +// }, + + { + "wo_tag", + "", + false, + 0, + "12345 789012345 78", + "12345 789012345\n78", + }, + + // + // wo_tag_col + // + + { + "wo_tag_col", + "", + false, + 10, + "12345", + " 12345", + }, + + { + "wo_tag_col", + "", + false, + 10, + "12345 7890", + " 12345\n" + " 7890", + }, + + { + "wo_tag_col", + "", + false, + 10, + "1 3 5 7 9", + " 1 3 5\n" + " 7 9", + }, + + // + // w_tag_no_repeat + // + + { + "w_tag_no_repeat", + "1234: ", + false, + 0, + "789012345", + "1234: 789012345", + }, + + { + "w_tag_no_repeat", + "1234: ", + false, + 0, + "789 1234 56789", + "1234: 789 1234\n" + " 56789", + }, + + { + "w_tag_no_repeat", + "1234: ", + false, + 0, + "789012345", + "1234: 789012345", + }, + + { + "w_tag_no_repeat", + "1234: ", + false, + 0, + "789012345 7890", + "1234: 789012345\n" + " 7890", + }, + + // + // w_tag_repeat + // + + { + "w_tag_repeat", + "1234: ", + true, + 0, + "789012345", + "1234: 789012345", + }, + + { + "w_tag_repeat", + "1234: ", + true, + 0, + "789 1234 56789", + "1234: 789 1234\n" + "1234: 56789", + }, + + { + "w_tag_repeat", + "1234: ", + true, + 0, + "789012345", + "1234: 789012345", + }, + + { + "w_tag_no_repeat", + "1234: ", + true, + 0, + "789012345 7890", + "1234: 789012345\n" + "1234: 7890", + }, + + // + // w_tag_col + // + + { + "w_tag_col", + "1234:", + false, + 10, + "1 3 5", + "1234: 1 3 5", + }, + + { + "w_tag_col", + "1234:", + false, + 10, + "1 3 5 7 9", + "1234: 1 3 5\n" + " 7 9", + }, + + { + "w_tag_col", + "1234:", + true, + 10, + "1 3 5 7 9", + "1234: 1 3 5\n" + "1234: 7 9", + }, + + // + // paragraphs + // + + { + "paragraphs", + "", + false, + 0, + "1 3 5\n\n", + "1 3 5" + }, + + { + "paragraphs", + "", + false, + 0, + "1 3 5\n2 4 6", + "1 3 5\n\n2 4 6" + }, + + { + "paragraphs", + "", + false, + 0, + "1234 6789 123456\n2 4 6", + "1234 6789\n123456\n\n2 4 6" + }, + + { + "paragraphs", + "12: ", + false, + 0, + "56789 123456\n2 4 6", + "12: 56789\n 123456\n\n 2 4 6" + }, + + { + "paragraphs", + "12: ", + true, + 0, + "56789 123456\n2 4 6", + "12: 56789\n12: 123456\n12: \n12: 2 4 6" + }, + + { + "paragraphs", + "12:", + false, + 4, + "56789 123456\n2 4 6", + "12: 56789\n 123456\n\n 2 4 6" + }, + + { + "paragraphs", + "12:", + true, + 4, + "56789 123456\n2 4 6", + "12: 56789\n12: 123456\n12:\n12: 2 4 6" + }, + + // + // end + // + + { + NULL, + NULL, + false, + 0, + NULL, + NULL, + }, +}; + +static +void +run_tests(const char *tc) +{ + struct test *t; + + std::cout << "Running tests for " << tc << "\n"; + + atf::env::set("COLUMNS", "15"); + + for (t = &tests[0]; t->tc != NULL; t++) { + if (std::strcmp(t->tc, tc) == 0) { + std::cout << "\n"; + std::cout << "Testing with tag '" << t->tag << "', '" + << (t->repeat ? "repeat" : "no repeat") << "', col " + << t->col << "\n"; + std::cout << "Input: >>>" << t->fmt << "<<<\n"; + std::cout << "Expected output: >>>" << t->result << "<<<\n"; + + std::string result = atf::ui::format_text_with_tag(t->fmt, t->tag, + t->repeat, t->col); + std::cout << "Output : >>>" << result << "<<<\n"; + ATF_REQUIRE_EQ(t->result, result); + } + } +} + +ATF_TEST_CASE(wo_tag); +ATF_TEST_CASE_HEAD(wo_tag) +{ + set_md_var("descr", "Checks formatting without tags"); +} +ATF_TEST_CASE_BODY(wo_tag) +{ + run_tests("wo_tag"); +} + +ATF_TEST_CASE(wo_tag_col); +ATF_TEST_CASE_HEAD(wo_tag_col) +{ + set_md_var("descr", "Checks formatting without tags and with a non-zero " + "starting column"); +} +ATF_TEST_CASE_BODY(wo_tag_col) +{ + run_tests("wo_tag_col"); +} + +ATF_TEST_CASE(w_tag_no_repeat); +ATF_TEST_CASE_HEAD(w_tag_no_repeat) +{ + set_md_var("descr", "Checks formatting with a tag"); +} +ATF_TEST_CASE_BODY(w_tag_no_repeat) +{ + run_tests("w_tag_no_repeat"); +} + +ATF_TEST_CASE(w_tag_repeat); +ATF_TEST_CASE_HEAD(w_tag_repeat) +{ + set_md_var("descr", "Checks formatting with a tag and repeating it on " + "each line"); +} +ATF_TEST_CASE_BODY(w_tag_repeat) +{ + run_tests("w_tag_repeat"); +} + +ATF_TEST_CASE(w_tag_col); +ATF_TEST_CASE_HEAD(w_tag_col) +{ + set_md_var("descr", "Checks formatting with a tag and starting at a " + "column greater than its length"); +} +ATF_TEST_CASE_BODY(w_tag_col) +{ + run_tests("w_tag_col"); +} + +ATF_TEST_CASE(paragraphs); +ATF_TEST_CASE_HEAD(paragraphs) +{ + set_md_var("descr", "Checks formatting a string that contains multiple " + "paragraphs"); +} +ATF_TEST_CASE_BODY(paragraphs) +{ + run_tests("paragraphs"); +} + +// ------------------------------------------------------------------------ +// Main. +// ------------------------------------------------------------------------ + +ATF_INIT_TEST_CASES(tcs) +{ + // Add the test cases for the free functions. + ATF_ADD_TEST_CASE(tcs, wo_tag); + ATF_ADD_TEST_CASE(tcs, wo_tag_col); + ATF_ADD_TEST_CASE(tcs, w_tag_no_repeat); + ATF_ADD_TEST_CASE(tcs, w_tag_repeat); + ATF_ADD_TEST_CASE(tcs, w_tag_col); + ATF_ADD_TEST_CASE(tcs, paragraphs); +} diff --git a/contrib/atf/atf-c++/macros.hpp b/contrib/atf/atf-c++/macros.hpp new file mode 100644 index 0000000..c6ce9c2 --- /dev/null +++ b/contrib/atf/atf-c++/macros.hpp @@ -0,0 +1,222 @@ +// +// Automated Testing Framework (atf) +// +// 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(x) \ + do { \ + if (!(x)) { \ + std::ostringstream atfu_ss; \ + atfu_ss << "Line " << __LINE__ << ": " << #x << " not met"; \ + atf::tests::tc::fail(atfu_ss.str()); \ + } \ + } while (false) + +#define ATF_REQUIRE_EQ(x, y) \ + do { \ + if ((x) != (y)) { \ + std::ostringstream atfu_ss; \ + atfu_ss << "Line " << __LINE__ << ": " << #x << " != " << #y \ + << " (" << (x) << " != " << (y) << ")"; \ + 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(e, x) \ + do { \ + try { \ + x; \ + std::ostringstream atfu_ss; \ + atfu_ss << "Line " << __LINE__ << ": " #x " did not throw " \ + #e " as expected"; \ + atf::tests::tc::fail(atfu_ss.str()); \ + } catch (const e&) { \ + } catch (const std::exception& atfu_e) { \ + std::ostringstream atfu_ss; \ + atfu_ss << "Line " << __LINE__ << ": " #x " threw an " \ + "unexpected error (not " #e "): " << atfu_e.what(); \ + atf::tests::tc::fail(atfu_ss.str()); \ + } catch (...) { \ + std::ostringstream atfu_ss; \ + atfu_ss << "Line " << __LINE__ << ": " #x " threw an " \ + "unexpected error (not " #e ")"; \ + atf::tests::tc::fail(atfu_ss.str()); \ + } \ + } while (false) + +#define ATF_REQUIRE_THROW_RE(type, regexp, x) \ + do { \ + try { \ + x; \ + std::ostringstream atfu_ss; \ + atfu_ss << "Line " << __LINE__ << ": " #x " did not throw " \ + #type " as expected"; \ + atf::tests::tc::fail(atfu_ss.str()); \ + } catch (const type& e) { \ + if (!atf::tests::detail::match(regexp, e.what())) { \ + std::ostringstream atfu_ss; \ + atfu_ss << "Line " << __LINE__ << ": " #x " threw " #type "(" \ + << 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__ << ": " #x " threw an " \ + "unexpected error (not " #type "): " << atfu_e.what(); \ + atf::tests::tc::fail(atfu_ss.str()); \ + } catch (...) { \ + std::ostringstream atfu_ss; \ + atfu_ss << "Line " << __LINE__ << ": " #x " threw an " \ + "unexpected error (not " #type ")"; \ + atf::tests::tc::fail(atfu_ss.str()); \ + } \ + } while (false) + +#define ATF_CHECK_ERRNO(exp_errno, bool_expr) \ + atf::tests::tc::check_errno(__FILE__, __LINE__, exp_errno, #bool_expr, \ + bool_expr) + +#define ATF_REQUIRE_ERRNO(exp_errno, bool_expr) \ + atf::tests::tc::require_errno(__FILE__, __LINE__, exp_errno, #bool_expr, \ + bool_expr) + +#define ATF_INIT_TEST_CASES(tcs) \ + namespace atf { \ + namespace tests { \ + int run_tp(int, char* const*, \ + void (*)(std::vector< atf::tests::tc * >&)); \ + } \ + } \ + \ + static void atfu_init_tcs(std::vector< atf::tests::tc * >&); \ + \ + int \ + main(int argc, char* const* 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/contrib/atf/atf-c++/macros_hpp_test.cpp b/contrib/atf/atf-c++/macros_hpp_test.cpp new file mode 100644 index 0000000..2cb7536 --- /dev/null +++ b/contrib/atf/atf-c++/macros_hpp_test.cpp @@ -0,0 +1,130 @@ +// +// Automated Testing Framework (atf) +// +// 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 <stdexcept> + +#include <atf-c++/macros.hpp> + +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/contrib/atf/atf-c++/macros_test.cpp b/contrib/atf/atf-c++/macros_test.cpp new file mode 100644 index 0000000..57e19e0 --- /dev/null +++ b/contrib/atf/atf-c++/macros_test.cpp @@ -0,0 +1,793 @@ +// +// Automated Testing Framework (atf) +// +// 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. +// + +extern "C" { +#include <fcntl.h> +#include <unistd.h> +} + +#include <cerrno> +#include <cstdlib> +#include <iostream> +#include <stdexcept> + +#include "macros.hpp" + +#include "detail/fs.hpp" +#include "detail/process.hpp" +#include "detail/sanity.hpp" +#include "detail/test_helpers.hpp" +#include "detail/text.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(grep_file("result", "^passed")); + 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(grep_file("result", "^failed: Failed on purpose")); + 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(grep_file("result", "^skipped: Skipped on purpose")); + 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(grep_file("result", "^passed")); + ATF_REQUIRE(atf::fs::exists(after)); + } else { + ATF_REQUIRE(grep_file("result", "^failed: .*condition not met")); + 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(grep_file("result", "^passed")); + ATF_REQUIRE(atf::fs::exists(after)); + } else { + ATF_REQUIRE(grep_file("result", "^failed: .*v1 != v2")); + 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(grep_file("result", "^passed")); + ATF_REQUIRE(atf::fs::exists(after)); + } else { + ATF_REQUIRE(grep_file("result", "^failed: ")); + 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(grep_file("result", "^passed")); + ATF_REQUIRE(atf::fs::exists(after)); + } else { + ATF_REQUIRE(grep_file("result", "^failed: ")); + 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(grep_file("result", "^passed")); + ATF_REQUIRE(atf::fs::exists(after)); + } else { + ATF_REQUIRE(grep_file("result", "^failed: ")); + 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(grep_file("result", "^passed")); + 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(grep_file("result", exp_result.c_str())); + 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(grep_file("result", "^passed")); + 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(grep_file("result", exp_result.c_str())); + 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(grep_file("result", "^passed")); + } else { + ATF_REQUIRE(grep_file("result", "^failed")); + + std::string exp_result = "macros_test.cpp:[0-9]+: " + + std::string(t->msg) + "$"; + ATF_REQUIRE(grep_file("stderr", exp_result.c_str())); + } + + 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(grep_file("result", "^passed")); + ATF_REQUIRE(atf::fs::exists(after)); + } else { + std::string exp_result = "^failed: .*macros_test.cpp:[0-9]+: " + + std::string(t->msg) + "$"; + ATF_REQUIRE(grep_file("result", exp_result.c_str())); + + ATF_REQUIRE(!atf::fs::exists(after)); + } + + atf::fs::remove(before); + if (t->ok) + atf::fs::remove(after); + } +} + +// ------------------------------------------------------------------------ +// Tests cases for the header file. +// ------------------------------------------------------------------------ + +HEADER_TC(include, "atf-c++/macros.hpp"); +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"); +BUILD_TC_FAIL(detect_unused_tests, "unused_test.cpp", + "Tests that defining an unused test case raises a warning (and thus " + "an error)", + "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, include); + ATF_ADD_TEST_CASE(tcs, use); + ATF_ADD_TEST_CASE(tcs, detect_unused_tests); +} diff --git a/contrib/atf/atf-c++/pkg_config_test.sh b/contrib/atf/atf-c++/pkg_config_test.sh new file mode 100644 index 0000000..8409902 --- /dev/null +++ b/contrib/atf/atf-c++/pkg_config_test.sh @@ -0,0 +1,149 @@ +# +# Automated Testing Framework (atf) +# +# 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() +{ + atf_check -s eq:0 -o save:stdout -e empty -x \ + "atf-version | head -n 1 | cut -d ' ' -f 4" + ver1=$(cat stdout) + echo "Version reported by atf-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/contrib/atf/atf-c++/tests.cpp b/contrib/atf/atf-c++/tests.cpp new file mode 100644 index 0000000..cdc0dfb --- /dev/null +++ b/contrib/atf/atf-c++/tests.cpp @@ -0,0 +1,710 @@ +// +// Automated Testing Framework (atf) +// +// 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. +// + +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 "tests.hpp" + +#include "detail/application.hpp" +#include "detail/env.hpp" +#include "detail/exceptions.hpp" +#include "detail/fs.hpp" +#include "detail/parser.hpp" +#include "detail/sanity.hpp" +#include "detail/text.hpp" + +namespace impl = atf::tests; +namespace detail = atf::tests::detail; +#define IMPL_NAME "atf::tests" + +// ------------------------------------------------------------------------ +// The "atf_tp_writer" class. +// ------------------------------------------------------------------------ + +detail::atf_tp_writer::atf_tp_writer(std::ostream& os) : + m_os(os), + m_is_first(true) +{ + atf::parser::headers_map hm; + atf::parser::attrs_map ct_attrs; + ct_attrs["version"] = "1"; + hm["Content-Type"] = atf::parser::header_entry("Content-Type", + "application/X-atf-tp", ct_attrs); + atf::parser::write_headers(hm, m_os); +} + +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. +// ------------------------------------------------------------------------ + +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 : atf::utils::noncopyable { + 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()); + try { + (*iter).second->body(); + } catch (const std::exception& e) { + (*iter).second->fail("Caught unhandled exception: " + std::string( + e.what())); + } catch (...) { + (*iter).second->fail("Caught unknown exception"); + } + } + + 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; + + utils::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()); +} + +// ------------------------------------------------------------------------ +// The "tp" class. +// ------------------------------------------------------------------------ + +class tp : public atf::application::app { +public: + typedef std::vector< impl::tc * > tc_vector; + +private: + static const char* m_description; + + bool m_lflag; + atf::fs::path m_resfile; + std::string m_srcdir_arg; + atf::fs::path m_srcdir; + + atf::tests::vars_map m_vars; + + std::string specific_args(void) const; + options_set specific_options(void) const; + void process_option(int, const char*); + + void (*m_add_tcs)(tc_vector&); + tc_vector m_tcs; + + void parse_vflag(const std::string&); + void handle_srcdir(void); + + tc_vector init_tcs(void); + + enum tc_part { + BODY, + CLEANUP, + }; + + void list_tcs(void); + impl::tc* find_tc(tc_vector, const std::string&); + static std::pair< std::string, tc_part > process_tcarg(const std::string&); + int run_tc(const std::string&); + +public: + tp(void (*)(tc_vector&)); + ~tp(void); + + int main(void); +}; + +const char* tp::m_description = + "This is an independent atf test program."; + +tp::tp(void (*add_tcs)(tc_vector&)) : + app(m_description, "atf-test-program(1)", "atf(7)", false), + m_lflag(false), + m_resfile("/dev/stdout"), + m_srcdir("."), + m_add_tcs(add_tcs) +{ +} + +tp::~tp(void) +{ + for (tc_vector::iterator iter = m_tcs.begin(); + iter != m_tcs.end(); iter++) { + impl::tc* tc = *iter; + + delete tc; + } +} + +std::string +tp::specific_args(void) + const +{ + return "test_case"; +} + +tp::options_set +tp::specific_options(void) + const +{ + using atf::application::option; + options_set opts; + opts.insert(option('l', "", "List test cases and their purpose")); + opts.insert(option('r', "resfile", "The file to which the test program " + "will write the results of the " + "executed test case")); + opts.insert(option('s', "srcdir", "Directory where the test's data " + "files are located")); + opts.insert(option('v', "var=value", "Sets the configuration variable " + "`var' to `value'")); + return opts; +} + +void +tp::process_option(int ch, const char* arg) +{ + switch (ch) { + case 'l': + m_lflag = true; + break; + + case 'r': + m_resfile = atf::fs::path(arg); + break; + + case 's': + m_srcdir_arg = arg; + break; + + case 'v': + parse_vflag(arg); + break; + + default: + UNREACHABLE; + } +} + +void +tp::parse_vflag(const std::string& str) +{ + 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] == '=') { + m_vars[ws[0]] = ""; + } else { + if (ws.size() != 2) + throw std::runtime_error("-v requires an argument of the form " + "var=value"); + + m_vars[ws[0]] = ws[1]; + } +} + +void +tp::handle_srcdir(void) +{ + if (m_srcdir_arg.empty()) { + m_srcdir = atf::fs::path(m_argv0).branch_path(); + if (m_srcdir.leaf_name() == ".libs") + m_srcdir = m_srcdir.branch_path(); + } else + m_srcdir = atf::fs::path(m_srcdir_arg); + + if (!atf::fs::exists(m_srcdir / m_prog_name)) + throw std::runtime_error("Cannot find the test program in the " + "source directory `" + m_srcdir.str() + "'"); + + if (!m_srcdir.is_absolute()) + m_srcdir = m_srcdir.to_absolute(); + + m_vars["srcdir"] = m_srcdir.str(); +} + +tp::tc_vector +tp::init_tcs(void) +{ + m_add_tcs(m_tcs); + for (tc_vector::iterator iter = m_tcs.begin(); + iter != m_tcs.end(); iter++) { + impl::tc* tc = *iter; + + tc->init(m_vars); + } + return m_tcs; +} + +// +// An auxiliary unary predicate that compares the given test case's +// identifier to the identifier stored in it. +// +class tc_equal_to_ident { + const std::string& m_ident; + +public: + tc_equal_to_ident(const std::string& i) : + m_ident(i) + { + } + + bool operator()(const impl::tc* tc) + { + return tc->get_md_var("ident") == m_ident; + } +}; + +void +tp::list_tcs(void) +{ + tc_vector tcs = init_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(); + } +} + +impl::tc* +tp::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 atf::application::usage_error("Unknown test case `%s'", + name.c_str()); +} + +std::pair< std::string, tp::tc_part > +tp::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 { + using atf::application::usage_error; + throw usage_error("Invalid test case part `%s'", partname.c_str()); + } + } +} + +int +tp::run_tc(const std::string& tcarg) +{ + const std::pair< std::string, tc_part > fields = process_tcarg(tcarg); + + impl::tc* tc = find_tc(init_tcs(), fields.first); + + if (!atf::env::has("__RUNNING_INSIDE_ATF_RUN") || atf::env::get( + "__RUNNING_INSIDE_ATF_RUN") != "internal-yes-value") + { + std::cerr << m_prog_name << ": WARNING: Running test cases without " + "atf-run(1) is unsupported\n"; + std::cerr << m_prog_name << ": WARNING: No isolation nor timeout " + "control is being applied; you may get unexpected failures; see " + "atf-test-case(4)\n"; + } + + try { + switch (fields.second) { + case BODY: + tc->run(m_resfile.str()); + break; + case CLEANUP: + tc->run_cleanup(); + break; + default: + UNREACHABLE; + } + return EXIT_SUCCESS; + } catch (const std::runtime_error& e) { + std::cerr << "ERROR: " << e.what() << "\n"; + return EXIT_FAILURE; + } +} + +int +tp::main(void) +{ + using atf::application::usage_error; + + int errcode; + + handle_srcdir(); + + if (m_lflag) { + if (m_argc > 0) + throw usage_error("Cannot provide test case names with -l"); + + list_tcs(); + errcode = EXIT_SUCCESS; + } else { + if (m_argc == 0) + throw usage_error("Must provide a test case name"); + else if (m_argc > 1) + throw usage_error("Cannot provide more than one test case name"); + INV(m_argc == 1); + + errcode = run_tc(m_argv[0]); + } + + return errcode; +} + +namespace atf { + namespace tests { + int run_tp(int, char* const*, void (*)(tp::tc_vector&)); + } +} + +int +impl::run_tp(int argc, char* const* argv, void (*add_tcs)(tp::tc_vector&)) +{ + return tp(add_tcs).run(argc, argv); +} diff --git a/contrib/atf/atf-c++/tests.hpp b/contrib/atf/atf-c++/tests.hpp new file mode 100644 index 0000000..af75229 --- /dev/null +++ b/contrib/atf/atf-c++/tests.hpp @@ -0,0 +1,127 @@ +// +// Automated Testing Framework (atf) +// +// 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> +} + +#include <atf-c++/utils.hpp> + +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 : utils::noncopyable { + 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/contrib/atf/atf-c++/tests_test.cpp b/contrib/atf/atf-c++/tests_test.cpp new file mode 100644 index 0000000..63ab2ef --- /dev/null +++ b/contrib/atf/atf-c++/tests_test.cpp @@ -0,0 +1,201 @@ +// +// Automated Testing Framework (atf) +// +// 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. +// + +extern "C" { +#include <sys/types.h> +#include <sys/stat.h> + +#include <fcntl.h> +#include <unistd.h> +} + +#include <fstream> +#include <sstream> + +#include "macros.hpp" + +#include "detail/parser.hpp" +#include "detail/test_helpers.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 +} + +// ------------------------------------------------------------------------ +// Tests cases for the header file. +// ------------------------------------------------------------------------ + +HEADER_TC(include, "atf-c++/tests.hpp"); + +// ------------------------------------------------------------------------ +// Main. +// ------------------------------------------------------------------------ + +ATF_INIT_TEST_CASES(tcs) +{ + // Add tests for the "atf_tp_writer" class. + ATF_ADD_TEST_CASE(tcs, atf_tp_writer); + + // Add the test cases for the header file. + ATF_ADD_TEST_CASE(tcs, include); +} diff --git a/contrib/atf/atf-c++/unused_test.cpp b/contrib/atf/atf-c++/unused_test.cpp new file mode 100644 index 0000000..2a18a45 --- /dev/null +++ b/contrib/atf/atf-c++/unused_test.cpp @@ -0,0 +1,52 @@ +// +// Automated Testing Framework (atf) +// +// 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++/macros.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/contrib/atf/atf-c++/utils.hpp b/contrib/atf/atf-c++/utils.hpp new file mode 100644 index 0000000..1858b7f --- /dev/null +++ b/contrib/atf/atf-c++/utils.hpp @@ -0,0 +1,200 @@ +// +// Automated Testing Framework (atf) +// +// 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_ + +#include <cstddef> + +namespace atf { +namespace utils { + +// ------------------------------------------------------------------------ +// 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()); +} + +// ------------------------------------------------------------------------ +// The "noncopyable" class. +// ------------------------------------------------------------------------ + +class noncopyable { + // The class cannot be empty; otherwise we get ABI-stability warnings + // during the build, which will break it due to strict checking. + int m_noncopyable_dummy; + + noncopyable(const noncopyable& nc); + noncopyable& operator=(const noncopyable& nc); + +protected: + // Explicitly needed to provide some non-private functions. Otherwise + // we also get some warnings during the build. + noncopyable(void) {} + ~noncopyable(void) {} +}; + +} // namespace utils +} // namespace atf + +#endif // !defined(_ATF_CXX_UTILS_HPP_) diff --git a/contrib/atf/atf-c++/utils_test.cpp b/contrib/atf/atf-c++/utils_test.cpp new file mode 100644 index 0000000..f75e0a7 --- /dev/null +++ b/contrib/atf/atf-c++/utils_test.cpp @@ -0,0 +1,310 @@ +// +// Automated Testing Framework (atf) +// +// 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 <iostream> + +#include "atf-c/defs.h" + +#include "macros.hpp" +#include "utils.hpp" + +#include "detail/test_helpers.hpp" + +// ------------------------------------------------------------------------ +// Tests for the "auto_array" class. +// ------------------------------------------------------------------------ + +class test_array { +public: + int m_value; + + static ssize_t m_nblocks; + + static + atf::utils::auto_array< test_array > + do_copy(atf::utils::auto_array< test_array >& ta) + { + return atf::utils::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::utils::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::utils::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::utils::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::utils::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::utils::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::utils::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::utils::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::utils::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::utils::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); +} + +// ------------------------------------------------------------------------ +// Tests cases for the header file. +// ------------------------------------------------------------------------ + +HEADER_TC(include, "atf-c++/utils.hpp"); + +// ------------------------------------------------------------------------ +// 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); + + // Add the test cases for the header file. + ATF_ADD_TEST_CASE(tcs, include); +} |