diff options
Diffstat (limited to 'unit/atf-src/atf-c')
69 files changed, 17914 insertions, 0 deletions
diff --git a/unit/atf-src/atf-c/Atffile b/unit/atf-src/atf-c/Atffile new file mode 100644 index 0000000..d6fd6b9 --- /dev/null +++ b/unit/atf-src/atf-c/Atffile @@ -0,0 +1,15 @@ +Content-Type: application/X-atf-atffile; version="1" + +prop: test-suite = atf + +tp: detail + +tp: atf_c_test +tp: build_test +tp: check_test +tp: error_test +tp: macros_test +tp: pkg_config_test +tp: tc_test +tp: tp_test +tp: utils_test diff --git a/unit/atf-src/atf-c/Kyuafile b/unit/atf-src/atf-c/Kyuafile new file mode 100644 index 0000000..40fdb92 --- /dev/null +++ b/unit/atf-src/atf-c/Kyuafile @@ -0,0 +1,15 @@ +syntax("kyuafile", 1) + +test_suite("atf") + +atf_test_program{name="atf_c_test"} +atf_test_program{name="build_test"} +atf_test_program{name="check_test"} +atf_test_program{name="error_test"} +atf_test_program{name="macros_test"} +atf_test_program{name="pkg_config_test"} +atf_test_program{name="tc_test"} +atf_test_program{name="tp_test"} +atf_test_program{name="utils_test"} + +include("detail/Kyuafile") diff --git a/unit/atf-src/atf-c/Makefile.am.inc b/unit/atf-src/atf-c/Makefile.am.inc new file mode 100644 index 0000000..fb89bf0 --- /dev/null +++ b/unit/atf-src/atf-c/Makefile.am.inc @@ -0,0 +1,145 @@ +# Copyright (c) 2007 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND +# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +lib_LTLIBRARIES += libatf-c.la +libatf_c_la_SOURCES = atf-c/build.c \ + atf-c/build.h \ + atf-c/check.c \ + atf-c/check.h \ + atf-c/error.c \ + atf-c/error.h \ + atf-c/error_fwd.h \ + atf-c/macros.h \ + atf-c/tc.c \ + atf-c/tc.h \ + atf-c/tp.c \ + atf-c/tp.h \ + atf-c/utils.c \ + atf-c/utils.h +nodist_libatf_c_la_SOURCES = atf-c/defs.h +#libatf_c_la_CPPFLAGS = "-DATF_BUILD_CC=\"$(ATF_BUILD_CC)\"" \ +# "-DATF_BUILD_CFLAGS=\"$(ATF_BUILD_CFLAGS)\"" \ +# "-DATF_BUILD_CPP=\"$(ATF_BUILD_CPP)\"" \ +# "-DATF_BUILD_CPPFLAGS=\"$(ATF_BUILD_CPPFLAGS)\"" \ +# "-DATF_BUILD_CXX=\"$(ATF_BUILD_CXX)\"" \ +# "-DATF_BUILD_CXXFLAGS=\"$(ATF_BUILD_CXXFLAGS)\"" \ +# "-DATF_INCLUDEDIR=\"$(includedir)\"" \ +# "-DATF_LIBEXECDIR=\"$(libexecdir)\"" \ +# "-DATF_PKGDATADIR=\"$(pkgdatadir)\"" \ +# "-DATF_SHELL=\"$(ATF_SHELL)\"" \ +# "-DATF_WORKDIR=\"$(ATF_WORKDIR)\"" +libatf_c_la_CPPFLAGS = "-DATF_BUILD_CC=\"$(ATF_BUILD_CC)\"" \ + "-DATF_BUILD_CFLAGS=\"$(ATF_BUILD_CFLAGS)\"" \ + "-DATF_BUILD_CPP=\"$(ATF_BUILD_CPP)\"" \ + "-DATF_BUILD_CPPFLAGS=\"$(ATF_BUILD_CPPFLAGS)\"" \ + "-DATF_BUILD_CXX=\"$(ATF_BUILD_CXX)\"" \ + "-DATF_BUILD_CXXFLAGS=\"$(ATF_BUILD_CXXFLAGS)\"" +libatf_c_la_LDFLAGS = -version-info 1:0:0 + +# XXX For some reason, the nodist line above does not work as expected. +# Work this problem around. +DIST_HOOKS += kill-defs-h +kill-defs-h: + rm -f $(distdir)/atf-c/defs.h + +include_HEADERS += atf-c.h +atf_c_HEADERS = atf-c/build.h \ + atf-c/check.h \ + atf-c/error.h \ + atf-c/error_fwd.h \ + atf-c/macros.h \ + atf-c/tc.h \ + atf-c/tp.h \ + atf-c/utils.h +nodist_atf_c_HEADERS = atf-c/defs.h +atf_cdir = $(includedir)/atf-c + +dist_man_MANS += atf-c/atf-c.3 + +atf_aclocal_DATA += atf-c/atf-common.m4 atf-c/atf-c.m4 +EXTRA_DIST += atf-c/atf-common.m4 atf-c/atf-c.m4 + +atf_cpkgconfigdir = $(atf_pkgconfigdir) +atf_cpkgconfig_DATA = atf-c/atf-c.pc +CLEANFILES += atf-c/atf-c.pc +EXTRA_DIST += atf-c/atf-c.pc.in +atf-c/atf-c.pc: $(srcdir)/atf-c/atf-c.pc.in Makefile + $(AM_V_GEN)test -d atf-c || mkdir -p atf-c; \ + sed -e 's#__ATF_VERSION__#$(PACKAGE_VERSION)#g' \ + -e 's#__CC__#$(CC)#g' \ + -e 's#__INCLUDEDIR__#$(includedir)#g' \ + -e 's#__LIBDIR__#$(libdir)#g' \ + <$(srcdir)/atf-c/atf-c.pc.in >atf-c/atf-c.pc.tmp; \ + mv atf-c/atf-c.pc.tmp atf-c/atf-c.pc + +tests_atf_c_DATA = atf-c/Atffile \ + atf-c/Kyuafile \ + atf-c/macros_h_test.c \ + atf-c/unused_test.c +tests_atf_cdir = $(pkgtestsdir)/atf-c +EXTRA_DIST += $(tests_atf_c_DATA) + +tests_atf_c_PROGRAMS = atf-c/atf_c_test +atf_c_atf_c_test_SOURCES = atf-c/atf_c_test.c +atf_c_atf_c_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_PROGRAMS += atf-c/build_test +atf_c_build_test_SOURCES = atf-c/build_test.c atf-c/h_build.h +atf_c_build_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_PROGRAMS += atf-c/check_test +atf_c_check_test_SOURCES = atf-c/check_test.c +atf_c_check_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_PROGRAMS += atf-c/error_test +atf_c_error_test_SOURCES = atf-c/error_test.c +atf_c_error_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_PROGRAMS += atf-c/macros_test +atf_c_macros_test_SOURCES = atf-c/macros_test.c +atf_c_macros_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_SCRIPTS = atf-c/pkg_config_test +CLEANFILES += atf-c/pkg_config_test +EXTRA_DIST += atf-c/pkg_config_test.sh +atf-c/pkg_config_test: $(srcdir)/atf-c/pkg_config_test.sh + $(AM_V_GEN)src="$(srcdir)/atf-c/pkg_config_test.sh"; \ + dst="atf-c/pkg_config_test"; $(BUILD_SH_TP) + +tests_atf_c_PROGRAMS += atf-c/tc_test +atf_c_tc_test_SOURCES = atf-c/tc_test.c +atf_c_tc_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_PROGRAMS += atf-c/tp_test +atf_c_tp_test_SOURCES = atf-c/tp_test.c +atf_c_tp_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_PROGRAMS += atf-c/utils_test +atf_c_utils_test_SOURCES = atf-c/utils_test.c atf-c/h_build.h +atf_c_utils_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +include atf-c/detail/Makefile.am.inc + +# vim: syntax=make:noexpandtab:shiftwidth=8:softtabstop=8 diff --git a/unit/atf-src/atf-c/atf-c.3 b/unit/atf-src/atf-c/atf-c.3 new file mode 100644 index 0000000..edb7207 --- /dev/null +++ b/unit/atf-src/atf-c/atf-c.3 @@ -0,0 +1,793 @@ +.\" Copyright (c) 2008 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND +.\" CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +.\" GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +.\" IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.Dd October 13, 2014 +.Dt ATF-C 3 +.Os +.Sh NAME +.Nm atf-c , +.Nm ATF_CHECK , +.Nm ATF_CHECK_MSG , +.Nm ATF_CHECK_EQ , +.Nm ATF_CHECK_EQ_MSG , +.Nm ATF_CHECK_MATCH , +.Nm ATF_CHECK_MATCH_MSG , +.Nm ATF_CHECK_STREQ , +.Nm ATF_CHECK_STREQ_MSG , +.Nm ATF_CHECK_ERRNO , +.Nm ATF_REQUIRE , +.Nm ATF_REQUIRE_MSG , +.Nm ATF_REQUIRE_EQ , +.Nm ATF_REQUIRE_EQ_MSG , +.Nm ATF_REQUIRE_MATCH , +.Nm ATF_REQUIRE_MATCH_MSG , +.Nm ATF_REQUIRE_STREQ , +.Nm ATF_REQUIRE_STREQ_MSG , +.Nm ATF_REQUIRE_ERRNO , +.Nm ATF_TC , +.Nm ATF_TC_BODY , +.Nm ATF_TC_BODY_NAME , +.Nm ATF_TC_CLEANUP , +.Nm ATF_TC_CLEANUP_NAME , +.Nm ATF_TC_HEAD , +.Nm ATF_TC_HEAD_NAME , +.Nm ATF_TC_NAME , +.Nm ATF_TC_WITH_CLEANUP , +.Nm ATF_TC_WITHOUT_HEAD , +.Nm ATF_TP_ADD_TC , +.Nm ATF_TP_ADD_TCS , +.Nm atf_tc_get_config_var , +.Nm atf_tc_get_config_var_wd , +.Nm atf_tc_get_config_var_as_bool , +.Nm atf_tc_get_config_var_as_bool_wd , +.Nm atf_tc_get_config_var_as_long , +.Nm atf_tc_get_config_var_as_long_wd , +.Nm atf_no_error , +.Nm atf_tc_expect_death , +.Nm atf_tc_expect_exit , +.Nm atf_tc_expect_fail , +.Nm atf_tc_expect_pass , +.Nm atf_tc_expect_signal , +.Nm atf_tc_expect_timeout , +.Nm atf_tc_fail , +.Nm atf_tc_fail_nonfatal , +.Nm atf_tc_pass , +.Nm atf_tc_skip , +.Nm atf_utils_cat_file , +.Nm atf_utils_compare_file , +.Nm atf_utils_copy_file , +.Nm atf_utils_create_file , +.Nm atf_utils_file_exists , +.Nm atf_utils_fork , +.Nm atf_utils_free_charpp , +.Nm atf_utils_grep_file , +.Nm atf_utils_grep_string , +.Nm atf_utils_readline , +.Nm atf_utils_redirect , +.Nm atf_utils_wait +.Nd C API to write ATF-based test programs +.Sh SYNOPSIS +.In atf-c.h +.\" NO_CHECK_STYLE_BEGIN +.Fn ATF_CHECK "expression" +.Fn ATF_CHECK_MSG "expression" "fail_msg_fmt" ... +.Fn ATF_CHECK_EQ "expected_expression" "actual_expression" +.Fn ATF_CHECK_EQ_MSG "expected_expression" "actual_expression" "fail_msg_fmt" ... +.Fn ATF_CHECK_MATCH "regexp" "string" +.Fn ATF_CHECK_MATCH_MSG "regexp" "string" "fail_msg_fmt" ... +.Fn ATF_CHECK_STREQ "string_1" "string_2" +.Fn ATF_CHECK_STREQ_MSG "string_1" "string_2" "fail_msg_fmt" ... +.Fn ATF_CHECK_ERRNO "expected_errno" "bool_expression" +.Fn ATF_REQUIRE "expression" +.Fn ATF_REQUIRE_MSG "expression" "fail_msg_fmt" ... +.Fn ATF_REQUIRE_EQ "expected_expression" "actual_expression" +.Fn ATF_REQUIRE_EQ_MSG "expected_expression" "actual_expression" "fail_msg_fmt" ... +.Fn ATF_REQUIRE_MATCH "regexp" "string" +.Fn ATF_REQUIRE_MATCH_MSG "regexp" "string" "fail_msg_fmt" ... +.Fn ATF_REQUIRE_STREQ "expected_string" "actual_string" +.Fn ATF_REQUIRE_STREQ_MSG "expected_string" "actual_string" "fail_msg_fmt" ... +.Fn ATF_REQUIRE_ERRNO "expected_errno" "bool_expression" +.\" NO_CHECK_STYLE_END +.Fn ATF_TC "name" +.Fn ATF_TC_BODY "name" "tc" +.Fn ATF_TC_BODY_NAME "name" +.Fn ATF_TC_CLEANUP "name" "tc" +.Fn ATF_TC_CLEANUP_NAME "name" +.Fn ATF_TC_HEAD "name" "tc" +.Fn ATF_TC_HEAD_NAME "name" +.Fn ATF_TC_NAME "name" +.Fn ATF_TC_WITH_CLEANUP "name" +.Fn ATF_TC_WITHOUT_HEAD "name" +.Fn ATF_TP_ADD_TC "tp_name" "tc_name" +.Fn ATF_TP_ADD_TCS "tp_name" +.Fn atf_tc_get_config_var "tc" "varname" +.Fn atf_tc_get_config_var_wd "tc" "variable_name" "default_value" +.Fn atf_tc_get_config_var_as_bool "tc" "variable_name" +.Fn atf_tc_get_config_var_as_bool_wd "tc" "variable_name" "default_value" +.Fn atf_tc_get_config_var_as_long "tc" "variable_name" +.Fn atf_tc_get_config_var_as_long_wd "tc" "variable_name" "default_value" +.Fn atf_no_error +.Fn atf_tc_expect_death "reason" "..." +.Fn atf_tc_expect_exit "exitcode" "reason" "..." +.Fn atf_tc_expect_fail "reason" "..." +.Fn atf_tc_expect_pass +.Fn atf_tc_expect_signal "signo" "reason" "..." +.Fn atf_tc_expect_timeout "reason" "..." +.Fn atf_tc_fail "reason" +.Fn atf_tc_fail_nonfatal "reason" +.Fn atf_tc_pass +.Fn atf_tc_skip "reason" +.Ft void +.Fo atf_utils_cat_file +.Fa "const char *file" +.Fa "const char *prefix" +.Fc +.Ft bool +.Fo atf_utils_compare_file +.Fa "const char *file" +.Fa "const char *contents" +.Fc +.Ft void +.Fo atf_utils_copy_file +.Fa "const char *source" +.Fa "const char *destination" +.Fc +.Ft void +.Fo atf_utils_create_file +.Fa "const char *file" +.Fa "const char *contents" +.Fa "..." +.Fc +.Ft void +.Fo atf_utils_file_exists +.Fa "const char *file" +.Fc +.Ft pid_t +.Fo atf_utils_fork +.Fa "void" +.Fc +.Ft void +.Fo atf_utils_free_charpp +.Fa "char **argv" +.Fc +.Ft bool +.Fo atf_utils_grep_file +.Fa "const char *regexp" +.Fa "const char *file" +.Fa "..." +.Fc +.Ft bool +.Fo atf_utils_grep_string +.Fa "const char *regexp" +.Fa "const char *str" +.Fa "..." +.Fc +.Ft char * +.Fo atf_utils_readline +.Fa "int fd" +.Fc +.Ft void +.Fo atf_utils_redirect +.Fa "const int fd" +.Fa "const char *file" +.Fc +.Ft void +.Fo atf_utils_wait +.Fa "const pid_t pid" +.Fa "const int expected_exit_status" +.Fa "const char *expected_stdout" +.Fa "const char *expected_stderr" +.Fc +.Sh DESCRIPTION +ATF provides a C programming interface to implement test programs. +C-based test programs follow this template: +.Bd -literal -offset indent +.Ns ... C-specific includes go here ... + +#include <atf-c.h> + +ATF_TC(tc1); +ATF_TC_HEAD(tc1, tc) +{ + ... first test case's header ... +} +ATF_TC_BODY(tc1, tc) +{ + ... first test case's body ... +} + +ATF_TC_WITH_CLEANUP(tc2); +ATF_TC_HEAD(tc2, tc) +{ + ... second test case's header ... +} +ATF_TC_BODY(tc2, tc) +{ + ... second test case's body ... +} +ATF_TC_CLEANUP(tc2, tc) +{ + ... second test case's cleanup ... +} + +ATF_TC_WITHOUT_HEAD(tc3); +ATF_TC_BODY(tc3, tc) +{ + ... third test case's body ... +} + +.Ns ... additional test cases ... + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tcs, tc1); + ATF_TP_ADD_TC(tcs, tc2); + ATF_TP_ADD_TC(tcs, tc3); + ... add additional test cases ... + + return atf_no_error(); +} +.Ed +.Ss Definition of test cases +Test cases have an identifier and are composed of three different parts: +the header, the body and an optional cleanup routine, all of which are +described in +.Xr atf-test-case 4 . +To define test cases, one can use the +.Fn ATF_TC , +.Fn ATF_TC_WITH_CLEANUP +or the +.Fn ATF_TC_WITHOUT_HEAD +macros, which take a single parameter specifiying the test case's name. +.Fn ATF_TC , +requires to define a head and a body for the test case, +.Fn ATF_TC_WITH_CLEANUP +requires to define a head, a body and a cleanup for the test case and +.Fn ATF_TC_WITHOUT_HEAD +requires only a body for the test case. +It is important to note that these +.Em do not +set the test case up for execution when the program is run. +In order to do so, a later registration is needed with the +.Fn ATF_TP_ADD_TC +macro detailed in +.Sx Program initialization . +.Pp +Later on, one must define the three parts of the body by means of three +functions. +Their headers are given by the +.Fn ATF_TC_HEAD , +.Fn ATF_TC_BODY +and +.Fn ATF_TC_CLEANUP +macros, all of which take the test case name provided to the +.Fn ATF_TC +.Fn ATF_TC_WITH_CLEANUP , +or +.Fn ATF_TC_WITHOUT_HEAD +macros and the name of the variable that will hold a pointer to the +test case data. +Following each of these, a block of code is expected, surrounded by the +opening and closing brackets. +.Ss Program initialization +The library provides a way to easily define the test program's +.Fn main +function. +You should never define one on your own, but rely on the +library to do it for you. +This is done by using the +.Fn ATF_TP_ADD_TCS +macro, which is passed the name of the object that will hold the test +cases; i.e. the test program instance. +This name can be whatever you want as long as it is a valid variable +identifier. +.Pp +After the macro, you are supposed to provide the body of a function, which +should only use the +.Fn ATF_TP_ADD_TC +macro to register the test cases the test program will execute and return +a success error code. +The first parameter of this macro matches the name you provided in the +former call. +The success status can be returned using the +.Fn atf_no_error +function. +.Ss Header definitions +The test case's header can define the meta-data by using the +.Fn atf_tc_set_md_var +method, which takes three parameters: the first one points to the test +case data, the second one specifies the meta-data variable to be set +and the third one specifies its value. +Both of them are strings. +.Ss Configuration variables +The test case has read-only access to the current configuration variables +by means of the +.Ft bool +.Fn atf_tc_has_config_var , +.Ft const char * +.Fn atf_tc_get_config_var , +.Ft const char * +.Fn atf_tc_get_config_var_wd , +.Ft bool +.Fn atf_tc_get_config_var_as_bool , +.Ft bool +.Fn atf_tc_get_config_var_as_bool_wd , +.Ft long +.Fn atf_tc_get_config_var_as_long , +and the +.Ft long +.Fn atf_tc_get_config_var_as_long_wd +functions, which can be called in any of the three parts of a test case. +.Pp +The +.Sq _wd +variants take a default value for the variable which is returned if the +variable is not defined. +The other functions without the +.Sq _wd +suffix +.Em require +the variable to be defined. +.Ss Access to the source directory +It is possible to get the path to the test case's source directory from any +of its three components by querying the +.Sq srcdir +configuration variable. +.Ss Requiring programs +Aside from the +.Va require.progs +meta-data variable available in the header only, one can also check for +additional programs in the test case's body by using the +.Fn atf_tc_require_prog +function, which takes the base name or full path of a single binary. +Relative paths are forbidden. +If it is not found, the test case will be automatically skipped. +.Ss Test case finalization +The test case finalizes either when the body reaches its end, at which +point the test is assumed to have +.Em passed , +unless any non-fatal errors were raised using +.Fn atf_tc_fail_nonfatal , +or at any explicit call to +.Fn atf_tc_pass , +.Fn atf_tc_fail +or +.Fn atf_tc_skip . +These three functions terminate the execution of the test case immediately. +The cleanup routine will be processed afterwards in a completely automated +way, regardless of the test case's termination reason. +.Pp +.Fn atf_tc_pass +does not take any parameters. +.Fn atf_tc_fail , +.Fn atf_tc_fail_nonfatal +and +.Fn atf_tc_skip +take a format string and a variable list of parameters, which describe, in +a user-friendly manner, why the test case failed or was skipped, +respectively. +It is very important to provide a clear error message in both cases so that +the user can quickly know why the test did not pass. +.Ss Expectations +Everything explained in the previous section changes when the test case +expectations are redefined by the programmer. +.Pp +Each test case has an internal state called +.Sq expect +that describes what the test case expectations are at any point in time. +The value of this property can change during execution by any of: +.Bl -tag -width indent +.It Fn atf_tc_expect_death "reason" "..." +Expects the test case to exit prematurely regardless of the nature of the +exit. +.It Fn atf_tc_expect_exit "exitcode" "reason" "..." +Expects the test case to exit cleanly. +If +.Va exitcode +is not +.Sq -1 , +the runtime engine will validate that the exit code of the test case +matches the one provided in this call. +Otherwise, the exact value will be ignored. +.It Fn atf_tc_expect_fail "reason" "..." +Any failure (be it fatal or non-fatal) raised in this mode is recorded. +However, such failures do not report the test case as failed; instead, the +test case finalizes cleanly and is reported as +.Sq expected failure ; +this report includes the provided +.Fa reason +as part of it. +If no error is raised while running in this mode, then the test case is +reported as +.Sq failed . +.Pp +This mode is useful to reproduce actual known bugs in tests. +Whenever the developer fixes the bug later on, the test case will start +reporting a failure, signaling the developer that the test case must be +adjusted to the new conditions. +In this situation, it is useful, for example, to set +.Fa reason +as the bug number for tracking purposes. +.It Fn atf_tc_expect_pass +This is the normal mode of execution. +In this mode, any failure is reported as such to the user and the test case +is marked as +.Sq failed . +.It Fn atf_tc_expect_signal "signo" "reason" "..." +Expects the test case to terminate due to the reception of a signal. +If +.Va signo +is not +.Sq -1 , +the runtime engine will validate that the signal that terminated the test +case matches the one provided in this call. +Otherwise, the exact value will be ignored. +.It Fn atf_tc_expect_timeout "reason" "..." +Expects the test case to execute for longer than its timeout. +.El +.Ss Helper macros for common checks +The library provides several macros that are very handy in multiple +situations. +These basically check some condition after executing a given statement or +processing a given expression and, if the condition is not met, they +report the test case as failed. +.Pp +The +.Sq REQUIRE +variant of the macros immediately abort the test case as soon as an error +condition is detected by calling the +.Fn atf_tc_fail +function. +Use this variant whenever it makes no sense to continue the execution of a +test case when the checked condition is not met. +The +.Sq CHECK +variant, on the other hand, reports a failure as soon as it is encountered +using the +.Fn atf_tc_fail_nonfatal +function, but the execution of the test case continues as if nothing had +happened. +Use this variant whenever the checked condition is important as a result of +the test case, but there are other conditions that can be subsequently +checked on the same run without aborting. +.Pp +Additionally, the +.Sq MSG +variants take an extra set of parameters to explicitly specify the failure +message. +This failure message is formatted according to the +.Xr printf 3 +formatters. +.Pp +.Fn ATF_CHECK , +.Fn ATF_CHECK_MSG , +.Fn ATF_REQUIRE +and +.Fn ATF_REQUIRE_MSG +take an expression and fail if the expression evaluates to false. +.Pp +.Fn ATF_CHECK_EQ , +.Fn ATF_CHECK_EQ_MSG , +.Fn ATF_REQUIRE_EQ +and +.Fn ATF_REQUIRE_EQ_MSG +take two expressions and fail if the two evaluated values are not equal. +The common style is to put the expected value in the first parameter and the +actual value in the second parameter. +.Pp +.Fn ATF_CHECK_MATCH , +.Fn ATF_CHECK_MATCH_MSG , +.Fn ATF_REQUIRE_MATCH +and +.Fn ATF_REQUIRE_MATCH_MSG +take a regular expression and a string and fail if the regular expression does +not match the given string. +Note that the regular expression is not anchored, so it will match anywhere in +the string. +.Pp +.Fn ATF_CHECK_STREQ , +.Fn ATF_CHECK_STREQ_MSG , +.Fn ATF_REQUIRE_STREQ +and +.Fn ATF_REQUIRE_STREQ_MSG +take two strings and fail if the two are not equal character by character. +The common style is to put the expected string in the first parameter and the +actual string in the second parameter. +.Pp +.Fn ATF_CHECK_ERRNO +and +.Fn ATF_REQUIRE_ERRNO +take, first, the error code that the check is expecting to find in the +.Va errno +variable and, second, a boolean expression that, if evaluates to true, +means that a call failed and +.Va errno +has to be checked against the first value. +.Ss Utility functions +The following functions are provided as part of the +.Nm +API to simplify the creation of a variety of tests. +In particular, these are useful to write tests for command-line interfaces. +.Pp +.Ft void +.Fo atf_utils_cat_file +.Fa "const char *file" +.Fa "const char *prefix" +.Fc +.Bd -ragged -offset indent +Prints the contents of +.Fa file +to the standard output, prefixing every line with the string in +.Fa prefix . +.Ed +.Pp +.Ft bool +.Fo atf_utils_compare_file +.Fa "const char *file" +.Fa "const char *contents" +.Fc +.Bd -ragged -offset indent +Returns true if the given +.Fa file +matches exactly the expected inlined +.Fa contents . +.Ed +.Pp +.Ft void +.Fo atf_utils_copy_file +.Fa "const char *source" +.Fa "const char *destination" +.Fc +.Bd -ragged -offset indent +Copies the file +.Fa source +to +.Fa destination . +The permissions of the file are preserved during the code. +.Ed +.Pp +.Ft void +.Fo atf_utils_create_file +.Fa "const char *file" +.Fa "const char *contents" +.Fa "..." +.Fc +.Bd -ragged -offset indent +Creates +.Fa file +with the text given in +.Fa contents , +which is a formatting string that uses the rest of the variable arguments. +.Ed +.Pp +.Ft void +.Fo atf_utils_file_exists +.Fa "const char *file" +.Fc +.Bd -ragged -offset indent +Checks if +.Fa file +exists. +.Ed +.Pp +.Ft pid_t +.Fo atf_utils_fork +.Fa "void" +.Fc +.Bd -ragged -offset indent +Forks a process and redirects the standard output and standard error of the +child to files for later validation with +.Fn atf_utils_wait . +Fails the test case if the fork fails, so this does not return an error. +.Ed +.Pp +.Ft void +.Fo atf_utils_free_charpp +.Fa "char **argv" +.Fc +.Bd -ragged -offset indent +Frees a dynamically-allocated array of dynamically-allocated strings. +.Ed +.Pp +.Ft bool +.Fo atf_utils_grep_file +.Fa "const char *regexp" +.Fa "const char *file" +.Fa "..." +.Fc +.Bd -ragged -offset indent +Searches for the +.Fa regexp , +which is a formatting string representing the regular expression, +in the +.Fa file . +The variable arguments are used to construct the regular expression. +.Ed +.Pp +.Ft bool +.Fo atf_utils_grep_string +.Fa "const char *regexp" +.Fa "const char *str" +.Fa "..." +.Fc +.Bd -ragged -offset indent +Searches for the +.Fa regexp , +which is a formatting string representing the regular expression, +in the literal string +.Fa str . +The variable arguments are used to construct the regular expression. +.Ed +.Pp +.Ft char * +.Fo atf_utils_readline +.Fa "int fd" +.Fc +.Bd -ragged -offset indent +Reads a line from the file descriptor +.Fa fd . +The line, if any, is returned as a dynamically-allocated buffer that must be +released with +.Xr free 3 . +If there was nothing to read, returns +.Sq NULL . +.Ed +.Pp +.Ft void +.Fo atf_utils_redirect +.Fa "const int fd" +.Fa "const char *file" +.Fc +.Bd -ragged -offset indent +Redirects the given file descriptor +.Fa fd +to +.Fa file . +This function exits the process in case of an error and does not properly mark +the test case as failed. +As a result, it should only be used in subprocesses of the test case; specially +those spawned by +.Fn atf_utils_fork . +.Ed +.Pp +.Ft void +.Fo atf_utils_wait +.Fa "const pid_t pid" +.Fa "const int expected_exit_status" +.Fa "const char *expected_stdout" +.Fa "const char *expected_stderr" +.Fc +.Bd -ragged -offset indent +Waits and validates the result of a subprocess spawned with +.Fn atf_utils_wait . +The validation involves checking that the subprocess exited cleanly and returned +the code specified in +.Fa expected_exit_status +and that its standard output and standard error match the strings given in +.Fa expected_stdout +and +.Fa expected_stderr . +.Pp +If any of the +.Fa expected_stdout +or +.Fa expected_stderr +strings are prefixed with +.Sq save: , +then they specify the name of the file into which to store the stdout or stderr +of the subprocess, and no comparison is performed. +.Ed +.Sh ENVIRONMENT +The following variables are recognized by +.Nm +but should not be overridden other than for testing purposes: +.Pp +.Bl -tag -width ATFXBUILDXCXXFLAGSXX -compact +.It Va ATF_BUILD_CC +Path to the C compiler. +.It Va ATF_BUILD_CFLAGS +C compiler flags. +.It Va ATF_BUILD_CPP +Path to the C/C++ preprocessor. +.It Va ATF_BUILD_CPPFLAGS +C/C++ preprocessor flags. +.It Va ATF_BUILD_CXX +Path to the C++ compiler. +.It Va ATF_BUILD_CXXFLAGS +C++ compiler flags. +.El +.Sh EXAMPLES +The following shows a complete test program with a single test case that +validates the addition operator: +.Bd -literal -offset indent +#include <atf-c.h> + +ATF_TC(addition); +ATF_TC_HEAD(addition, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Sample tests for the addition operator"); +} +ATF_TC_BODY(addition, tc) +{ + ATF_CHECK_EQ(0, 0 + 0); + ATF_CHECK_EQ(1, 0 + 1); + ATF_CHECK_EQ(1, 1 + 0); + + ATF_CHECK_EQ(2, 1 + 1); + + ATF_CHECK_EQ(300, 100 + 200); +} + +ATF_TC(string_formatting); +ATF_TC_HEAD(string_formatting, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Sample tests for the snprintf"); +} +ATF_TC_BODY(string_formatting, tc) +{ + char buf[1024]; + snprintf(buf, sizeof(buf), "a %s", "string"); + ATF_CHECK_STREQ_MSG("a string", buf, "%s is not working"); +} + +ATF_TC(open_failure); +ATF_TC_HEAD(open_failure, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Sample tests for the open function"); +} +ATF_TC_BODY(open_failure, tc) +{ + ATF_CHECK_ERRNO(ENOENT, open("non-existent", O_RDONLY) == -1); +} + +ATF_TC(known_bug); +ATF_TC_HEAD(known_bug, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Reproduces a known bug"); +} +ATF_TC_BODY(known_bug, tc) +{ + atf_tc_expect_fail("See bug number foo/bar"); + ATF_CHECK_EQ(3, 1 + 1); + atf_tc_expect_pass(); + ATF_CHECK_EQ(3, 1 + 2); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, addition); + ATF_TP_ADD_TC(tp, string_formatting); + ATF_TP_ADD_TC(tp, open_failure); + ATF_TP_ADD_TC(tp, known_bug); + + return atf_no_error(); +} +.Ed +.Sh SEE ALSO +.Xr atf-test-program 1 , +.Xr atf-test-case 4 diff --git a/unit/atf-src/atf-c/atf-c.m4 b/unit/atf-src/atf-c/atf-c.m4 new file mode 100644 index 0000000..94288f7 --- /dev/null +++ b/unit/atf-src/atf-c/atf-c.m4 @@ -0,0 +1,44 @@ +dnl Copyright 2011 Google Inc. +dnl All rights reserved. +dnl +dnl Redistribution and use in source and binary forms, with or without +dnl modification, are permitted provided that the following conditions are +dnl met: +dnl +dnl * Redistributions of source code must retain the above copyright +dnl notice, this list of conditions and the following disclaimer. +dnl * Redistributions in binary form must reproduce the above copyright +dnl notice, this list of conditions and the following disclaimer in the +dnl documentation and/or other materials provided with the distribution. +dnl * Neither the name of Google Inc. nor the names of its contributors +dnl may be used to endorse or promote products derived from this software +dnl without specific prior written permission. +dnl +dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +dnl ATF_CHECK_C([version-spec]) +dnl +dnl Checks if atf-c is present. If version-spec is provided, ensures that +dnl the installed version of atf-sh matches the required version. This +dnl argument must be something like '>= 0.14' and accepts any version +dnl specification supported by pkg-config. +dnl +dnl Defines and substitutes ATF_C_CFLAGS and ATF_C_LIBS with the compiler +dnl and linker flags need to build against atf-c. +AC_DEFUN([ATF_CHECK_C], [ + spec="atf-c[]m4_default_nblank([ $1], [])" + _ATF_CHECK_ARG_WITH( + [PKG_CHECK_MODULES([ATF_C], [${spec}], + [found=yes found_atf_c=yes], [found=no])], + [required ${spec} not found]) +]) diff --git a/unit/atf-src/atf-c/atf-c.pc.in b/unit/atf-src/atf-c/atf-c.pc.in new file mode 100644 index 0000000..6fd5274 --- /dev/null +++ b/unit/atf-src/atf-c/atf-c.pc.in @@ -0,0 +1,11 @@ +# ATF pkg-config file + +cc=__CC__ +includedir=__INCLUDEDIR__ +libdir=__LIBDIR__ + +Name: atf-c +Description: Automated Testing Framework (C binding) +Version: __ATF_VERSION__ +Cflags: -I${includedir} +Libs: -L${libdir} -latf-c diff --git a/unit/atf-src/atf-c/atf-common.m4 b/unit/atf-src/atf-c/atf-common.m4 new file mode 100644 index 0000000..f8fc7e9 --- /dev/null +++ b/unit/atf-src/atf-c/atf-common.m4 @@ -0,0 +1,88 @@ +dnl Copyright 2011 Google Inc. +dnl All rights reserved. +dnl +dnl Redistribution and use in source and binary forms, with or without +dnl modification, are permitted provided that the following conditions are +dnl met: +dnl +dnl * Redistributions of source code must retain the above copyright +dnl notice, this list of conditions and the following disclaimer. +dnl * Redistributions in binary form must reproduce the above copyright +dnl notice, this list of conditions and the following disclaimer in the +dnl documentation and/or other materials provided with the distribution. +dnl * Neither the name of Google Inc. nor the names of its contributors +dnl may be used to endorse or promote products derived from this software +dnl without specific prior written permission. +dnl +dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +dnl ATF_ARG_WITH +dnl +dnl Adds a --with-atf flag to the configure script that allows the user to +dnl enable or disable atf support. +dnl +dnl The ATF_CHECK_{C,CXX,SH} macros honor the flag defined herein if +dnl instantiated. If not instantiated, they will request the presence of +dnl the libraries unconditionally. +dnl +dnl Defines the WITH_ATF Automake conditional if ATF has been found by any +dnl of the ATF_CHECK_{C,CXX,SH} macros. +AC_DEFUN([ATF_ARG_WITH], [ + m4_define([atf_arg_with_called], [yes]) + + m4_divert_text([DEFAULTS], [with_atf=auto]) + AC_ARG_WITH([atf], + [AS_HELP_STRING([--with-atf=<yes|no|auto>], + [build atf-based test programs])], + [with_atf=${withval}], [with_atf=auto]) + + m4_divert_text([DEFAULTS], [ + found_atf_c=no + found_atf_cxx=no + found_atf_sh=no + ]) + AM_CONDITIONAL([WITH_ATF], [test x"${found_atf_c}" = x"yes" -o \ + x"${found_atf_cxx}" = x"yes" -o \ + x"${found_atf_sh}" = x"yes"]) +]) + +dnl _ATF_CHECK_ARG_WITH(check, error_message) +dnl +dnl Internal macro to execute a check conditional on the --with-atf flag +dnl and handle the result accordingly. +dnl +dnl 'check' specifies the piece of code to be run to detect the feature. +dnl This code must set the 'found' shell variable to yes or no depending +dnl on the raw result of the check. +AC_DEFUN([_ATF_CHECK_ARG_WITH], [ + m4_ifdef([atf_arg_with_called], [ + m4_fatal([ATF_ARG_WITH must be called after the ATF_CHECK_* checks]) + ]) + + m4_divert_text([DEFAULTS], [with_atf=yes]) + + if test x"${with_atf}" = x"no"; then + _found=no + else + $1 + if test x"${with_atf}" = x"auto"; then + _found="${found}" + else + if test x"${found}" = x"yes"; then + _found=yes + else + AC_MSG_ERROR([$2]) + fi + fi + fi +]) diff --git a/unit/atf-src/atf-c/atf_c_test.c b/unit/atf-src/atf-c/atf_c_test.c new file mode 100644 index 0000000..eda9596 --- /dev/null +++ b/unit/atf-src/atf-c/atf_c_test.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" + +/* --------------------------------------------------------------------- + * Tests cases for the header file. + * --------------------------------------------------------------------- */ + +HEADER_TC(include, "atf-c.h"); + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Add the test cases for the header file. */ + ATF_TP_ADD_TC(tp, include); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/build.c b/unit/atf-src/atf-c/build.c new file mode 100644 index 0000000..c119fc9 --- /dev/null +++ b/unit/atf-src/atf-c/build.c @@ -0,0 +1,282 @@ +/* Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/build.h" + +#include <stdlib.h> +#include <string.h> + +#include "atf-c/detail/env.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/detail/text.h" +#include "atf-c/error.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +atf_error_t +append_config_var(const char *var, const char *default_value, atf_list_t *argv) +{ + atf_error_t err; + atf_list_t words; + + err = atf_text_split(atf_env_get_with_default(var, default_value), + " ", &words); + if (atf_is_error(err)) + goto out; + + atf_list_append_list(argv, &words); + +out: + return err; +} + +static +atf_error_t +append_arg1(const char *arg, atf_list_t *argv) +{ + return atf_list_append(argv, strdup(arg), true); +} + +static +atf_error_t +append_arg2(const char *flag, const char *arg, atf_list_t *argv) +{ + atf_error_t err; + + err = append_arg1(flag, argv); + if (!atf_is_error(err)) + err = append_arg1(arg, argv); + + return err; +} + +static +atf_error_t +append_optargs(const char *const optargs[], atf_list_t *argv) +{ + atf_error_t err; + + err = atf_no_error(); + while (*optargs != NULL && !atf_is_error(err)) { + err = append_arg1(strdup(*optargs), argv); + optargs++; + } + + return err; +} + +static +atf_error_t +append_src_out(const char *src, const char *obj, atf_list_t *argv) +{ + atf_error_t err; + + err = append_arg2("-o", obj, argv); + if (atf_is_error(err)) + goto out; + + err = append_arg1("-c", argv); + if (atf_is_error(err)) + goto out; + + err = append_arg1(src, argv); + +out: + return err; +} + +static +atf_error_t +list_to_array(const atf_list_t *l, char ***ap) +{ + atf_error_t err; + char **a; + + a = (char **)malloc((atf_list_size(l) + 1) * sizeof(char *)); + if (a == NULL) + err = atf_no_memory_error(); + else { + char **aiter; + atf_list_citer_t liter; + + aiter = a; + atf_list_for_each_c(liter, l) { + *aiter = strdup((const char *)atf_list_citer_data(liter)); + aiter++; + } + *aiter = NULL; + + err = atf_no_error(); + } + *ap = a; /* Shut up warnings in the caller about uninitialized *ap. */ + + return err; +} + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +atf_error_t +atf_build_c_o(const char *sfile, + const char *ofile, + const char *const optargs[], + char ***argv) +{ + atf_error_t err; + atf_list_t argv_list; + + err = atf_list_init(&argv_list); + if (atf_is_error(err)) + goto out; + + err = append_config_var("ATF_BUILD_CC", ATF_BUILD_CC, &argv_list); + if (atf_is_error(err)) + goto out_list; + + err = append_config_var("ATF_BUILD_CPPFLAGS", ATF_BUILD_CPPFLAGS, + &argv_list); + if (atf_is_error(err)) + goto out_list; + + err = append_config_var("ATF_BUILD_CFLAGS", ATF_BUILD_CFLAGS, &argv_list); + if (atf_is_error(err)) + goto out_list; + + if (optargs != NULL) { + err = append_optargs(optargs, &argv_list); + if (atf_is_error(err)) + goto out_list; + } + + err = append_src_out(sfile, ofile, &argv_list); + if (atf_is_error(err)) + goto out_list; + + err = list_to_array(&argv_list, argv); + if (atf_is_error(err)) + goto out_list; + +out_list: + atf_list_fini(&argv_list); +out: + return err; +} + +atf_error_t +atf_build_cpp(const char *sfile, + const char *ofile, + const char *const optargs[], + char ***argv) +{ + atf_error_t err; + atf_list_t argv_list; + + err = atf_list_init(&argv_list); + if (atf_is_error(err)) + goto out; + + err = append_config_var("ATF_BUILD_CPP", ATF_BUILD_CPP, &argv_list); + if (atf_is_error(err)) + goto out_list; + + err = append_config_var("ATF_BUILD_CPPFLAGS", ATF_BUILD_CPPFLAGS, + &argv_list); + if (atf_is_error(err)) + goto out_list; + + if (optargs != NULL) { + err = append_optargs(optargs, &argv_list); + if (atf_is_error(err)) + goto out_list; + } + + err = append_arg2("-o", ofile, &argv_list); + if (atf_is_error(err)) + goto out_list; + + err = append_arg1(sfile, &argv_list); + if (atf_is_error(err)) + goto out_list; + + err = list_to_array(&argv_list, argv); + if (atf_is_error(err)) + goto out_list; + +out_list: + atf_list_fini(&argv_list); +out: + return err; +} + +atf_error_t +atf_build_cxx_o(const char *sfile, + const char *ofile, + const char *const optargs[], + char ***argv) +{ + atf_error_t err; + atf_list_t argv_list; + + err = atf_list_init(&argv_list); + if (atf_is_error(err)) + goto out; + + err = append_config_var("ATF_BUILD_CXX", ATF_BUILD_CXX, &argv_list); + if (atf_is_error(err)) + goto out_list; + + err = append_config_var("ATF_BUILD_CPPFLAGS", ATF_BUILD_CPPFLAGS, + &argv_list); + if (atf_is_error(err)) + goto out_list; + + err = append_config_var("ATF_BUILD_CXXFLAGS", ATF_BUILD_CXXFLAGS, + &argv_list); + if (atf_is_error(err)) + goto out_list; + + if (optargs != NULL) { + err = append_optargs(optargs, &argv_list); + if (atf_is_error(err)) + goto out_list; + } + + err = append_src_out(sfile, ofile, &argv_list); + if (atf_is_error(err)) + goto out_list; + + err = list_to_array(&argv_list, argv); + if (atf_is_error(err)) + goto out_list; + +out_list: + atf_list_fini(&argv_list); +out: + return err; +} diff --git a/unit/atf-src/atf-c/build.h b/unit/atf-src/atf-c/build.h new file mode 100644 index 0000000..a263f50 --- /dev/null +++ b/unit/atf-src/atf-c/build.h @@ -0,0 +1,38 @@ +/* Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_BUILD_H) +#define ATF_C_BUILD_H + +#include <atf-c/error_fwd.h> + +atf_error_t atf_build_c_o(const char *, const char *, const char *const [], + char ***); +atf_error_t atf_build_cpp(const char *, const char *, const char *const [], + char ***); +atf_error_t atf_build_cxx_o(const char *, const char *, const char *const [], + char ***); + +#endif /* !defined(ATF_C_BUILD_H) */ diff --git a/unit/atf-src/atf-c/build_test.c b/unit/atf-src/atf-c/build_test.c new file mode 100644 index 0000000..c24b573 --- /dev/null +++ b/unit/atf-src/atf-c/build_test.c @@ -0,0 +1,249 @@ +/* Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/build.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <atf-c.h> + +#include "atf-c/detail/env.h" +#include "atf-c/detail/test_helpers.h" +#include "atf-c/h_build.h" +#include "atf-c/utils.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +bool +equal_arrays(const char *const *exp_array, char **actual_array) +{ + bool equal; + + if (*exp_array == NULL && *actual_array == NULL) + equal = true; + else if (*exp_array == NULL || *actual_array == NULL) + equal = false; + else { + equal = true; + while (*actual_array != NULL) { + if (*exp_array == NULL || strcmp(*exp_array, *actual_array) != 0) { + equal = false; + break; + } + exp_array++; + actual_array++; + } + } + + return equal; +} + +static +void +check_equal_array(const char *const *exp_array, char **actual_array) +{ + { + const char *const *exp_ptr; + printf("Expected arguments:"); + for (exp_ptr = exp_array; *exp_ptr != NULL; exp_ptr++) + printf(" '%s'", *exp_ptr); + printf("\n"); + } + + { + char **actual_ptr; + printf("Returned arguments:"); + for (actual_ptr = actual_array; *actual_ptr != NULL; actual_ptr++) + printf(" '%s'", *actual_ptr); + printf("\n"); + } + + if (!equal_arrays(exp_array, actual_array)) + atf_tc_fail_nonfatal("The constructed argv differs from the " + "expected values"); +} + +static +void +verbose_set_env(const char *var, const char *val) +{ + printf("Setting %s to '%s'\n", var, val); + RE(atf_env_set(var, val)); +} + +/* --------------------------------------------------------------------- + * Internal test cases. + * --------------------------------------------------------------------- */ + +ATF_TC(equal_arrays); +ATF_TC_HEAD(equal_arrays, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the test case internal " + "equal_arrays function"); +} +ATF_TC_BODY(equal_arrays, tc) +{ + { + const char *const exp[] = { NULL }; + char *actual[] = { NULL }; + + ATF_CHECK(equal_arrays(exp, actual)); + } + + { + const char *const exp[] = { NULL }; + char *actual[2] = { strdup("foo"), NULL }; + + ATF_CHECK(!equal_arrays(exp, actual)); + free(actual[0]); + } + + { + const char *const exp[] = { "foo", NULL }; + char *actual[] = { NULL }; + + ATF_CHECK(!equal_arrays(exp, actual)); + } + + { + const char *const exp[] = { "foo", NULL }; + char *actual[2] = { strdup("foo"), NULL }; + + ATF_CHECK(equal_arrays(exp, actual)); + free(actual[0]); + } +} + +/* --------------------------------------------------------------------- + * Test cases for the free functions. + * --------------------------------------------------------------------- */ + +ATF_TC(c_o); +ATF_TC_HEAD(c_o, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_build_c_o function"); +} +ATF_TC_BODY(c_o, tc) +{ + struct c_o_test *test; + + for (test = c_o_tests; test->expargv[0] != NULL; test++) { + printf("> Test: %s\n", test->msg); + + verbose_set_env("ATF_BUILD_CC", test->cc); + verbose_set_env("ATF_BUILD_CFLAGS", test->cflags); + verbose_set_env("ATF_BUILD_CPPFLAGS", test->cppflags); + + { + char **argv; + if (test->hasoptargs) + RE(atf_build_c_o(test->sfile, test->ofile, test->optargs, + &argv)); + else + RE(atf_build_c_o(test->sfile, test->ofile, NULL, &argv)); + check_equal_array(test->expargv, argv); + atf_utils_free_charpp(argv); + } + } +} + +ATF_TC(cpp); +ATF_TC_HEAD(cpp, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_build_cpp function"); +} +ATF_TC_BODY(cpp, tc) +{ + struct cpp_test *test; + + for (test = cpp_tests; test->expargv[0] != NULL; test++) { + printf("> Test: %s\n", test->msg); + + verbose_set_env("ATF_BUILD_CPP", test->cpp); + verbose_set_env("ATF_BUILD_CPPFLAGS", test->cppflags); + + { + char **argv; + if (test->hasoptargs) + RE(atf_build_cpp(test->sfile, test->ofile, test->optargs, + &argv)); + else + RE(atf_build_cpp(test->sfile, test->ofile, NULL, &argv)); + check_equal_array(test->expargv, argv); + atf_utils_free_charpp(argv); + } + } +} + +ATF_TC(cxx_o); +ATF_TC_HEAD(cxx_o, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_build_cxx_o function"); +} +ATF_TC_BODY(cxx_o, tc) +{ + struct cxx_o_test *test; + + for (test = cxx_o_tests; test->expargv[0] != NULL; test++) { + printf("> Test: %s\n", test->msg); + + verbose_set_env("ATF_BUILD_CXX", test->cxx); + verbose_set_env("ATF_BUILD_CXXFLAGS", test->cxxflags); + verbose_set_env("ATF_BUILD_CPPFLAGS", test->cppflags); + + { + char **argv; + if (test->hasoptargs) + RE(atf_build_cxx_o(test->sfile, test->ofile, test->optargs, + &argv)); + else + RE(atf_build_cxx_o(test->sfile, test->ofile, NULL, &argv)); + check_equal_array(test->expargv, argv); + atf_utils_free_charpp(argv); + } + } +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Add the internal test cases. */ + ATF_TP_ADD_TC(tp, equal_arrays); + + /* Add the test cases for the free functions. */ + ATF_TP_ADD_TC(tp, c_o); + ATF_TP_ADD_TC(tp, cpp); + ATF_TP_ADD_TC(tp, cxx_o); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/check.c b/unit/atf-src/atf-c/check.c new file mode 100644 index 0000000..38afdf3 --- /dev/null +++ b/unit/atf-src/atf-c/check.c @@ -0,0 +1,484 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/check.h" + +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "atf-c/build.h" +#include "atf-c/defs.h" +#include "atf-c/detail/dynstr.h" +#include "atf-c/detail/env.h" +#include "atf-c/detail/fs.h" +#include "atf-c/detail/list.h" +#include "atf-c/detail/process.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/error.h" +#include "atf-c/utils.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +atf_error_t +create_tmpdir(atf_fs_path_t *dir) +{ + atf_error_t err; + + err = atf_fs_path_init_fmt(dir, "%s/check.XXXXXX", + atf_env_get_with_default("TMPDIR", "/tmp")); + if (atf_is_error(err)) + goto out; + + err = atf_fs_mkdtemp(dir); + if (atf_is_error(err)) { + atf_fs_path_fini(dir); + goto out; + } + + INV(!atf_is_error(err)); +out: + return err; +} + +static +void +cleanup_tmpdir(const atf_fs_path_t *dir, const atf_fs_path_t *outfile, + const atf_fs_path_t *errfile) +{ + { + atf_error_t err = atf_fs_unlink(outfile); + if (atf_is_error(err)) { + INV(atf_error_is(err, "libc") && + atf_libc_error_code(err) == ENOENT); + atf_error_free(err); + } else + INV(!atf_is_error(err)); + } + + { + atf_error_t err = atf_fs_unlink(errfile); + if (atf_is_error(err)) { + INV(atf_error_is(err, "libc") && + atf_libc_error_code(err) == ENOENT); + atf_error_free(err); + } else + INV(!atf_is_error(err)); + } + + { + atf_error_t err = atf_fs_rmdir(dir); + INV(!atf_is_error(err)); + } +} + +static +int +const_execvp(const char *file, const char *const *argv) +{ +#define UNCONST(a) ((void *)(unsigned long)(const void *)(a)) + return execvp(file, UNCONST(argv)); +#undef UNCONST +} + +static +atf_error_t +init_sb(const atf_fs_path_t *path, atf_process_stream_t *sb) +{ + atf_error_t err; + + if (path == NULL) + err = atf_process_stream_init_inherit(sb); + else + err = atf_process_stream_init_redirect_path(sb, path); + + return err; +} + +static +atf_error_t +init_sbs(const atf_fs_path_t *outfile, atf_process_stream_t *outsb, + const atf_fs_path_t *errfile, atf_process_stream_t *errsb) +{ + atf_error_t err; + + err = init_sb(outfile, outsb); + if (atf_is_error(err)) + goto out; + + err = init_sb(errfile, errsb); + if (atf_is_error(err)) { + atf_process_stream_fini(outsb); + goto out; + } + +out: + return err; +} + +struct exec_data { + const char *const *m_argv; +}; + +static void exec_child(void *) ATF_DEFS_ATTRIBUTE_NORETURN; + +static +void +exec_child(void *v) +{ + struct exec_data *ea = v; + + const_execvp(ea->m_argv[0], ea->m_argv); + fprintf(stderr, "execvp(%s) failed: %s\n", ea->m_argv[0], strerror(errno)); + exit(127); +} + +static +atf_error_t +fork_and_wait(const char *const *argv, const atf_fs_path_t *outfile, + const atf_fs_path_t *errfile, atf_process_status_t *status) +{ + atf_error_t err; + atf_process_child_t child; + atf_process_stream_t outsb, errsb; + struct exec_data ea = { argv }; + + err = init_sbs(outfile, &outsb, errfile, &errsb); + if (atf_is_error(err)) + goto out; + + err = atf_process_fork(&child, exec_child, &outsb, &errsb, &ea); + if (atf_is_error(err)) + goto out_sbs; + + err = atf_process_child_wait(&child, status); + +out_sbs: + atf_process_stream_fini(&errsb); + atf_process_stream_fini(&outsb); +out: + return err; +} + +static +void +update_success_from_status(const char *progname, + const atf_process_status_t *status, bool *success) +{ + bool s = atf_process_status_exited(status) && + atf_process_status_exitstatus(status) == EXIT_SUCCESS; + + if (atf_process_status_exited(status)) { + if (atf_process_status_exitstatus(status) == EXIT_SUCCESS) + INV(s); + else { + INV(!s); + fprintf(stderr, "%s failed with exit code %d\n", progname, + atf_process_status_exitstatus(status)); + } + } else if (atf_process_status_signaled(status)) { + INV(!s); + fprintf(stderr, "%s failed due to signal %d%s\n", progname, + atf_process_status_termsig(status), + atf_process_status_coredump(status) ? " (core dumped)" : ""); + } else { + INV(!s); + fprintf(stderr, "%s failed due to unknown reason\n", progname); + } + + *success = s; +} + +static +atf_error_t +array_to_list(const char *const *a, atf_list_t *l) +{ + atf_error_t err; + + err = atf_list_init(l); + if (atf_is_error(err)) + goto out; + + while (*a != NULL) { + char *item = strdup(*a); + if (item == NULL) { + err = atf_no_memory_error(); + goto out; + } + + err = atf_list_append(l, item, true); + if (atf_is_error(err)) + goto out; + + a++; + } + +out: + return err; +} + +static void +print_array(const char *const *array, const char *pfx) +{ + const char *const *ptr; + + printf("%s", pfx); + for (ptr = array; *ptr != NULL; ptr++) + printf(" %s", *ptr); + printf("\n"); +} + +static +atf_error_t +check_build_run(const char *const *argv, bool *success) +{ + atf_error_t err; + atf_process_status_t status; + + print_array(argv, ">"); + + err = fork_and_wait(argv, NULL, NULL, &status); + if (atf_is_error(err)) + goto out; + + update_success_from_status(argv[0], &status, success); + atf_process_status_fini(&status); + + INV(!atf_is_error(err)); +out: + return err; +} + +/* --------------------------------------------------------------------- + * The "atf_check_result" type. + * --------------------------------------------------------------------- */ + +struct atf_check_result_impl { + atf_list_t m_argv; + atf_fs_path_t m_dir; + atf_fs_path_t m_stdout; + atf_fs_path_t m_stderr; + atf_process_status_t m_status; +}; + +static +atf_error_t +atf_check_result_init(atf_check_result_t *r, const char *const *argv, + const atf_fs_path_t *dir) +{ + atf_error_t err; + + r->pimpl = malloc(sizeof(struct atf_check_result_impl)); + if (r->pimpl == NULL) + return atf_no_memory_error(); + + err = array_to_list(argv, &r->pimpl->m_argv); + if (atf_is_error(err)) + goto out; + + err = atf_fs_path_copy(&r->pimpl->m_dir, dir); + if (atf_is_error(err)) + goto err_argv; + + err = atf_fs_path_init_fmt(&r->pimpl->m_stdout, "%s/stdout", + atf_fs_path_cstring(dir)); + if (atf_is_error(err)) + goto err_dir; + + err = atf_fs_path_init_fmt(&r->pimpl->m_stderr, "%s/stderr", + atf_fs_path_cstring(dir)); + if (atf_is_error(err)) + goto err_stdout; + + INV(!atf_is_error(err)); + goto out; + +err_stdout: + atf_fs_path_fini(&r->pimpl->m_stdout); +err_dir: + atf_fs_path_fini(&r->pimpl->m_dir); +err_argv: + atf_list_fini(&r->pimpl->m_argv); +out: + return err; +} + +void +atf_check_result_fini(atf_check_result_t *r) +{ + atf_process_status_fini(&r->pimpl->m_status); + + cleanup_tmpdir(&r->pimpl->m_dir, &r->pimpl->m_stdout, + &r->pimpl->m_stderr); + atf_fs_path_fini(&r->pimpl->m_stdout); + atf_fs_path_fini(&r->pimpl->m_stderr); + atf_fs_path_fini(&r->pimpl->m_dir); + + atf_list_fini(&r->pimpl->m_argv); + + free(r->pimpl); +} + +const char * +atf_check_result_stdout(const atf_check_result_t *r) +{ + return atf_fs_path_cstring(&r->pimpl->m_stdout); +} + +const char * +atf_check_result_stderr(const atf_check_result_t *r) +{ + return atf_fs_path_cstring(&r->pimpl->m_stderr); +} + +bool +atf_check_result_exited(const atf_check_result_t *r) +{ + return atf_process_status_exited(&r->pimpl->m_status); +} + +int +atf_check_result_exitcode(const atf_check_result_t *r) +{ + return atf_process_status_exitstatus(&r->pimpl->m_status); +} + +bool +atf_check_result_signaled(const atf_check_result_t *r) +{ + return atf_process_status_signaled(&r->pimpl->m_status); +} + +int +atf_check_result_termsig(const atf_check_result_t *r) +{ + return atf_process_status_termsig(&r->pimpl->m_status); +} + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +/* XXX: This function shouldn't be in this module. It messes with stdout + * and stderr, and it provides a very high-end interface. This belongs, + * probably, somewhere related to test cases (such as in the tc module). */ +atf_error_t +atf_check_build_c_o(const char *sfile, + const char *ofile, + const char *const optargs[], + bool *success) +{ + atf_error_t err; + char **argv; + + err = atf_build_c_o(sfile, ofile, optargs, &argv); + if (atf_is_error(err)) + goto out; + + err = check_build_run((const char *const *)argv, success); + + atf_utils_free_charpp(argv); +out: + return err; +} + +atf_error_t +atf_check_build_cpp(const char *sfile, + const char *ofile, + const char *const optargs[], + bool *success) +{ + atf_error_t err; + char **argv; + + err = atf_build_cpp(sfile, ofile, optargs, &argv); + if (atf_is_error(err)) + goto out; + + err = check_build_run((const char *const *)argv, success); + + atf_utils_free_charpp(argv); +out: + return err; +} + +atf_error_t +atf_check_build_cxx_o(const char *sfile, + const char *ofile, + const char *const optargs[], + bool *success) +{ + atf_error_t err; + char **argv; + + err = atf_build_cxx_o(sfile, ofile, optargs, &argv); + if (atf_is_error(err)) + goto out; + + err = check_build_run((const char *const *)argv, success); + + atf_utils_free_charpp(argv); +out: + return err; +} + +atf_error_t +atf_check_exec_array(const char *const *argv, atf_check_result_t *r) +{ + atf_error_t err; + atf_fs_path_t dir; + + err = create_tmpdir(&dir); + if (atf_is_error(err)) + goto out; + + err = atf_check_result_init(r, argv, &dir); + if (atf_is_error(err)) { + atf_error_t err2 = atf_fs_rmdir(&dir); + INV(!atf_is_error(err2)); + goto out; + } + + err = fork_and_wait(argv, &r->pimpl->m_stdout, &r->pimpl->m_stderr, + &r->pimpl->m_status); + if (atf_is_error(err)) { + atf_check_result_fini(r); + goto out; + } + + INV(!atf_is_error(err)); + + atf_fs_path_fini(&dir); +out: + return err; +} diff --git a/unit/atf-src/atf-c/check.h b/unit/atf-src/atf-c/check.h new file mode 100644 index 0000000..1eb2267 --- /dev/null +++ b/unit/atf-src/atf-c/check.h @@ -0,0 +1,69 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_CHECK_H) +#define ATF_C_CHECK_H + +#include <stdbool.h> + +#include <atf-c/error_fwd.h> + +/* --------------------------------------------------------------------- + * The "atf_check_result" type. + * --------------------------------------------------------------------- */ + +struct atf_check_result_impl; +struct atf_check_result { + struct atf_check_result_impl *pimpl; +}; +typedef struct atf_check_result atf_check_result_t; + +/* Construtors and destructors */ +void atf_check_result_fini(atf_check_result_t *); + +/* Getters */ +const char *atf_check_result_stdout(const atf_check_result_t *); +const char *atf_check_result_stderr(const atf_check_result_t *); +bool atf_check_result_exited(const atf_check_result_t *); +int atf_check_result_exitcode(const atf_check_result_t *); +bool atf_check_result_signaled(const atf_check_result_t *); +int atf_check_result_termsig(const atf_check_result_t *); + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +atf_error_t atf_check_build_c_o(const char *, const char *, + const char *const [], + bool *); +atf_error_t atf_check_build_cpp(const char *, const char *, + const char *const [], + bool *); +atf_error_t atf_check_build_cxx_o(const char *, const char *, + const char *const [], + bool *); +atf_error_t atf_check_exec_array(const char *const *, atf_check_result_t *); + +#endif /* !defined(ATF_C_CHECK_H) */ diff --git a/unit/atf-src/atf-c/check_test.c b/unit/atf-src/atf-c/check_test.c new file mode 100644 index 0000000..adaca64 --- /dev/null +++ b/unit/atf-src/atf-c/check_test.c @@ -0,0 +1,521 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/check.h" + +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/detail/fs.h" +#include "atf-c/detail/map.h" +#include "atf-c/detail/process.h" +#include "atf-c/detail/test_helpers.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +void +do_exec(const atf_tc_t *tc, const char *helper_name, atf_check_result_t *r) +{ + atf_fs_path_t process_helpers; + const char *argv[3]; + + get_process_helpers_path(tc, false, &process_helpers); + + argv[0] = atf_fs_path_cstring(&process_helpers); + argv[1] = helper_name; + argv[2] = NULL; + printf("Executing %s %s\n", argv[0], argv[1]); + RE(atf_check_exec_array(argv, r)); + + atf_fs_path_fini(&process_helpers); +} + +static +void +do_exec_with_arg(const atf_tc_t *tc, const char *helper_name, const char *arg, + atf_check_result_t *r) +{ + atf_fs_path_t process_helpers; + const char *argv[4]; + + get_process_helpers_path(tc, false, &process_helpers); + + argv[0] = atf_fs_path_cstring(&process_helpers); + argv[1] = helper_name; + argv[2] = arg; + argv[3] = NULL; + printf("Executing %s %s %s\n", argv[0], argv[1], argv[2]); + RE(atf_check_exec_array(argv, r)); + + atf_fs_path_fini(&process_helpers); +} + +static +void +check_line(int fd, const char *exp) +{ + char *line = atf_utils_readline(fd); + ATF_CHECK(line != NULL); + ATF_CHECK_STREQ_MSG(exp, line, "read: '%s', expected: '%s'", line, exp); + free(line); +} + +/* --------------------------------------------------------------------- + * Helper test cases for the free functions. + * --------------------------------------------------------------------- */ + +ATF_TC(h_build_c_o_ok); +ATF_TC_HEAD(h_build_c_o_ok, tc) +{ + atf_tc_set_md_var(tc, "descr", "Helper test case for build_c_o"); +} +ATF_TC_BODY(h_build_c_o_ok, tc) +{ + FILE *sfile; + bool success; + + ATF_REQUIRE((sfile = fopen("test.c", "w")) != NULL); + fprintf(sfile, "#include <stdio.h>\n"); + fclose(sfile); + + RE(atf_check_build_c_o("test.c", "test.o", NULL, &success)); + ATF_REQUIRE(success); +} + +ATF_TC(h_build_c_o_fail); +ATF_TC_HEAD(h_build_c_o_fail, tc) +{ + atf_tc_set_md_var(tc, "descr", "Helper test case for build_c_o"); +} +ATF_TC_BODY(h_build_c_o_fail, tc) +{ + FILE *sfile; + bool success; + + ATF_REQUIRE((sfile = fopen("test.c", "w")) != NULL); + fprintf(sfile, "void foo(void) { int a = UNDEFINED_SYMBOL; }\n"); + fclose(sfile); + + RE(atf_check_build_c_o("test.c", "test.o", NULL, &success)); + ATF_REQUIRE(!success); +} + +ATF_TC(h_build_cpp_ok); +ATF_TC_HEAD(h_build_cpp_ok, tc) +{ + atf_tc_set_md_var(tc, "descr", "Helper test case for build_cpp"); +} +ATF_TC_BODY(h_build_cpp_ok, tc) +{ + FILE *sfile; + bool success; + atf_fs_path_t test_p; + + RE(atf_fs_path_init_fmt(&test_p, "test.p")); + + ATF_REQUIRE((sfile = fopen("test.c", "w")) != NULL); + fprintf(sfile, "#define A foo\n"); + fprintf(sfile, "#define B bar\n"); + fprintf(sfile, "A B\n"); + fclose(sfile); + + RE(atf_check_build_cpp("test.c", atf_fs_path_cstring(&test_p), NULL, + &success)); + ATF_REQUIRE(success); + + atf_fs_path_fini(&test_p); +} + +ATF_TC(h_build_cpp_fail); +ATF_TC_HEAD(h_build_cpp_fail, tc) +{ + atf_tc_set_md_var(tc, "descr", "Helper test case for build_cpp"); +} +ATF_TC_BODY(h_build_cpp_fail, tc) +{ + FILE *sfile; + bool success; + + ATF_REQUIRE((sfile = fopen("test.c", "w")) != NULL); + fprintf(sfile, "#include \"./non-existent.h\"\n"); + fclose(sfile); + + RE(atf_check_build_cpp("test.c", "test.p", NULL, &success)); + ATF_REQUIRE(!success); +} + +ATF_TC(h_build_cxx_o_ok); +ATF_TC_HEAD(h_build_cxx_o_ok, tc) +{ + atf_tc_set_md_var(tc, "descr", "Helper test case for build_cxx_o"); +} +ATF_TC_BODY(h_build_cxx_o_ok, tc) +{ + FILE *sfile; + bool success; + + ATF_REQUIRE((sfile = fopen("test.cpp", "w")) != NULL); + fprintf(sfile, "#include <iostream>\n"); + fclose(sfile); + + RE(atf_check_build_cxx_o("test.cpp", "test.o", NULL, &success)); + ATF_REQUIRE(success); +} + +ATF_TC(h_build_cxx_o_fail); +ATF_TC_HEAD(h_build_cxx_o_fail, tc) +{ + atf_tc_set_md_var(tc, "descr", "Helper test case for build_cxx_o"); +} +ATF_TC_BODY(h_build_cxx_o_fail, tc) +{ + FILE *sfile; + bool success; + + ATF_REQUIRE((sfile = fopen("test.cpp", "w")) != NULL); + fprintf(sfile, "void foo(void) { int a = UNDEFINED_SYMBOL; }\n"); + fclose(sfile); + + RE(atf_check_build_cxx_o("test.cpp", "test.o", NULL, &success)); + ATF_REQUIRE(!success); +} + +/* --------------------------------------------------------------------- + * Test cases for the free functions. + * --------------------------------------------------------------------- */ + +static +void +init_and_run_h_tc(atf_tc_t *tc, const atf_tc_pack_t *tcpack, + const char *outname, const char *errname) +{ + const char *const config[] = { NULL }; + + RE(atf_tc_init_pack(tc, tcpack, config)); + run_h_tc(tc, outname, errname, "result"); + atf_tc_fini(tc); +} + +ATF_TC(build_c_o); +ATF_TC_HEAD(build_c_o, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_check_build_c_o " + "function"); +} +ATF_TC_BODY(build_c_o, tc) +{ + init_and_run_h_tc(&ATF_TC_NAME(h_build_c_o_ok), + &ATF_TC_PACK_NAME(h_build_c_o_ok), "stdout", "stderr"); + ATF_CHECK(atf_utils_grep_file("-o test.o", "stdout")); + ATF_CHECK(atf_utils_grep_file("-c test.c", "stdout")); + + init_and_run_h_tc(&ATF_TC_NAME(h_build_c_o_fail), + &ATF_TC_PACK_NAME(h_build_c_o_fail), "stdout", "stderr"); + ATF_CHECK(atf_utils_grep_file("-o test.o", "stdout")); + ATF_CHECK(atf_utils_grep_file("-c test.c", "stdout")); + ATF_CHECK(atf_utils_grep_file("test.c", "stderr")); + ATF_CHECK(atf_utils_grep_file("UNDEFINED_SYMBOL", "stderr")); +} + +ATF_TC(build_cpp); +ATF_TC_HEAD(build_cpp, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_check_build_cpp " + "function"); +} +ATF_TC_BODY(build_cpp, tc) +{ + init_and_run_h_tc(&ATF_TC_NAME(h_build_cpp_ok), + &ATF_TC_PACK_NAME(h_build_cpp_ok), "stdout", "stderr"); + ATF_CHECK(atf_utils_grep_file("-o.*test.p", "stdout")); + ATF_CHECK(atf_utils_grep_file("test.c", "stdout")); + ATF_CHECK(atf_utils_grep_file("foo bar", "test.p")); + + init_and_run_h_tc(&ATF_TC_NAME(h_build_cpp_fail), + &ATF_TC_PACK_NAME(h_build_cpp_fail), "stdout", "stderr"); + ATF_CHECK(atf_utils_grep_file("-o test.p", "stdout")); + ATF_CHECK(atf_utils_grep_file("test.c", "stdout")); + ATF_CHECK(atf_utils_grep_file("test.c", "stderr")); + ATF_CHECK(atf_utils_grep_file("non-existent.h", "stderr")); +} + +ATF_TC(build_cxx_o); +ATF_TC_HEAD(build_cxx_o, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_check_build_cxx_o " + "function"); +} +ATF_TC_BODY(build_cxx_o, tc) +{ + init_and_run_h_tc(&ATF_TC_NAME(h_build_cxx_o_ok), + &ATF_TC_PACK_NAME(h_build_cxx_o_ok), "stdout", "stderr"); + ATF_CHECK(atf_utils_grep_file("-o test.o", "stdout")); + ATF_CHECK(atf_utils_grep_file("-c test.cpp", "stdout")); + + init_and_run_h_tc(&ATF_TC_NAME(h_build_cxx_o_fail), + &ATF_TC_PACK_NAME(h_build_cxx_o_fail), "stdout", "stderr"); + ATF_CHECK(atf_utils_grep_file("-o test.o", "stdout")); + ATF_CHECK(atf_utils_grep_file("-c test.cpp", "stdout")); + ATF_CHECK(atf_utils_grep_file("test.cpp", "stderr")); + ATF_CHECK(atf_utils_grep_file("UNDEFINED_SYMBOL", "stderr")); +} + +ATF_TC(exec_array); +ATF_TC_HEAD(exec_array, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that atf_check_exec_array " + "works properly"); +} +ATF_TC_BODY(exec_array, tc) +{ + atf_fs_path_t process_helpers; + atf_check_result_t result; + + get_process_helpers_path(tc, false, &process_helpers); + + const char *argv[4]; + argv[0] = atf_fs_path_cstring(&process_helpers); + argv[1] = "echo"; + argv[2] = "test-message"; + argv[3] = NULL; + + RE(atf_check_exec_array(argv, &result)); + + ATF_CHECK(atf_check_result_exited(&result)); + ATF_CHECK(atf_check_result_exitcode(&result) == EXIT_SUCCESS); + + { + const char *path = atf_check_result_stdout(&result); + int fd = open(path, O_RDONLY); + ATF_CHECK(fd != -1); + check_line(fd, "test-message"); + close(fd); + } + + atf_check_result_fini(&result); + atf_fs_path_fini(&process_helpers); +} + +ATF_TC(exec_cleanup); +ATF_TC_HEAD(exec_cleanup, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that atf_check_exec_array " + "properly cleans up the temporary files it creates"); +} +ATF_TC_BODY(exec_cleanup, tc) +{ + atf_fs_path_t out, err; + atf_check_result_t result; + bool exists; + + do_exec(tc, "exit-success", &result); + RE(atf_fs_path_init_fmt(&out, "%s", atf_check_result_stdout(&result))); + RE(atf_fs_path_init_fmt(&err, "%s", atf_check_result_stderr(&result))); + + RE(atf_fs_exists(&out, &exists)); ATF_CHECK(exists); + RE(atf_fs_exists(&err, &exists)); ATF_CHECK(exists); + atf_check_result_fini(&result); + RE(atf_fs_exists(&out, &exists)); ATF_CHECK(!exists); + RE(atf_fs_exists(&err, &exists)); ATF_CHECK(!exists); + + atf_fs_path_fini(&err); + atf_fs_path_fini(&out); +} + +ATF_TC(exec_exitstatus); +ATF_TC_HEAD(exec_exitstatus, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that atf_check_exec_array " + "properly captures the exit status of the executed " + "command"); +} +ATF_TC_BODY(exec_exitstatus, tc) +{ + { + atf_check_result_t result; + do_exec(tc, "exit-success", &result); + ATF_CHECK(atf_check_result_exited(&result)); + ATF_CHECK(!atf_check_result_signaled(&result)); + ATF_CHECK(atf_check_result_exitcode(&result) == EXIT_SUCCESS); + atf_check_result_fini(&result); + } + + { + atf_check_result_t result; + do_exec(tc, "exit-failure", &result); + ATF_CHECK(atf_check_result_exited(&result)); + ATF_CHECK(!atf_check_result_signaled(&result)); + ATF_CHECK(atf_check_result_exitcode(&result) == EXIT_FAILURE); + atf_check_result_fini(&result); + } + + { + atf_check_result_t result; + do_exec(tc, "exit-signal", &result); + ATF_CHECK(!atf_check_result_exited(&result)); + ATF_CHECK(atf_check_result_signaled(&result)); + ATF_CHECK(atf_check_result_termsig(&result) == SIGKILL); + atf_check_result_fini(&result); + } +} + +ATF_TC(exec_stdout_stderr); +ATF_TC_HEAD(exec_stdout_stderr, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that atf_check_exec_array " + "properly captures the stdout and stderr streams " + "of the child process"); +} +ATF_TC_BODY(exec_stdout_stderr, tc) +{ + atf_check_result_t result1, result2; + const char *out1, *out2; + const char *err1, *err2; + + do_exec_with_arg(tc, "stdout-stderr", "result1", &result1); + ATF_CHECK(atf_check_result_exited(&result1)); + ATF_CHECK(atf_check_result_exitcode(&result1) == EXIT_SUCCESS); + + do_exec_with_arg(tc, "stdout-stderr", "result2", &result2); + ATF_CHECK(atf_check_result_exited(&result2)); + ATF_CHECK(atf_check_result_exitcode(&result2) == EXIT_SUCCESS); + + out1 = atf_check_result_stdout(&result1); + out2 = atf_check_result_stdout(&result2); + err1 = atf_check_result_stderr(&result1); + err2 = atf_check_result_stderr(&result2); + + ATF_CHECK(strstr(out1, "check.XXXXXX") == NULL); + ATF_CHECK(strstr(out2, "check.XXXXXX") == NULL); + ATF_CHECK(strstr(err1, "check.XXXXXX") == NULL); + ATF_CHECK(strstr(err2, "check.XXXXXX") == NULL); + + ATF_CHECK(strstr(out1, "/check") != NULL); + ATF_CHECK(strstr(out2, "/check") != NULL); + ATF_CHECK(strstr(err1, "/check") != NULL); + ATF_CHECK(strstr(err2, "/check") != NULL); + + ATF_CHECK(strstr(out1, "/stdout") != NULL); + ATF_CHECK(strstr(out2, "/stdout") != NULL); + ATF_CHECK(strstr(err1, "/stderr") != NULL); + ATF_CHECK(strstr(err2, "/stderr") != NULL); + + ATF_CHECK(strcmp(out1, out2) != 0); + ATF_CHECK(strcmp(err1, err2) != 0); + +#define CHECK_LINES(path, outname, resname) \ + do { \ + int fd = open(path, O_RDONLY); \ + ATF_CHECK(fd != -1); \ + check_line(fd, "Line 1 to " outname " for " resname); \ + check_line(fd, "Line 2 to " outname " for " resname); \ + close(fd); \ + } while (false) + + CHECK_LINES(out1, "stdout", "result1"); + CHECK_LINES(out2, "stdout", "result2"); + CHECK_LINES(err1, "stderr", "result1"); + CHECK_LINES(err2, "stderr", "result2"); + +#undef CHECK_LINES + + atf_check_result_fini(&result2); + atf_check_result_fini(&result1); +} + +ATF_TC(exec_umask); +ATF_TC_HEAD(exec_umask, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that atf_check_exec_array " + "correctly reports an error if the umask is too " + "restrictive to create temporary files"); +} +ATF_TC_BODY(exec_umask, tc) +{ + atf_check_result_t result; + atf_fs_path_t process_helpers; + const char *argv[3]; + + get_process_helpers_path(tc, false, &process_helpers); + argv[0] = atf_fs_path_cstring(&process_helpers); + argv[1] = "exit-success"; + argv[2] = NULL; + + umask(0222); + atf_error_t err = atf_check_exec_array(argv, &result); + ATF_CHECK(atf_is_error(err)); + ATF_CHECK(atf_error_is(err, "invalid_umask")); + atf_error_free(err); + + atf_fs_path_fini(&process_helpers); +} + +ATF_TC(exec_unknown); +ATF_TC_HEAD(exec_unknown, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that running a non-existing " + "binary is handled correctly"); +} +ATF_TC_BODY(exec_unknown, tc) +{ + const char *argv[2]; + argv[0] = "/foo/bar/non-existent"; + argv[1] = NULL; + + atf_check_result_t result; + RE(atf_check_exec_array(argv, &result)); + ATF_CHECK(atf_check_result_exited(&result)); + ATF_CHECK(atf_check_result_exitcode(&result) == 127); + atf_check_result_fini(&result); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Add the test cases for the free functions. */ + ATF_TP_ADD_TC(tp, build_c_o); + ATF_TP_ADD_TC(tp, build_cpp); + ATF_TP_ADD_TC(tp, build_cxx_o); + ATF_TP_ADD_TC(tp, exec_array); + ATF_TP_ADD_TC(tp, exec_cleanup); + ATF_TP_ADD_TC(tp, exec_exitstatus); + ATF_TP_ADD_TC(tp, exec_stdout_stderr); + ATF_TP_ADD_TC(tp, exec_umask); + ATF_TP_ADD_TC(tp, exec_unknown); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/defs.h.in b/unit/atf-src/atf-c/defs.h.in new file mode 100644 index 0000000..6059e7f --- /dev/null +++ b/unit/atf-src/atf-c/defs.h.in @@ -0,0 +1,33 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DEFS_H) +#define ATF_C_DEFS_H + +#define ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(a, b) @ATTRIBUTE_FORMAT_PRINTF@ +#define ATF_DEFS_ATTRIBUTE_NORETURN @ATTRIBUTE_NORETURN@ +#define ATF_DEFS_ATTRIBUTE_UNUSED @ATTRIBUTE_UNUSED@ + +#endif /* !defined(ATF_C_DEFS_H) */ diff --git a/unit/atf-src/atf-c/detail/Atffile b/unit/atf-src/atf-c/detail/Atffile new file mode 100644 index 0000000..5fd8593 --- /dev/null +++ b/unit/atf-src/atf-c/detail/Atffile @@ -0,0 +1,13 @@ +Content-Type: application/X-atf-atffile; version="1" + +prop: test-suite = atf + +tp: dynstr_test +tp: env_test +tp: fs_test +tp: list_test +tp: map_test +tp: process_test +tp: sanity_test +tp: text_test +tp: user_test diff --git a/unit/atf-src/atf-c/detail/Kyuafile b/unit/atf-src/atf-c/detail/Kyuafile new file mode 100644 index 0000000..bb741da --- /dev/null +++ b/unit/atf-src/atf-c/detail/Kyuafile @@ -0,0 +1,13 @@ +syntax("kyuafile", 1) + +test_suite("atf") + +atf_test_program{name="dynstr_test"} +atf_test_program{name="env_test"} +atf_test_program{name="fs_test"} +atf_test_program{name="list_test"} +atf_test_program{name="map_test"} +atf_test_program{name="process_test"} +atf_test_program{name="sanity_test"} +atf_test_program{name="text_test"} +atf_test_program{name="user_test"} diff --git a/unit/atf-src/atf-c/detail/Makefile.am.inc b/unit/atf-src/atf-c/detail/Makefile.am.inc new file mode 100644 index 0000000..ad45602 --- /dev/null +++ b/unit/atf-src/atf-c/detail/Makefile.am.inc @@ -0,0 +1,99 @@ +# Copyright (c) 2007 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND +# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +libatf_c_la_SOURCES += atf-c/detail/dynstr.c \ + atf-c/detail/dynstr.h \ + atf-c/detail/env.c \ + atf-c/detail/env.h \ + atf-c/detail/fs.c \ + atf-c/detail/fs.h \ + atf-c/detail/list.c \ + atf-c/detail/list.h \ + atf-c/detail/map.c \ + atf-c/detail/map.h \ + atf-c/detail/process.c \ + atf-c/detail/process.h \ + atf-c/detail/sanity.c \ + atf-c/detail/sanity.h \ + atf-c/detail/text.c \ + atf-c/detail/text.h \ + atf-c/detail/tp_main.c \ + atf-c/detail/user.c \ + atf-c/detail/user.h + +tests_atf_c_detail_DATA = atf-c/detail/Atffile \ + atf-c/detail/Kyuafile +tests_atf_c_detaildir = $(pkgtestsdir)/atf-c/detail +EXTRA_DIST += $(tests_atf_c_detail_DATA) + +noinst_LTLIBRARIES += atf-c/detail/libtest_helpers.la +atf_c_detail_libtest_helpers_la_SOURCES = atf-c/detail/test_helpers.c \ + atf-c/detail/test_helpers.h +atf_c_detail_libtest_helpers_la_CPPFLAGS = -I$(srcdir)/atf-c \ + -DATF_INCLUDEDIR=\"$(includedir)\" + +tests_atf_c_detail_PROGRAMS = atf-c/detail/dynstr_test +atf_c_detail_dynstr_test_SOURCES = atf-c/detail/dynstr_test.c +atf_c_detail_dynstr_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/env_test +atf_c_detail_env_test_SOURCES = atf-c/detail/env_test.c +atf_c_detail_env_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/fs_test +atf_c_detail_fs_test_SOURCES = atf-c/detail/fs_test.c +atf_c_detail_fs_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/list_test +atf_c_detail_list_test_SOURCES = atf-c/detail/list_test.c +atf_c_detail_list_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/map_test +atf_c_detail_map_test_SOURCES = atf-c/detail/map_test.c +atf_c_detail_map_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/process_helpers +atf_c_detail_process_helpers_SOURCES = atf-c/detail/process_helpers.c + +tests_atf_c_detail_PROGRAMS += atf-c/detail/process_test +atf_c_detail_process_test_SOURCES = atf-c/detail/process_test.c +atf_c_detail_process_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/sanity_test +atf_c_detail_sanity_test_SOURCES = atf-c/detail/sanity_test.c +atf_c_detail_sanity_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/text_test +atf_c_detail_text_test_SOURCES = atf-c/detail/text_test.c +atf_c_detail_text_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/user_test +atf_c_detail_user_test_SOURCES = atf-c/detail/user_test.c +atf_c_detail_user_test_LDADD = atf-c/detail/libtest_helpers.la libatf-c.la + +tests_atf_c_detail_PROGRAMS += atf-c/detail/version_helper +atf_c_detail_version_helper_SOURCES = atf-c/detail/version_helper.c + +# vim: syntax=make:noexpandtab:shiftwidth=8:softtabstop=8 diff --git a/unit/atf-src/atf-c/detail/dynstr.c b/unit/atf-src/atf-c/detail/dynstr.c new file mode 100644 index 0000000..424a1c9 --- /dev/null +++ b/unit/atf-src/atf-c/detail/dynstr.c @@ -0,0 +1,394 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/dynstr.h" + +#include <errno.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "atf-c/detail/sanity.h" +#include "atf-c/detail/text.h" +#include "atf-c/error.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +atf_error_t +resize(atf_dynstr_t *ad, size_t newsize) +{ + char *newdata; + atf_error_t err; + + PRE(newsize > ad->m_datasize); + + newdata = (char *)malloc(newsize); + if (newdata == NULL) { + err = atf_no_memory_error(); + } else { + strcpy(newdata, ad->m_data); + free(ad->m_data); + ad->m_data = newdata; + ad->m_datasize = newsize; + err = atf_no_error(); + } + + return err; +} + +static +atf_error_t +prepend_or_append(atf_dynstr_t *ad, const char *fmt, va_list ap, + bool prepend) +{ + char *aux; + atf_error_t err; + size_t newlen; + va_list ap2; + + va_copy(ap2, ap); + err = atf_text_format_ap(&aux, fmt, ap2); + va_end(ap2); + if (atf_is_error(err)) + goto out; + newlen = ad->m_length + strlen(aux); + + if (newlen + sizeof(char) > ad->m_datasize) { + err = resize(ad, newlen + sizeof(char)); + if (atf_is_error(err)) + goto out_free; + } + + if (prepend) { + memmove(ad->m_data + strlen(aux), ad->m_data, ad->m_length + 1); + memcpy(ad->m_data, aux, strlen(aux)); + } else + strcpy(ad->m_data + ad->m_length, aux); + ad->m_length = newlen; + err = atf_no_error(); + +out_free: + free(aux); +out: + return err; +} + +/* --------------------------------------------------------------------- + * The "atf_dynstr" type. + * --------------------------------------------------------------------- */ + +/* + * Constants. + */ + +const size_t atf_dynstr_npos = SIZE_MAX; + +/* + * Constructors and destructors. + */ + +atf_error_t +atf_dynstr_init(atf_dynstr_t *ad) +{ + atf_error_t err; + + ad->m_data = (char *)malloc(sizeof(char)); + if (ad->m_data == NULL) { + err = atf_no_memory_error(); + goto out; + } + + ad->m_data[0] = '\0'; + ad->m_datasize = 1; + ad->m_length = 0; + err = atf_no_error(); + +out: + return err; +} + +atf_error_t +atf_dynstr_init_ap(atf_dynstr_t *ad, const char *fmt, va_list ap) +{ + atf_error_t err; + + ad->m_datasize = strlen(fmt) + 1; + ad->m_length = 0; + + do { + va_list ap2; + int ret; + + ad->m_datasize *= 2; + ad->m_data = (char *)malloc(ad->m_datasize); + if (ad->m_data == NULL) { + err = atf_no_memory_error(); + goto out; + } + + va_copy(ap2, ap); + ret = vsnprintf(ad->m_data, ad->m_datasize, fmt, ap2); + va_end(ap2); + if (ret < 0) { + free(ad->m_data); + err = atf_libc_error(errno, "Cannot format string"); + goto out; + } + + INV(ret >= 0); + if ((size_t)ret >= ad->m_datasize) { + free(ad->m_data); + ad->m_data = NULL; + } + ad->m_length = ret; + } while (ad->m_length >= ad->m_datasize); + + err = atf_no_error(); +out: + POST(atf_is_error(err) || ad->m_data != NULL); + return err; +} + +atf_error_t +atf_dynstr_init_fmt(atf_dynstr_t *ad, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = atf_dynstr_init_ap(ad, fmt, ap); + va_end(ap); + + return err; +} + +atf_error_t +atf_dynstr_init_raw(atf_dynstr_t *ad, const void *mem, size_t memlen) +{ + atf_error_t err; + + if (memlen >= SIZE_MAX - 1) { + err = atf_no_memory_error(); + goto out; + } + + ad->m_data = (char *)malloc(memlen + 1); + if (ad->m_data == NULL) { + err = atf_no_memory_error(); + goto out; + } + + ad->m_datasize = memlen + 1; + memcpy(ad->m_data, mem, memlen); + ad->m_data[memlen] = '\0'; + ad->m_length = strlen(ad->m_data); + INV(ad->m_length <= memlen); + err = atf_no_error(); + +out: + return err; +} + +atf_error_t +atf_dynstr_init_rep(atf_dynstr_t *ad, size_t len, char ch) +{ + atf_error_t err; + + if (len == SIZE_MAX) { + err = atf_no_memory_error(); + goto out; + } + + ad->m_datasize = (len + 1) * sizeof(char); + ad->m_data = (char *)malloc(ad->m_datasize); + if (ad->m_data == NULL) { + err = atf_no_memory_error(); + goto out; + } + + memset(ad->m_data, ch, len); + ad->m_data[len] = '\0'; + ad->m_length = len; + err = atf_no_error(); + +out: + return err; +} + +atf_error_t +atf_dynstr_init_substr(atf_dynstr_t *ad, const atf_dynstr_t *src, + size_t beg, size_t end) +{ + if (beg > src->m_length) + beg = src->m_length; + + if (end == atf_dynstr_npos || end > src->m_length) + end = src->m_length; + + return atf_dynstr_init_raw(ad, src->m_data + beg, end - beg); +} + +atf_error_t +atf_dynstr_copy(atf_dynstr_t *dest, const atf_dynstr_t *src) +{ + atf_error_t err; + + dest->m_data = (char *)malloc(src->m_datasize); + if (dest->m_data == NULL) + err = atf_no_memory_error(); + else { + memcpy(dest->m_data, src->m_data, src->m_datasize); + dest->m_datasize = src->m_datasize; + dest->m_length = src->m_length; + err = atf_no_error(); + } + + return err; +} + +void +atf_dynstr_fini(atf_dynstr_t *ad) +{ + INV(ad->m_data != NULL); + free(ad->m_data); +} + +char * +atf_dynstr_fini_disown(atf_dynstr_t *ad) +{ + INV(ad->m_data != NULL); + return ad->m_data; +} + +/* + * Getters. + */ + +const char * +atf_dynstr_cstring(const atf_dynstr_t *ad) +{ + return ad->m_data; +} + +size_t +atf_dynstr_length(const atf_dynstr_t *ad) +{ + return ad->m_length; +} + +size_t +atf_dynstr_rfind_ch(const atf_dynstr_t *ad, char ch) +{ + size_t pos; + + for (pos = ad->m_length; pos > 0 && ad->m_data[pos - 1] != ch; pos--) + ; + + return pos == 0 ? atf_dynstr_npos : pos - 1; +} + +/* + * Modifiers. + */ + +atf_error_t +atf_dynstr_append_ap(atf_dynstr_t *ad, const char *fmt, va_list ap) +{ + atf_error_t err; + va_list ap2; + + va_copy(ap2, ap); + err = prepend_or_append(ad, fmt, ap2, false); + va_end(ap2); + + return err; +} + +atf_error_t +atf_dynstr_append_fmt(atf_dynstr_t *ad, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = prepend_or_append(ad, fmt, ap, false); + va_end(ap); + + return err; +} + +void +atf_dynstr_clear(atf_dynstr_t *ad) +{ + ad->m_data[0] = '\0'; + ad->m_length = 0; +} + +atf_error_t +atf_dynstr_prepend_ap(atf_dynstr_t *ad, const char *fmt, va_list ap) +{ + atf_error_t err; + va_list ap2; + + va_copy(ap2, ap); + err = prepend_or_append(ad, fmt, ap2, true); + va_end(ap2); + + return err; +} + +atf_error_t +atf_dynstr_prepend_fmt(atf_dynstr_t *ad, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = prepend_or_append(ad, fmt, ap, true); + va_end(ap); + + return err; +} + +/* + * Operators. + */ + +bool +atf_equal_dynstr_cstring(const atf_dynstr_t *ad, const char *str) +{ + return strcmp(ad->m_data, str) == 0; +} + +bool +atf_equal_dynstr_dynstr(const atf_dynstr_t *s1, const atf_dynstr_t *s2) +{ + return strcmp(s1->m_data, s2->m_data) == 0; +} diff --git a/unit/atf-src/atf-c/detail/dynstr.h b/unit/atf-src/atf-c/detail/dynstr.h new file mode 100644 index 0000000..60b8e42 --- /dev/null +++ b/unit/atf-src/atf-c/detail/dynstr.h @@ -0,0 +1,77 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_DYNSTR_H) +#define ATF_C_DETAIL_DYNSTR_H + +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> + +#include <atf-c/error_fwd.h> + +/* --------------------------------------------------------------------- + * The "atf_dynstr" type. + * --------------------------------------------------------------------- */ + +struct atf_dynstr { + char *m_data; + size_t m_datasize; + size_t m_length; +}; +typedef struct atf_dynstr atf_dynstr_t; + +/* Constants */ +extern const size_t atf_dynstr_npos; + +/* Constructors and destructors */ +atf_error_t atf_dynstr_init(atf_dynstr_t *); +atf_error_t atf_dynstr_init_ap(atf_dynstr_t *, const char *, va_list); +atf_error_t atf_dynstr_init_fmt(atf_dynstr_t *, const char *, ...); +atf_error_t atf_dynstr_init_raw(atf_dynstr_t *, const void *, size_t); +atf_error_t atf_dynstr_init_rep(atf_dynstr_t *, size_t, char); +atf_error_t atf_dynstr_init_substr(atf_dynstr_t *, const atf_dynstr_t *, + size_t, size_t); +atf_error_t atf_dynstr_copy(atf_dynstr_t *, const atf_dynstr_t *); +void atf_dynstr_fini(atf_dynstr_t *); +char *atf_dynstr_fini_disown(atf_dynstr_t *); + +/* Getters */ +const char *atf_dynstr_cstring(const atf_dynstr_t *); +size_t atf_dynstr_length(const atf_dynstr_t *); +size_t atf_dynstr_rfind_ch(const atf_dynstr_t *, char); + +/* Modifiers */ +atf_error_t atf_dynstr_append_ap(atf_dynstr_t *, const char *, va_list); +atf_error_t atf_dynstr_append_fmt(atf_dynstr_t *, const char *, ...); +void atf_dynstr_clear(atf_dynstr_t *); +atf_error_t atf_dynstr_prepend_ap(atf_dynstr_t *, const char *, va_list); +atf_error_t atf_dynstr_prepend_fmt(atf_dynstr_t *, const char *, ...); + +/* Operators */ +bool atf_equal_dynstr_cstring(const atf_dynstr_t *, const char *); +bool atf_equal_dynstr_dynstr(const atf_dynstr_t *, const atf_dynstr_t *); + +#endif /* !defined(ATF_C_DETAIL_DYNSTR_H) */ diff --git a/unit/atf-src/atf-c/detail/dynstr_test.c b/unit/atf-src/atf-c/detail/dynstr_test.c new file mode 100644 index 0000000..2512752 --- /dev/null +++ b/unit/atf-src/atf-c/detail/dynstr_test.c @@ -0,0 +1,634 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/dynstr.h" + +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" + +/* --------------------------------------------------------------------- + * Tests for the "atf_dynstr" type. + * --------------------------------------------------------------------- */ + +/* + * Constructors and destructors. + */ + +ATF_TC(init); +ATF_TC_HEAD(init, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the empty constructor"); +} +ATF_TC_BODY(init, tc) +{ + atf_dynstr_t str; + + RE(atf_dynstr_init(&str)); + ATF_REQUIRE_EQ(atf_dynstr_length(&str), 0); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0); + atf_dynstr_fini(&str); +} + +static +void +init_fmt(atf_dynstr_t *str, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + RE(atf_dynstr_init_ap(str, fmt, ap)); + va_end(ap); +} + +ATF_TC(init_ap); +ATF_TC_HEAD(init_ap, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the formatted constructor using " + "a va_list argument"); +} +ATF_TC_BODY(init_ap, tc) +{ + atf_dynstr_t str; + + init_fmt(&str, "String 1"); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 1") == 0); + atf_dynstr_fini(&str); + + init_fmt(&str, "String %d", 2); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 2") == 0); + atf_dynstr_fini(&str); + + init_fmt(&str, "%s %d", "String", 3); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 3") == 0); + atf_dynstr_fini(&str); + + init_fmt(&str, "%s%s%s%s%s%s%s", "This ", "should ", "be ", "a ", + "large ", "string ", "aaaabbbbccccdddd"); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), + "This should be a large string " + "aaaabbbbccccdddd") == 0); + atf_dynstr_fini(&str); +} + +ATF_TC(init_fmt); +ATF_TC_HEAD(init_fmt, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the formatted constructor using " + "a variable list of parameters"); +} +ATF_TC_BODY(init_fmt, tc) +{ + atf_dynstr_t str; + + RE(atf_dynstr_init_fmt(&str, "String 1")); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 1") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_fmt(&str, "String %d", 2)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 2") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_fmt(&str, "%s %d", "String", 3)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 3") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_fmt(&str, "%s%s%s%s%s%s%s", "This ", "should ", + "be ", "a ", "large ", "string ", + "aaaabbbbccccdddd")); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), + "This should be a large string " + "aaaabbbbccccdddd") == 0); + atf_dynstr_fini(&str); +} + +ATF_TC(init_raw); +ATF_TC_HEAD(init_raw, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of a string " + "using a raw memory pointer"); +} +ATF_TC_BODY(init_raw, tc) +{ + const char *src = "String 1, String 2"; + atf_dynstr_t str; + + RE(atf_dynstr_init_raw(&str, src, 0)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_raw(&str, src, 8)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 1") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_raw(&str, src + 10, 8)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String 2") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_raw(&str, "String\0Lost", 11)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "String") == 0); + atf_dynstr_fini(&str); + + { + atf_error_t err = atf_dynstr_init_raw(&str, "NULL", SIZE_MAX - 1); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "no_memory")); + atf_error_free(err); + } +} + +ATF_TC(init_rep); +ATF_TC_HEAD(init_rep, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of a string by " + "repeating characters"); +} +ATF_TC_BODY(init_rep, tc) +{ + const size_t maxlen = 8192; + char buf[maxlen + 1]; + size_t i; + + buf[0] = '\0'; + + for (i = 0; i < maxlen; i++) { + atf_dynstr_t str; + + RE(atf_dynstr_init_rep(&str, i, 'a')); + + if (strcmp(atf_dynstr_cstring(&str), buf) != 0) { + fprintf(stderr, "Failed at iteration %zd\n", i); + atf_tc_fail("Failed to construct dynstr by repeating %zd " + "times the '%c' character", i, 'a'); + } + + atf_dynstr_fini(&str); + + strcat(buf, "a"); + } + + { + atf_dynstr_t str; + atf_error_t err; + + err = atf_dynstr_init_rep(&str, SIZE_MAX, 'a'); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "no_memory")); + atf_error_free(err); + + err = atf_dynstr_init_rep(&str, SIZE_MAX - 1, 'a'); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "no_memory")); + atf_error_free(err); + } +} + +ATF_TC(init_substr); +ATF_TC_HEAD(init_substr, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of a string " + "using a substring of another one"); +} +ATF_TC_BODY(init_substr, tc) +{ + atf_dynstr_t src; + atf_dynstr_t str; + + RE(atf_dynstr_init_fmt(&src, "Str 1, Str 2")); + + RE(atf_dynstr_init_substr(&str, &src, 0, 0)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_substr(&str, &src, 0, atf_dynstr_npos)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 1, Str 2") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_substr(&str, &src, 0, 100)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 1, Str 2") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_substr(&str, &src, 0, 5)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 1") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_substr(&str, &src, 100, atf_dynstr_npos)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_substr(&str, &src, 7, atf_dynstr_npos)); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Str 2") == 0); + atf_dynstr_fini(&str); + + atf_dynstr_fini(&src); +} + +ATF_TC(copy); +ATF_TC_HEAD(copy, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_dynstr_copy constructor"); +} +ATF_TC_BODY(copy, tc) +{ + atf_dynstr_t str, str2; + + RE(atf_dynstr_init_fmt(&str, "Test string")); + RE(atf_dynstr_copy(&str2, &str)); + + ATF_REQUIRE(atf_equal_dynstr_dynstr(&str, &str2)); + + RE(atf_dynstr_append_fmt(&str2, " non-shared text")); + + ATF_REQUIRE(!atf_equal_dynstr_dynstr(&str, &str2)); + + atf_dynstr_fini(&str2); + atf_dynstr_fini(&str); +} + +ATF_TC(fini_disown); +ATF_TC_HEAD(fini_disown, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks grabbing ownership of the " + "internal plain C string"); +} +ATF_TC_BODY(fini_disown, tc) +{ + const char *cstr; + char *cstr2; + atf_dynstr_t str; + + RE(atf_dynstr_init_fmt(&str, "Test string 1")); + cstr = atf_dynstr_cstring(&str); + cstr2 = atf_dynstr_fini_disown(&str); + + ATF_REQUIRE_EQ(cstr, cstr2); + free(cstr2); +} + +/* + * Getters. + */ + +ATF_TC(cstring); +ATF_TC_HEAD(cstring, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the method to obtain a plain C " + "string"); +} +ATF_TC_BODY(cstring, tc) +{ + const char *cstr; + atf_dynstr_t str; + + RE(atf_dynstr_init_fmt(&str, "Test string 1")); + cstr = atf_dynstr_cstring(&str); + ATF_REQUIRE(cstr != NULL); + ATF_REQUIRE(strcmp(cstr, "Test string 1") == 0); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_fmt(&str, "Test string 2")); + cstr = atf_dynstr_cstring(&str); + ATF_REQUIRE(cstr != NULL); + ATF_REQUIRE(strcmp(cstr, "Test string 2") == 0); + atf_dynstr_fini(&str); +} + +ATF_TC(length); +ATF_TC_HEAD(length, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the method to obtain the length"); +} +ATF_TC_BODY(length, tc) +{ + size_t i; + + for (i = 0; i < 8192; i++) { + atf_dynstr_t str; + RE(atf_dynstr_init_rep(&str, i, 'a')); + ATF_REQUIRE_EQ(atf_dynstr_length(&str), i); + atf_dynstr_fini(&str); + } +} + +ATF_TC(rfind_ch); +ATF_TC_HEAD(rfind_ch, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the method to locate the first " + "occurrence of a character starting from the end"); +} +ATF_TC_BODY(rfind_ch, tc) +{ + atf_dynstr_t str; + + RE(atf_dynstr_init_fmt(&str, "Foo1/Bar2/,.Baz")); + + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, '\0'), atf_dynstr_npos); + + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, '0'), atf_dynstr_npos); + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'b'), atf_dynstr_npos); + + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'F'), 0); + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, '/'), 9); + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'a'), 13); + ATF_REQUIRE_EQ(atf_dynstr_rfind_ch(&str, 'z'), 14); + + atf_dynstr_fini(&str); +} + +/* + * Modifiers. + */ + +static +void +check_append(atf_error_t (*append)(atf_dynstr_t *, const char *, ...)) +{ + const size_t maxlen = 8192; + char buf[maxlen + 1]; + size_t i; + atf_dynstr_t str; + + printf("Appending with plain string\n"); + buf[0] = '\0'; + RE(atf_dynstr_init(&str)); + for (i = 0; i < maxlen; i++) { + if (strcmp(atf_dynstr_cstring(&str), buf) != 0) { + fprintf(stderr, "Failed at iteration %zd\n", i); + atf_tc_fail("Failed to append character at iteration %zd", i); + } + + RE(append(&str, "a")); + strcat(buf, "a"); + } + atf_dynstr_fini(&str); + + printf("Appending with formatted string\n"); + buf[0] = '\0'; + RE(atf_dynstr_init(&str)); + for (i = 0; i < maxlen; i++) { + if (strcmp(atf_dynstr_cstring(&str), buf) != 0) { + fprintf(stderr, "Failed at iteration %zd\n", i); + atf_tc_fail("Failed to append character at iteration %zd", i); + } + + RE(append(&str, "%s", "a")); + strcat(buf, "a"); + } + atf_dynstr_fini(&str); +} + +static +atf_error_t +append_ap_aux(atf_dynstr_t *str, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = atf_dynstr_append_ap(str, fmt, ap); + va_end(ap); + + return err; +} + +ATF_TC(append_ap); +ATF_TC_HEAD(append_ap, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that appending a string to " + "another one works"); +} +ATF_TC_BODY(append_ap, tc) +{ + check_append(append_ap_aux); +} + +ATF_TC(append_fmt); +ATF_TC_HEAD(append_fmt, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that appending a string to " + "another one works"); +} +ATF_TC_BODY(append_fmt, tc) +{ + check_append(atf_dynstr_append_fmt); +} + +ATF_TC(clear); +ATF_TC_HEAD(clear, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks clearing a string"); +} +ATF_TC_BODY(clear, tc) +{ + atf_dynstr_t str; + + printf("Clear an empty string\n"); + RE(atf_dynstr_init(&str)); + atf_dynstr_clear(&str); + ATF_REQUIRE_EQ(atf_dynstr_length(&str), 0); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0); + atf_dynstr_fini(&str); + + printf("Clear a non-empty string\n"); + RE(atf_dynstr_init_fmt(&str, "Not empty")); + ATF_REQUIRE_EQ(atf_dynstr_length(&str), strlen("Not empty")); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "Not empty") == 0); + atf_dynstr_clear(&str); + ATF_REQUIRE_EQ(atf_dynstr_length(&str), 0); + ATF_REQUIRE(strcmp(atf_dynstr_cstring(&str), "") == 0); + atf_dynstr_fini(&str); +} + +static +void +check_prepend(atf_error_t (*prepend)(atf_dynstr_t *, const char *, ...)) +{ + const size_t maxlen = 8192; + char buf[maxlen + 1]; + size_t i; + atf_dynstr_t str; + + printf("Prepending with plain string\n"); + buf[0] = '\0'; + RE(atf_dynstr_init(&str)); + for (i = 0; i < maxlen; i++) { + if (strcmp(atf_dynstr_cstring(&str), buf) != 0) { + fprintf(stderr, "Failed at iteration %zd\n", i); + atf_tc_fail("Failed to prepend character at iteration %zd", i); + } + + memmove(buf + 1, buf, i + 1); + if (i % 2 == 0) { + RE(prepend(&str, "%s", "a")); + buf[0] = 'a'; + } else { + RE(prepend(&str, "%s", "b")); + buf[0] = 'b'; + } + } + atf_dynstr_fini(&str); + + printf("Prepending with formatted string\n"); + buf[0] = '\0'; + RE(atf_dynstr_init(&str)); + for (i = 0; i < maxlen; i++) { + if (strcmp(atf_dynstr_cstring(&str), buf) != 0) { + fprintf(stderr, "Failed at iteration %zd\n", i); + atf_tc_fail("Failed to prepend character at iteration %zd", i); + } + + memmove(buf + 1, buf, i + 1); + if (i % 2 == 0) { + RE(prepend(&str, "%s", "a")); + buf[0] = 'a'; + } else { + RE(prepend(&str, "%s", "b")); + buf[0] = 'b'; + } + } + atf_dynstr_fini(&str); +} + +static +atf_error_t +prepend_ap_aux(atf_dynstr_t *str, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = atf_dynstr_prepend_ap(str, fmt, ap); + va_end(ap); + + return err; +} + +ATF_TC(prepend_ap); +ATF_TC_HEAD(prepend_ap, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that prepending a string to " + "another one works"); +} +ATF_TC_BODY(prepend_ap, tc) +{ + check_prepend(prepend_ap_aux); +} + +ATF_TC(prepend_fmt); +ATF_TC_HEAD(prepend_fmt, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that prepending a string to " + "another one works"); +} +ATF_TC_BODY(prepend_fmt, tc) +{ + check_prepend(atf_dynstr_prepend_fmt); +} + +/* + * Operators. + */ + +ATF_TC(equal_cstring); +ATF_TC_HEAD(equal_cstring, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_equal_dynstr_cstring " + "function"); +} +ATF_TC_BODY(equal_cstring, tc) +{ + atf_dynstr_t str; + + RE(atf_dynstr_init(&str)); + ATF_REQUIRE( atf_equal_dynstr_cstring(&str, "")); + ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "Test")); + atf_dynstr_fini(&str); + + RE(atf_dynstr_init_fmt(&str, "Test")); + ATF_REQUIRE( atf_equal_dynstr_cstring(&str, "Test")); + ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "")); + ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "Tes")); + ATF_REQUIRE(!atf_equal_dynstr_cstring(&str, "Test ")); + atf_dynstr_fini(&str); +} + +ATF_TC(equal_dynstr); +ATF_TC_HEAD(equal_dynstr, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_equal_dynstr_dynstr " + "function"); +} +ATF_TC_BODY(equal_dynstr, tc) +{ + atf_dynstr_t str, str2; + + RE(atf_dynstr_init(&str)); + RE(atf_dynstr_init_fmt(&str2, "Test")); + ATF_REQUIRE( atf_equal_dynstr_dynstr(&str, &str)); + ATF_REQUIRE(!atf_equal_dynstr_dynstr(&str, &str2)); + atf_dynstr_fini(&str2); + atf_dynstr_fini(&str); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Constructors and destructors. */ + ATF_TP_ADD_TC(tp, init); + ATF_TP_ADD_TC(tp, init_ap); + ATF_TP_ADD_TC(tp, init_fmt); + ATF_TP_ADD_TC(tp, init_raw); + ATF_TP_ADD_TC(tp, init_rep); + ATF_TP_ADD_TC(tp, init_substr); + ATF_TP_ADD_TC(tp, copy); + ATF_TP_ADD_TC(tp, fini_disown); + + /* Getters. */ + ATF_TP_ADD_TC(tp, cstring); + ATF_TP_ADD_TC(tp, length); + ATF_TP_ADD_TC(tp, rfind_ch); + + /* Modifiers. */ + ATF_TP_ADD_TC(tp, append_ap); + ATF_TP_ADD_TC(tp, append_fmt); + ATF_TP_ADD_TC(tp, clear); + ATF_TP_ADD_TC(tp, prepend_ap); + ATF_TP_ADD_TC(tp, prepend_fmt); + + /* Operators. */ + ATF_TP_ADD_TC(tp, equal_cstring); + ATF_TP_ADD_TC(tp, equal_dynstr); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/env.c b/unit/atf-src/atf-c/detail/env.c new file mode 100644 index 0000000..8ee3d69 --- /dev/null +++ b/unit/atf-src/atf-c/detail/env.c @@ -0,0 +1,114 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/env.h" + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <errno.h> +#include <stdlib.h> + +#include "atf-c/detail/sanity.h" +#include "atf-c/detail/text.h" +#include "atf-c/error.h" + +const char * +atf_env_get(const char *name) +{ + const char* val = getenv(name); + PRE(val != NULL); + return val; +} + +const char * +atf_env_get_with_default(const char *name, const char *default_value) +{ + const char* val = getenv(name); + if (val == NULL) + return default_value; + else + return val; +} + +bool +atf_env_has(const char *name) +{ + return getenv(name) != NULL; +} + +atf_error_t +atf_env_set(const char *name, const char *val) +{ + atf_error_t err; + +#if defined(HAVE_SETENV) + if (setenv(name, val, 1) == -1) + err = atf_libc_error(errno, "Cannot set environment variable " + "'%s' to '%s'", name, val); + else + err = atf_no_error(); +#elif defined(HAVE_PUTENV) + char *buf; + + err = atf_text_format(&buf, "%s=%s", name, val); + if (!atf_is_error(err)) { + if (putenv(buf) == -1) + err = atf_libc_error(errno, "Cannot set environment variable " + "'%s' to '%s'", name, val); + free(buf); + } +#else +# error "Don't know how to set an environment variable." +#endif + + return err; +} + +atf_error_t +atf_env_unset(const char *name) +{ + atf_error_t err; + +#if defined(HAVE_UNSETENV) + unsetenv(name); + err = atf_no_error(); +#elif defined(HAVE_PUTENV) + char *buf; + + err = atf_text_format(&buf, "%s=", name); + if (!atf_is_error(err)) { + if (putenv(buf) == -1) + err = atf_libc_error(errno, "Cannot unset environment variable" + " '%s'", name); + free(buf); + } +#else +# error "Don't know how to unset an environment variable." +#endif + + return err; +} diff --git a/unit/atf-src/atf-c/detail/env.h b/unit/atf-src/atf-c/detail/env.h new file mode 100644 index 0000000..5d29440 --- /dev/null +++ b/unit/atf-src/atf-c/detail/env.h @@ -0,0 +1,39 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_ENV_H) +#define ATF_C_DETAIL_ENV_H + +#include <stdbool.h> + +#include <atf-c/error_fwd.h> + +const char *atf_env_get(const char *); +const char *atf_env_get_with_default(const char *, const char *); +bool atf_env_has(const char *); +atf_error_t atf_env_set(const char *, const char *); +atf_error_t atf_env_unset(const char *); + +#endif /* !defined(ATF_C_DETAIL_ENV_H) */ diff --git a/unit/atf-src/atf-c/detail/env_test.c b/unit/atf-src/atf-c/detail/env_test.c new file mode 100644 index 0000000..3909c29 --- /dev/null +++ b/unit/atf-src/atf-c/detail/env_test.c @@ -0,0 +1,133 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/env.h" + +#include <stdlib.h> +#include <string.h> + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" +#include "atf-c/detail/text.h" + +/* --------------------------------------------------------------------- + * Test cases for the free functions. + * --------------------------------------------------------------------- */ + +ATF_TC(has); +ATF_TC_HEAD(has, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_env_has function"); +} +ATF_TC_BODY(has, tc) +{ + ATF_REQUIRE(atf_env_has("PATH")); + ATF_REQUIRE(!atf_env_has("_UNDEFINED_VARIABLE_")); +} + +ATF_TC(get); +ATF_TC_HEAD(get, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_env_get function"); +} +ATF_TC_BODY(get, tc) +{ + const char *val; + + ATF_REQUIRE(atf_env_has("PATH")); + + val = atf_env_get("PATH"); + ATF_REQUIRE(strlen(val) > 0); + ATF_REQUIRE(strchr(val, ':') != NULL); +} + +ATF_TC(get_with_default); +ATF_TC_HEAD(get_with_default, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_env_get_with_default " + "function"); +} +ATF_TC_BODY(get_with_default, tc) +{ + const char *val; + + ATF_REQUIRE(atf_env_has("PATH")); + + val = atf_env_get_with_default("PATH", "unknown"); + ATF_REQUIRE(strcmp(val, "unknown") != 0); + + val = atf_env_get_with_default("_UNKNOWN_VARIABLE_", "foo bar"); + ATF_REQUIRE(strcmp(val, "foo bar") == 0); +} + +ATF_TC(set); +ATF_TC_HEAD(set, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_env_set function"); +} +ATF_TC_BODY(set, tc) +{ + char *oldval; + + ATF_REQUIRE(atf_env_has("PATH")); + RE(atf_text_format(&oldval, "%s", atf_env_get("PATH"))); + RE(atf_env_set("PATH", "foo-bar")); + ATF_REQUIRE(strcmp(atf_env_get("PATH"), oldval) != 0); + ATF_REQUIRE(strcmp(atf_env_get("PATH"), "foo-bar") == 0); + free(oldval); + + ATF_REQUIRE(!atf_env_has("_UNDEFINED_VARIABLE_")); + RE(atf_env_set("_UNDEFINED_VARIABLE_", "foo2-bar2")); + ATF_REQUIRE(strcmp(atf_env_get("_UNDEFINED_VARIABLE_"), + "foo2-bar2") == 0); +} + +ATF_TC(unset); +ATF_TC_HEAD(unset, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_env_unset function"); +} +ATF_TC_BODY(unset, tc) +{ + ATF_REQUIRE(atf_env_has("PATH")); + RE(atf_env_unset("PATH")); + ATF_REQUIRE(!atf_env_has("PATH")); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, has); + ATF_TP_ADD_TC(tp, get); + ATF_TP_ADD_TC(tp, get_with_default); + ATF_TP_ADD_TC(tp, set); + ATF_TP_ADD_TC(tp, unset); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/fs.c b/unit/atf-src/atf-c/detail/fs.c new file mode 100644 index 0000000..5ff7648 --- /dev/null +++ b/unit/atf-src/atf-c/detail/fs.c @@ -0,0 +1,884 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/fs.h" + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <dirent.h> +#include <errno.h> +#include <libgen.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "atf-c/defs.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/detail/text.h" +#include "atf-c/detail/user.h" +#include "atf-c/error.h" + +/* --------------------------------------------------------------------- + * Prototypes for auxiliary functions. + * --------------------------------------------------------------------- */ + +static bool check_umask(const mode_t, const mode_t); +static atf_error_t copy_contents(const atf_fs_path_t *, char **); +static mode_t current_umask(void); +static atf_error_t do_mkdtemp(char *); +static atf_error_t normalize(atf_dynstr_t *, char *); +static atf_error_t normalize_ap(atf_dynstr_t *, const char *, va_list); +static void replace_contents(atf_fs_path_t *, const char *); +static const char *stat_type_to_string(const int); + +/* --------------------------------------------------------------------- + * The "invalid_umask" error type. + * --------------------------------------------------------------------- */ + +struct invalid_umask_error_data { + /* One of atf_fs_stat_*_type. */ + int m_type; + + /* The original path causing the error. */ + /* XXX: Ideally this would be an atf_fs_path_t, but if we create it + * from the error constructor, we cannot delete the path later on. + * Can't remember why atf_error_new does not take a hook for + * deletion. */ + char m_path[1024]; + + /* The umask that caused the error. */ + mode_t m_umask; +}; +typedef struct invalid_umask_error_data invalid_umask_error_data_t; + +static +void +invalid_umask_format(const atf_error_t err, char *buf, size_t buflen) +{ + const invalid_umask_error_data_t *data; + + PRE(atf_error_is(err, "invalid_umask")); + + data = atf_error_data(err); + snprintf(buf, buflen, "Could not create the temporary %s %s because " + "it will not have enough access rights due to the current " + "umask %05o", stat_type_to_string(data->m_type), + data->m_path, (unsigned int)data->m_umask); +} + +static +atf_error_t +invalid_umask_error(const atf_fs_path_t *path, const int type, + const mode_t failing_mask) +{ + atf_error_t err; + invalid_umask_error_data_t data; + + data.m_type = type; + + strncpy(data.m_path, atf_fs_path_cstring(path), sizeof(data.m_path)); + data.m_path[sizeof(data.m_path) - 1] = '\0'; + + data.m_umask = failing_mask; + + err = atf_error_new("invalid_umask", &data, sizeof(data), + invalid_umask_format); + + return err; +} + +/* --------------------------------------------------------------------- + * The "unknown_file_type" error type. + * --------------------------------------------------------------------- */ + +struct unknown_type_error_data { + const char *m_path; + int m_type; +}; +typedef struct unknown_type_error_data unknown_type_error_data_t; + +static +void +unknown_type_format(const atf_error_t err, char *buf, size_t buflen) +{ + const unknown_type_error_data_t *data; + + PRE(atf_error_is(err, "unknown_type")); + + data = atf_error_data(err); + snprintf(buf, buflen, "Unknown file type %d of %s", data->m_type, + data->m_path); +} + +static +atf_error_t +unknown_type_error(const char *path, int type) +{ + atf_error_t err; + unknown_type_error_data_t data; + + data.m_path = path; + data.m_type = type; + + err = atf_error_new("unknown_type", &data, sizeof(data), + unknown_type_format); + + return err; +} + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +bool +check_umask(const mode_t exp_mode, const mode_t min_mode) +{ + const mode_t actual_mode = (~current_umask() & exp_mode); + return (actual_mode & min_mode) == min_mode; +} + +static +atf_error_t +copy_contents(const atf_fs_path_t *p, char **buf) +{ + atf_error_t err; + char *str; + + str = (char *)malloc(atf_dynstr_length(&p->m_data) + 1); + if (str == NULL) + err = atf_no_memory_error(); + else { + strcpy(str, atf_dynstr_cstring(&p->m_data)); + *buf = str; + err = atf_no_error(); + } + + return err; +} + +static +mode_t +current_umask(void) +{ + const mode_t current = umask(0); + (void)umask(current); + return current; +} + +static +atf_error_t +do_mkdtemp(char *tmpl) +{ + atf_error_t err; + + PRE(strstr(tmpl, "XXXXXX") != NULL); + + if (mkdtemp(tmpl) == NULL) + err = atf_libc_error(errno, "Cannot create temporary directory " + "with template '%s'", tmpl); + else + err = atf_no_error(); + + return err; +} + +static +atf_error_t +do_mkstemp(char *tmpl, int *fdout) +{ + atf_error_t err; + + PRE(strstr(tmpl, "XXXXXX") != NULL); + + *fdout = mkstemp(tmpl); + if (*fdout == -1) + err = atf_libc_error(errno, "Cannot create temporary file " + "with template '%s'", tmpl); + + else + err = atf_no_error(); + + return err; +} + +static +atf_error_t +normalize(atf_dynstr_t *d, char *p) +{ + const char *ptr; + char *last; + atf_error_t err; + bool first; + + PRE(strlen(p) > 0); + PRE(atf_dynstr_length(d) == 0); + + if (p[0] == '/') + err = atf_dynstr_append_fmt(d, "/"); + else + err = atf_no_error(); + + first = true; + last = NULL; /* Silence GCC warning. */ + ptr = strtok_r(p, "/", &last); + while (!atf_is_error(err) && ptr != NULL) { + if (strlen(ptr) > 0) { + err = atf_dynstr_append_fmt(d, "%s%s", first ? "" : "/", ptr); + first = false; + } + + ptr = strtok_r(NULL, "/", &last); + } + + return err; +} + +static +atf_error_t +normalize_ap(atf_dynstr_t *d, const char *p, va_list ap) +{ + char *str; + atf_error_t err; + va_list ap2; + + err = atf_dynstr_init(d); + if (atf_is_error(err)) + goto out; + + va_copy(ap2, ap); + err = atf_text_format_ap(&str, p, ap2); + va_end(ap2); + if (atf_is_error(err)) + atf_dynstr_fini(d); + else { + err = normalize(d, str); + free(str); + } + +out: + return err; +} + +static +void +replace_contents(atf_fs_path_t *p, const char *buf) +{ + atf_error_t err; + + PRE(atf_dynstr_length(&p->m_data) == strlen(buf)); + + atf_dynstr_clear(&p->m_data); + err = atf_dynstr_append_fmt(&p->m_data, "%s", buf); + + INV(!atf_is_error(err)); +} + +static +const char * +stat_type_to_string(const int type) +{ + const char *str; + + if (type == atf_fs_stat_blk_type) + str = "block device"; + else if (type == atf_fs_stat_chr_type) + str = "character device"; + else if (type == atf_fs_stat_dir_type) + str = "directory"; + else if (type == atf_fs_stat_fifo_type) + str = "named pipe"; + else if (type == atf_fs_stat_lnk_type) + str = "symbolic link"; + else if (type == atf_fs_stat_reg_type) + str = "regular file"; + else if (type == atf_fs_stat_sock_type) + str = "socket"; + else if (type == atf_fs_stat_wht_type) + str = "whiteout"; + else { + UNREACHABLE; + str = NULL; + } + + return str; +} + +/* --------------------------------------------------------------------- + * The "atf_fs_path" type. + * --------------------------------------------------------------------- */ + +/* + * Constructors/destructors. + */ + +atf_error_t +atf_fs_path_init_ap(atf_fs_path_t *p, const char *fmt, va_list ap) +{ + atf_error_t err; + va_list ap2; + + va_copy(ap2, ap); + err = normalize_ap(&p->m_data, fmt, ap2); + va_end(ap2); + + return err; +} + +atf_error_t +atf_fs_path_init_fmt(atf_fs_path_t *p, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = atf_fs_path_init_ap(p, fmt, ap); + va_end(ap); + + return err; +} + +atf_error_t +atf_fs_path_copy(atf_fs_path_t *dest, const atf_fs_path_t *src) +{ + return atf_dynstr_copy(&dest->m_data, &src->m_data); +} + +void +atf_fs_path_fini(atf_fs_path_t *p) +{ + atf_dynstr_fini(&p->m_data); +} + +/* + * Getters. + */ + +atf_error_t +atf_fs_path_branch_path(const atf_fs_path_t *p, atf_fs_path_t *bp) +{ + const size_t endpos = atf_dynstr_rfind_ch(&p->m_data, '/'); + atf_error_t err; + + if (endpos == atf_dynstr_npos) + err = atf_fs_path_init_fmt(bp, "."); + else if (endpos == 0) + err = atf_fs_path_init_fmt(bp, "/"); + else + err = atf_dynstr_init_substr(&bp->m_data, &p->m_data, 0, endpos); + +#if defined(HAVE_CONST_DIRNAME) + INV(atf_equal_dynstr_cstring(&bp->m_data, + dirname(atf_dynstr_cstring(&p->m_data)))); +#endif /* defined(HAVE_CONST_DIRNAME) */ + + return err; +} + +const char * +atf_fs_path_cstring(const atf_fs_path_t *p) +{ + return atf_dynstr_cstring(&p->m_data); +} + +atf_error_t +atf_fs_path_leaf_name(const atf_fs_path_t *p, atf_dynstr_t *ln) +{ + size_t begpos = atf_dynstr_rfind_ch(&p->m_data, '/'); + atf_error_t err; + + if (begpos == atf_dynstr_npos) + begpos = 0; + else + begpos++; + + err = atf_dynstr_init_substr(ln, &p->m_data, begpos, atf_dynstr_npos); + +#if defined(HAVE_CONST_BASENAME) + INV(atf_equal_dynstr_cstring(ln, + basename(atf_dynstr_cstring(&p->m_data)))); +#endif /* defined(HAVE_CONST_BASENAME) */ + + return err; +} + +bool +atf_fs_path_is_absolute(const atf_fs_path_t *p) +{ + return atf_dynstr_cstring(&p->m_data)[0] == '/'; +} + +bool +atf_fs_path_is_root(const atf_fs_path_t *p) +{ + return atf_equal_dynstr_cstring(&p->m_data, "/"); +} + +/* + * Modifiers. + */ + +atf_error_t +atf_fs_path_append_ap(atf_fs_path_t *p, const char *fmt, va_list ap) +{ + atf_dynstr_t aux; + atf_error_t err; + va_list ap2; + + va_copy(ap2, ap); + err = normalize_ap(&aux, fmt, ap2); + va_end(ap2); + if (!atf_is_error(err)) { + const char *auxstr = atf_dynstr_cstring(&aux); + const bool needslash = auxstr[0] != '/'; + + err = atf_dynstr_append_fmt(&p->m_data, "%s%s", + needslash ? "/" : "", auxstr); + + atf_dynstr_fini(&aux); + } + + return err; +} + +atf_error_t +atf_fs_path_append_fmt(atf_fs_path_t *p, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = atf_fs_path_append_ap(p, fmt, ap); + va_end(ap); + + return err; +} + +atf_error_t +atf_fs_path_append_path(atf_fs_path_t *p, const atf_fs_path_t *p2) +{ + return atf_fs_path_append_fmt(p, "%s", atf_dynstr_cstring(&p2->m_data)); +} + +atf_error_t +atf_fs_path_to_absolute(const atf_fs_path_t *p, atf_fs_path_t *pa) +{ + atf_error_t err; + + PRE(!atf_fs_path_is_absolute(p)); + + err = atf_fs_getcwd(pa); + if (atf_is_error(err)) + goto out; + + err = atf_fs_path_append_path(pa, p); + if (atf_is_error(err)) + atf_fs_path_fini(pa); + +out: + return err; +} + +/* + * Operators. + */ + +bool atf_equal_fs_path_fs_path(const atf_fs_path_t *p1, + const atf_fs_path_t *p2) +{ + return atf_equal_dynstr_dynstr(&p1->m_data, &p2->m_data); +} + +/* --------------------------------------------------------------------- + * The "atf_fs_path" type. + * --------------------------------------------------------------------- */ + +/* + * Constants. + */ + +const int atf_fs_stat_blk_type = 1; +const int atf_fs_stat_chr_type = 2; +const int atf_fs_stat_dir_type = 3; +const int atf_fs_stat_fifo_type = 4; +const int atf_fs_stat_lnk_type = 5; +const int atf_fs_stat_reg_type = 6; +const int atf_fs_stat_sock_type = 7; +const int atf_fs_stat_wht_type = 8; + +/* + * Constructors/destructors. + */ + +atf_error_t +atf_fs_stat_init(atf_fs_stat_t *st, const atf_fs_path_t *p) +{ + atf_error_t err; + const char *pstr = atf_fs_path_cstring(p); + + if (lstat(pstr, &st->m_sb) == -1) { + err = atf_libc_error(errno, "Cannot get information of %s; " + "lstat(2) failed", pstr); + } else { + int type = st->m_sb.st_mode & S_IFMT; + err = atf_no_error(); + switch (type) { + case S_IFBLK: st->m_type = atf_fs_stat_blk_type; break; + case S_IFCHR: st->m_type = atf_fs_stat_chr_type; break; + case S_IFDIR: st->m_type = atf_fs_stat_dir_type; break; + case S_IFIFO: st->m_type = atf_fs_stat_fifo_type; break; + case S_IFLNK: st->m_type = atf_fs_stat_lnk_type; break; + case S_IFREG: st->m_type = atf_fs_stat_reg_type; break; + case S_IFSOCK: st->m_type = atf_fs_stat_sock_type; break; +#if defined(S_IFWHT) + case S_IFWHT: st->m_type = atf_fs_stat_wht_type; break; +#endif + default: + err = unknown_type_error(pstr, type); + } + } + + return err; +} + +void +atf_fs_stat_copy(atf_fs_stat_t *dest, const atf_fs_stat_t *src) +{ + dest->m_type = src->m_type; + dest->m_sb = src->m_sb; +} + +void +atf_fs_stat_fini(atf_fs_stat_t *st ATF_DEFS_ATTRIBUTE_UNUSED) +{ +} + +/* + * Getters. + */ + +dev_t +atf_fs_stat_get_device(const atf_fs_stat_t *st) +{ + return st->m_sb.st_dev; +} + +ino_t +atf_fs_stat_get_inode(const atf_fs_stat_t *st) +{ + return st->m_sb.st_ino; +} + +mode_t +atf_fs_stat_get_mode(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & ~S_IFMT; +} + +off_t +atf_fs_stat_get_size(const atf_fs_stat_t *st) +{ + return st->m_sb.st_size; +} + +int +atf_fs_stat_get_type(const atf_fs_stat_t *st) +{ + return st->m_type; +} + +bool +atf_fs_stat_is_owner_readable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IRUSR; +} + +bool +atf_fs_stat_is_owner_writable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IWUSR; +} + +bool +atf_fs_stat_is_owner_executable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IXUSR; +} + +bool +atf_fs_stat_is_group_readable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IRGRP; +} + +bool +atf_fs_stat_is_group_writable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IWGRP; +} + +bool +atf_fs_stat_is_group_executable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IXGRP; +} + +bool +atf_fs_stat_is_other_readable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IROTH; +} + +bool +atf_fs_stat_is_other_writable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IWOTH; +} + +bool +atf_fs_stat_is_other_executable(const atf_fs_stat_t *st) +{ + return st->m_sb.st_mode & S_IXOTH; +} + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +const int atf_fs_access_f = 1 << 0; +const int atf_fs_access_r = 1 << 1; +const int atf_fs_access_w = 1 << 2; +const int atf_fs_access_x = 1 << 3; + +/* + * An implementation of access(2) but using the effective user value + * instead of the real one. Also avoids false positives for root when + * asking for execute permissions, which appear in SunOS. + */ +atf_error_t +atf_fs_eaccess(const atf_fs_path_t *p, int mode) +{ + atf_error_t err; + struct stat st; + bool ok; + + PRE(mode & atf_fs_access_f || mode & atf_fs_access_r || + mode & atf_fs_access_w || mode & atf_fs_access_x); + + if (lstat(atf_fs_path_cstring(p), &st) == -1) { + err = atf_libc_error(errno, "Cannot get information from file %s", + atf_fs_path_cstring(p)); + goto out; + } + + err = atf_no_error(); + + /* Early return if we are only checking for existence and the file + * exists (stat call returned). */ + if (mode & atf_fs_access_f) + goto out; + + ok = false; + if (atf_user_is_root()) { + if (!ok && !(mode & atf_fs_access_x)) { + /* Allow root to read/write any file. */ + ok = true; + } + + if (!ok && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { + /* Allow root to execute the file if any of its execution bits + * are set. */ + ok = true; + } + } else { + if (!ok && (atf_user_euid() == st.st_uid)) { + ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRUSR)) || + ((mode & atf_fs_access_w) && (st.st_mode & S_IWUSR)) || + ((mode & atf_fs_access_x) && (st.st_mode & S_IXUSR)); + } + if (!ok && atf_user_is_member_of_group(st.st_gid)) { + ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IRGRP)) || + ((mode & atf_fs_access_w) && (st.st_mode & S_IWGRP)) || + ((mode & atf_fs_access_x) && (st.st_mode & S_IXGRP)); + } + if (!ok && ((atf_user_euid() != st.st_uid) && + !atf_user_is_member_of_group(st.st_gid))) { + ok = ((mode & atf_fs_access_r) && (st.st_mode & S_IROTH)) || + ((mode & atf_fs_access_w) && (st.st_mode & S_IWOTH)) || + ((mode & atf_fs_access_x) && (st.st_mode & S_IXOTH)); + } + } + + if (!ok) + err = atf_libc_error(EACCES, "Access check failed"); + +out: + return err; +} + +atf_error_t +atf_fs_exists(const atf_fs_path_t *p, bool *b) +{ + atf_error_t err; + + err = atf_fs_eaccess(p, atf_fs_access_f); + if (atf_is_error(err)) { + if (atf_error_is(err, "libc") && atf_libc_error_code(err) == ENOENT) { + atf_error_free(err); + err = atf_no_error(); + *b = false; + } + } else + *b = true; + + return err; +} + +atf_error_t +atf_fs_getcwd(atf_fs_path_t *p) +{ + atf_error_t err; + char *cwd; + +#if defined(HAVE_GETCWD_DYN) + cwd = getcwd(NULL, 0); +#else + cwd = getcwd(NULL, MAXPATHLEN); +#endif + if (cwd == NULL) { + err = atf_libc_error(errno, "Cannot determine current directory"); + goto out; + } + + err = atf_fs_path_init_fmt(p, "%s", cwd); + free(cwd); + +out: + return err; +} + +atf_error_t +atf_fs_mkdtemp(atf_fs_path_t *p) +{ + atf_error_t err; + char *buf; + + if (!check_umask(S_IRWXU, S_IRWXU)) { + err = invalid_umask_error(p, atf_fs_stat_dir_type, current_umask()); + goto out; + } + + err = copy_contents(p, &buf); + if (atf_is_error(err)) + goto out; + + err = do_mkdtemp(buf); + if (atf_is_error(err)) + goto out_buf; + + replace_contents(p, buf); + + INV(!atf_is_error(err)); +out_buf: + free(buf); +out: + return err; +} + +atf_error_t +atf_fs_mkstemp(atf_fs_path_t *p, int *fdout) +{ + atf_error_t err; + char *buf; + int fd; + + if (!check_umask(S_IRWXU, S_IRWXU)) { + err = invalid_umask_error(p, atf_fs_stat_reg_type, current_umask()); + goto out; + } + + err = copy_contents(p, &buf); + if (atf_is_error(err)) + goto out; + + err = do_mkstemp(buf, &fd); + if (atf_is_error(err)) + goto out_buf; + + replace_contents(p, buf); + *fdout = fd; + + INV(!atf_is_error(err)); +out_buf: + free(buf); +out: + return err; +} + +atf_error_t +atf_fs_rmdir(const atf_fs_path_t *p) +{ + atf_error_t err; + + if (rmdir(atf_fs_path_cstring(p))) { + if (errno == EEXIST) { + /* Some operating systems (e.g. OpenSolaris 200906) return + * EEXIST instead of ENOTEMPTY for non-empty directories. + * Homogenize the return value so that callers don't need + * to bother about differences in operating systems. */ + errno = ENOTEMPTY; + } + err = atf_libc_error(errno, "Cannot remove directory"); + } else + err = atf_no_error(); + + return err; +} + +atf_error_t +atf_fs_unlink(const atf_fs_path_t *p) +{ + atf_error_t err; + const char *path; + + path = atf_fs_path_cstring(p); + + if (unlink(path) != 0) + err = atf_libc_error(errno, "Cannot unlink file: '%s'", path); + else + err = atf_no_error(); + + return err; +} diff --git a/unit/atf-src/atf-c/detail/fs.h b/unit/atf-src/atf-c/detail/fs.h new file mode 100644 index 0000000..1c6c0eb --- /dev/null +++ b/unit/atf-src/atf-c/detail/fs.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_FS_H) +#define ATF_C_DETAIL_FS_H + +#include <sys/types.h> +#include <sys/stat.h> + +#include <stdarg.h> +#include <stdbool.h> + +#include <atf-c/detail/dynstr.h> +#include <atf-c/error_fwd.h> + +/* --------------------------------------------------------------------- + * The "atf_fs_path" type. + * --------------------------------------------------------------------- */ + +struct atf_fs_path { + atf_dynstr_t m_data; +}; +typedef struct atf_fs_path atf_fs_path_t; + +/* Constructors/destructors. */ +atf_error_t atf_fs_path_init_ap(atf_fs_path_t *, const char *, va_list); +atf_error_t atf_fs_path_init_fmt(atf_fs_path_t *, const char *, ...); +atf_error_t atf_fs_path_copy(atf_fs_path_t *, const atf_fs_path_t *); +void atf_fs_path_fini(atf_fs_path_t *); + +/* Getters. */ +atf_error_t atf_fs_path_branch_path(const atf_fs_path_t *, atf_fs_path_t *); +const char *atf_fs_path_cstring(const atf_fs_path_t *); +atf_error_t atf_fs_path_leaf_name(const atf_fs_path_t *, atf_dynstr_t *); +bool atf_fs_path_is_absolute(const atf_fs_path_t *); +bool atf_fs_path_is_root(const atf_fs_path_t *); + +/* Modifiers. */ +atf_error_t atf_fs_path_append_ap(atf_fs_path_t *, const char *, va_list); +atf_error_t atf_fs_path_append_fmt(atf_fs_path_t *, const char *, ...); +atf_error_t atf_fs_path_append_path(atf_fs_path_t *, const atf_fs_path_t *); +atf_error_t atf_fs_path_to_absolute(const atf_fs_path_t *, atf_fs_path_t *); + +/* Operators. */ +bool atf_equal_fs_path_fs_path(const atf_fs_path_t *, + const atf_fs_path_t *); + +/* --------------------------------------------------------------------- + * The "atf_fs_stat" type. + * --------------------------------------------------------------------- */ + +struct atf_fs_stat { + int m_type; + struct stat m_sb; +}; +typedef struct atf_fs_stat atf_fs_stat_t; + +/* Constants. */ +extern const int atf_fs_stat_blk_type; +extern const int atf_fs_stat_chr_type; +extern const int atf_fs_stat_dir_type; +extern const int atf_fs_stat_fifo_type; +extern const int atf_fs_stat_lnk_type; +extern const int atf_fs_stat_reg_type; +extern const int atf_fs_stat_sock_type; +extern const int atf_fs_stat_wht_type; + +/* Constructors/destructors. */ +atf_error_t atf_fs_stat_init(atf_fs_stat_t *, const atf_fs_path_t *); +void atf_fs_stat_copy(atf_fs_stat_t *, const atf_fs_stat_t *); +void atf_fs_stat_fini(atf_fs_stat_t *); + +/* Getters. */ +dev_t atf_fs_stat_get_device(const atf_fs_stat_t *); +ino_t atf_fs_stat_get_inode(const atf_fs_stat_t *); +mode_t atf_fs_stat_get_mode(const atf_fs_stat_t *); +off_t atf_fs_stat_get_size(const atf_fs_stat_t *); +int atf_fs_stat_get_type(const atf_fs_stat_t *); +bool atf_fs_stat_is_owner_readable(const atf_fs_stat_t *); +bool atf_fs_stat_is_owner_writable(const atf_fs_stat_t *); +bool atf_fs_stat_is_owner_executable(const atf_fs_stat_t *); +bool atf_fs_stat_is_group_readable(const atf_fs_stat_t *); +bool atf_fs_stat_is_group_writable(const atf_fs_stat_t *); +bool atf_fs_stat_is_group_executable(const atf_fs_stat_t *); +bool atf_fs_stat_is_other_readable(const atf_fs_stat_t *); +bool atf_fs_stat_is_other_writable(const atf_fs_stat_t *); +bool atf_fs_stat_is_other_executable(const atf_fs_stat_t *); + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +extern const int atf_fs_access_f; +extern const int atf_fs_access_r; +extern const int atf_fs_access_w; +extern const int atf_fs_access_x; + +atf_error_t atf_fs_eaccess(const atf_fs_path_t *, int); +atf_error_t atf_fs_exists(const atf_fs_path_t *, bool *); +atf_error_t atf_fs_getcwd(atf_fs_path_t *); +atf_error_t atf_fs_mkdtemp(atf_fs_path_t *); +atf_error_t atf_fs_mkstemp(atf_fs_path_t *, int *); +atf_error_t atf_fs_rmdir(const atf_fs_path_t *); +atf_error_t atf_fs_unlink(const atf_fs_path_t *); + +#endif /* !defined(ATF_C_DETAIL_FS_H) */ diff --git a/unit/atf-src/atf-c/detail/fs_test.c b/unit/atf-src/atf-c/detail/fs_test.c new file mode 100644 index 0000000..3dbc4d3 --- /dev/null +++ b/unit/atf-src/atf-c/detail/fs_test.c @@ -0,0 +1,1079 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/fs.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" +#include "atf-c/detail/user.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +void +create_dir(const char *p, int mode) +{ + int ret; + + ret = mkdir(p, mode); + if (ret == -1) + atf_tc_fail("Could not create helper directory %s", p); +} + +static +void +create_file(const char *p, int mode) +{ + int fd; + + fd = open(p, O_CREAT | O_WRONLY | O_TRUNC, mode); + if (fd == -1) + atf_tc_fail("Could not create helper file %s", p); + close(fd); +} + +static +bool +exists(const atf_fs_path_t *p) +{ + return access(atf_fs_path_cstring(p), F_OK) == 0; +} + +static +atf_error_t +mkstemp_discard_fd(atf_fs_path_t *p) +{ + int fd; + atf_error_t err = atf_fs_mkstemp(p, &fd); + if (!atf_is_error(err)) + close(fd); + return err; +} + +/* --------------------------------------------------------------------- + * Test cases for the "atf_fs_path" type. + * --------------------------------------------------------------------- */ + +ATF_TC(path_normalize); +ATF_TC_HEAD(path_normalize, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the path's normalization"); +} +ATF_TC_BODY(path_normalize, tc) +{ + struct test { + const char *in; + const char *out; + } tests[] = { + { ".", ".", }, + { "..", "..", }, + + { "/", "/", }, + { "//", "/", }, /* NO_CHECK_STYLE */ + { "///", "/", }, /* NO_CHECK_STYLE */ + + { "foo", "foo", }, + { "foo/", "foo", }, + { "foo/bar", "foo/bar", }, + { "foo/bar/", "foo/bar", }, + + { "/foo", "/foo", }, + { "/foo/bar", "/foo/bar", }, + { "/foo/bar/", "/foo/bar", }, + + { "///foo", "/foo", }, /* NO_CHECK_STYLE */ + { "///foo///bar", "/foo/bar", }, /* NO_CHECK_STYLE */ + { "///foo///bar///", "/foo/bar", }, /* NO_CHECK_STYLE */ + + { NULL, NULL } + }; + struct test *t; + + for (t = &tests[0]; t->in != NULL; t++) { + atf_fs_path_t p; + + printf("Input : >%s<\n", t->in); + printf("Expected output: >%s<\n", t->out); + + RE(atf_fs_path_init_fmt(&p, "%s", t->in)); + printf("Output : >%s<\n", atf_fs_path_cstring(&p)); + ATF_REQUIRE(strcmp(atf_fs_path_cstring(&p), t->out) == 0); + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_copy); +ATF_TC_HEAD(path_copy, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_copy constructor"); +} +ATF_TC_BODY(path_copy, tc) +{ + atf_fs_path_t str, str2; + + RE(atf_fs_path_init_fmt(&str, "foo")); + RE(atf_fs_path_copy(&str2, &str)); + + ATF_REQUIRE(atf_equal_fs_path_fs_path(&str, &str2)); + + RE(atf_fs_path_append_fmt(&str2, "bar")); + + ATF_REQUIRE(!atf_equal_fs_path_fs_path(&str, &str2)); + + atf_fs_path_fini(&str2); + atf_fs_path_fini(&str); +} + +ATF_TC(path_is_absolute); +ATF_TC_HEAD(path_is_absolute, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the path::is_absolute function"); +} +ATF_TC_BODY(path_is_absolute, tc) +{ + struct test { + const char *in; + bool abs; + } tests[] = { + { "/", true }, + { "////", true }, /* NO_CHECK_STYLE */ + { "////a", true }, /* NO_CHECK_STYLE */ + { "//a//", true }, /* NO_CHECK_STYLE */ + { "a////", false }, /* NO_CHECK_STYLE */ + { "../foo", false }, + { NULL, false }, + }; + struct test *t; + + for (t = &tests[0]; t->in != NULL; t++) { + atf_fs_path_t p; + + printf("Input : %s\n", t->in); + printf("Expected result: %s\n", t->abs ? "true" : "false"); + + RE(atf_fs_path_init_fmt(&p, "%s", t->in)); + printf("Result : %s\n", + atf_fs_path_is_absolute(&p) ? "true" : "false"); + if (t->abs) + ATF_REQUIRE(atf_fs_path_is_absolute(&p)); + else + ATF_REQUIRE(!atf_fs_path_is_absolute(&p)); + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_is_root); +ATF_TC_HEAD(path_is_root, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the path::is_root function"); +} +ATF_TC_BODY(path_is_root, tc) +{ + struct test { + const char *in; + bool root; + } tests[] = { + { "/", true }, + { "////", true }, /* NO_CHECK_STYLE */ + { "////a", false }, /* NO_CHECK_STYLE */ + { "//a//", false }, /* NO_CHECK_STYLE */ + { "a////", false }, /* NO_CHECK_STYLE */ + { "../foo", false }, + { NULL, false }, + }; + struct test *t; + + for (t = &tests[0]; t->in != NULL; t++) { + atf_fs_path_t p; + + printf("Input : %s\n", t->in); + printf("Expected result: %s\n", t->root ? "true" : "false"); + + RE(atf_fs_path_init_fmt(&p, "%s", t->in)); + printf("Result : %s\n", + atf_fs_path_is_root(&p) ? "true" : "false"); + if (t->root) + ATF_REQUIRE(atf_fs_path_is_root(&p)); + else + ATF_REQUIRE(!atf_fs_path_is_root(&p)); + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_branch_path); +ATF_TC_HEAD(path_branch_path, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_branch_path " + "function"); +} +ATF_TC_BODY(path_branch_path, tc) +{ + struct test { + const char *in; + const char *branch; + } tests[] = { + { ".", "." }, + { "foo", "." }, + { "foo/bar", "foo" }, + { "/foo", "/" }, + { "/foo/bar", "/foo" }, + { NULL, NULL }, + }; + struct test *t; + + for (t = &tests[0]; t->in != NULL; t++) { + atf_fs_path_t p, bp; + + printf("Input : %s\n", t->in); + printf("Expected output: %s\n", t->branch); + + RE(atf_fs_path_init_fmt(&p, "%s", t->in)); + RE(atf_fs_path_branch_path(&p, &bp)); + printf("Output : %s\n", atf_fs_path_cstring(&bp)); + ATF_REQUIRE(strcmp(atf_fs_path_cstring(&bp), t->branch) == 0); + atf_fs_path_fini(&bp); + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_leaf_name); +ATF_TC_HEAD(path_leaf_name, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_leaf_name " + "function"); +} +ATF_TC_BODY(path_leaf_name, tc) +{ + struct test { + const char *in; + const char *leaf; + } tests[] = { + { ".", "." }, + { "foo", "foo" }, + { "foo/bar", "bar" }, + { "/foo", "foo" }, + { "/foo/bar", "bar" }, + { NULL, NULL }, + }; + struct test *t; + + for (t = &tests[0]; t->in != NULL; t++) { + atf_fs_path_t p; + atf_dynstr_t ln; + + printf("Input : %s\n", t->in); + printf("Expected output: %s\n", t->leaf); + + RE(atf_fs_path_init_fmt(&p, "%s", t->in)); + RE(atf_fs_path_leaf_name(&p, &ln)); + printf("Output : %s\n", atf_dynstr_cstring(&ln)); + ATF_REQUIRE(atf_equal_dynstr_cstring(&ln, t->leaf)); + atf_dynstr_fini(&ln); + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_append); +ATF_TC_HEAD(path_append, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the concatenation of multiple " + "paths"); +} +ATF_TC_BODY(path_append, tc) +{ + struct test { + const char *in; + const char *ap; + const char *out; + } tests[] = { + { "foo", "bar", "foo/bar" }, + { "foo/", "/bar", "foo/bar" }, + { "foo/", "/bar/baz", "foo/bar/baz" }, + { "foo/", "///bar///baz", "foo/bar/baz" }, /* NO_CHECK_STYLE */ + + { NULL, NULL, NULL } + }; + struct test *t; + + for (t = &tests[0]; t->in != NULL; t++) { + atf_fs_path_t p; + + printf("Input : >%s<\n", t->in); + printf("Append : >%s<\n", t->ap); + printf("Expected output: >%s<\n", t->out); + + RE(atf_fs_path_init_fmt(&p, "%s", t->in)); + + RE(atf_fs_path_append_fmt(&p, "%s", t->ap)); + + printf("Output : >%s<\n", atf_fs_path_cstring(&p)); + ATF_REQUIRE(strcmp(atf_fs_path_cstring(&p), t->out) == 0); + + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_to_absolute); +ATF_TC_HEAD(path_to_absolute, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_path_to_absolute " + "function"); +} +ATF_TC_BODY(path_to_absolute, tc) +{ + const char *names[] = { ".", "dir", NULL }; + const char **n; + + ATF_REQUIRE(mkdir("dir", 0755) != -1); + + for (n = names; *n != NULL; n++) { + atf_fs_path_t p, p2; + atf_fs_stat_t st1, st2; + + RE(atf_fs_path_init_fmt(&p, "%s", *n)); + RE(atf_fs_stat_init(&st1, &p)); + printf("Relative path: %s\n", atf_fs_path_cstring(&p)); + + RE(atf_fs_path_to_absolute(&p, &p2)); + printf("Absolute path: %s\n", atf_fs_path_cstring(&p2)); + + ATF_REQUIRE(atf_fs_path_is_absolute(&p2)); + RE(atf_fs_stat_init(&st2, &p2)); + + ATF_REQUIRE_EQ(atf_fs_stat_get_device(&st1), + atf_fs_stat_get_device(&st2)); + ATF_REQUIRE_EQ(atf_fs_stat_get_inode(&st1), + atf_fs_stat_get_inode(&st2)); + + atf_fs_stat_fini(&st2); + atf_fs_stat_fini(&st1); + atf_fs_path_fini(&p2); + atf_fs_path_fini(&p); + + printf("\n"); + } +} + +ATF_TC(path_equal); +ATF_TC_HEAD(path_equal, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the equality operators for paths"); +} +ATF_TC_BODY(path_equal, tc) +{ + atf_fs_path_t p1, p2; + + RE(atf_fs_path_init_fmt(&p1, "foo")); + + RE(atf_fs_path_init_fmt(&p2, "foo")); + ATF_REQUIRE(atf_equal_fs_path_fs_path(&p1, &p2)); + atf_fs_path_fini(&p2); + + RE(atf_fs_path_init_fmt(&p2, "bar")); + ATF_REQUIRE(!atf_equal_fs_path_fs_path(&p1, &p2)); + atf_fs_path_fini(&p2); + + atf_fs_path_fini(&p1); +} + +/* --------------------------------------------------------------------- + * Test cases for the "atf_fs_stat" type. + * --------------------------------------------------------------------- */ + +ATF_TC(stat_mode); +ATF_TC_HEAD(stat_mode, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_stat_get_mode function " + "and, indirectly, the constructor"); +} +ATF_TC_BODY(stat_mode, tc) +{ + atf_fs_path_t p; + atf_fs_stat_t st; + + create_file("f1", 0400); + create_file("f2", 0644); + + RE(atf_fs_path_init_fmt(&p, "f1")); + RE(atf_fs_stat_init(&st, &p)); + ATF_CHECK_EQ(0400, atf_fs_stat_get_mode(&st)); + atf_fs_stat_fini(&st); + atf_fs_path_fini(&p); + + RE(atf_fs_path_init_fmt(&p, "f2")); + RE(atf_fs_stat_init(&st, &p)); + ATF_CHECK_EQ(0644, atf_fs_stat_get_mode(&st)); + atf_fs_stat_fini(&st); + atf_fs_path_fini(&p); +} + +ATF_TC(stat_type); +ATF_TC_HEAD(stat_type, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_stat_get_type function " + "and, indirectly, the constructor"); +} +ATF_TC_BODY(stat_type, tc) +{ + atf_fs_path_t p; + atf_fs_stat_t st; + + create_dir("dir", 0755); + create_file("reg", 0644); + + RE(atf_fs_path_init_fmt(&p, "dir")); + RE(atf_fs_stat_init(&st, &p)); + ATF_REQUIRE_EQ(atf_fs_stat_get_type(&st), atf_fs_stat_dir_type); + atf_fs_stat_fini(&st); + atf_fs_path_fini(&p); + + RE(atf_fs_path_init_fmt(&p, "reg")); + RE(atf_fs_stat_init(&st, &p)); + ATF_REQUIRE_EQ(atf_fs_stat_get_type(&st), atf_fs_stat_reg_type); + atf_fs_stat_fini(&st); + atf_fs_path_fini(&p); +} + +ATF_TC(stat_perms); +ATF_TC_HEAD(stat_perms, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_stat_is_* functions"); +} +ATF_TC_BODY(stat_perms, tc) +{ + atf_fs_path_t p; + atf_fs_stat_t st; + + create_file("reg", 0); + + RE(atf_fs_path_init_fmt(&p, "reg")); + +#define perms(ur, uw, ux, gr, gw, gx, othr, othw, othx) \ + { \ + RE(atf_fs_stat_init(&st, &p)); \ + ATF_REQUIRE(atf_fs_stat_is_owner_readable(&st) == ur); \ + ATF_REQUIRE(atf_fs_stat_is_owner_writable(&st) == uw); \ + ATF_REQUIRE(atf_fs_stat_is_owner_executable(&st) == ux); \ + ATF_REQUIRE(atf_fs_stat_is_group_readable(&st) == gr); \ + ATF_REQUIRE(atf_fs_stat_is_group_writable(&st) == gw); \ + ATF_REQUIRE(atf_fs_stat_is_group_executable(&st) == gx); \ + ATF_REQUIRE(atf_fs_stat_is_other_readable(&st) == othr); \ + ATF_REQUIRE(atf_fs_stat_is_other_writable(&st) == othw); \ + ATF_REQUIRE(atf_fs_stat_is_other_executable(&st) == othx); \ + atf_fs_stat_fini(&st); \ + } + + chmod("reg", 0000); + perms(false, false, false, false, false, false, false, false, false); + + chmod("reg", 0001); + perms(false, false, false, false, false, false, false, false, true); + + chmod("reg", 0010); + perms(false, false, false, false, false, true, false, false, false); + + chmod("reg", 0100); + perms(false, false, true, false, false, false, false, false, false); + + chmod("reg", 0002); + perms(false, false, false, false, false, false, false, true, false); + + chmod("reg", 0020); + perms(false, false, false, false, true, false, false, false, false); + + chmod("reg", 0200); + perms(false, true, false, false, false, false, false, false, false); + + chmod("reg", 0004); + perms(false, false, false, false, false, false, true, false, false); + + chmod("reg", 0040); + perms(false, false, false, true, false, false, false, false, false); + + chmod("reg", 0400); + perms(true, false, false, false, false, false, false, false, false); + + chmod("reg", 0644); + perms(true, true, false, true, false, false, true, false, false); + + chmod("reg", 0755); + perms(true, true, true, true, false, true, true, false, true); + + chmod("reg", 0777); + perms(true, true, true, true, true, true, true, true, true); + +#undef perms + + atf_fs_path_fini(&p); +} + +/* --------------------------------------------------------------------- + * Test cases for the free functions. + * --------------------------------------------------------------------- */ + +ATF_TC(exists); +ATF_TC_HEAD(exists, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_exists function"); +} +ATF_TC_BODY(exists, tc) +{ + atf_error_t err; + atf_fs_path_t pdir, pfile; + bool b; + + RE(atf_fs_path_init_fmt(&pdir, "dir")); + RE(atf_fs_path_init_fmt(&pfile, "dir/file")); + + create_dir(atf_fs_path_cstring(&pdir), 0755); + create_file(atf_fs_path_cstring(&pfile), 0644); + + printf("Checking existence of a directory\n"); + RE(atf_fs_exists(&pdir, &b)); + ATF_REQUIRE(b); + + printf("Checking existence of a file\n"); + RE(atf_fs_exists(&pfile, &b)); + ATF_REQUIRE(b); + + /* XXX: This should probably be a separate test case to let the user + * be aware that some tests were skipped because privileges were not + * correct. */ + if (!atf_user_is_root()) { + printf("Checking existence of a file inside a directory without " + "permissions\n"); + ATF_REQUIRE(chmod(atf_fs_path_cstring(&pdir), 0000) != -1); + err = atf_fs_exists(&pfile, &b); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_REQUIRE(chmod(atf_fs_path_cstring(&pdir), 0755) != -1); + atf_error_free(err); + } + + printf("Checking existence of a non-existent file\n"); + ATF_REQUIRE(unlink(atf_fs_path_cstring(&pfile)) != -1); + RE(atf_fs_exists(&pfile, &b)); + ATF_REQUIRE(!b); + + atf_fs_path_fini(&pfile); + atf_fs_path_fini(&pdir); +} + +ATF_TC(eaccess); +ATF_TC_HEAD(eaccess, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_eaccess function"); +} +ATF_TC_BODY(eaccess, tc) +{ + const int modes[] = { atf_fs_access_f, atf_fs_access_r, atf_fs_access_w, + atf_fs_access_x, 0 }; + const int *m; + struct tests { + mode_t fmode; + int amode; + int uerror; + int rerror; + } tests[] = { + { 0000, atf_fs_access_r, EACCES, 0 }, + { 0000, atf_fs_access_w, EACCES, 0 }, + { 0000, atf_fs_access_x, EACCES, EACCES }, + + { 0001, atf_fs_access_r, EACCES, 0 }, + { 0001, atf_fs_access_w, EACCES, 0 }, + { 0001, atf_fs_access_x, EACCES, 0 }, + { 0002, atf_fs_access_r, EACCES, 0 }, + { 0002, atf_fs_access_w, EACCES, 0 }, + { 0002, atf_fs_access_x, EACCES, EACCES }, + { 0004, atf_fs_access_r, EACCES, 0 }, + { 0004, atf_fs_access_w, EACCES, 0 }, + { 0004, atf_fs_access_x, EACCES, EACCES }, + + { 0010, atf_fs_access_r, EACCES, 0 }, + { 0010, atf_fs_access_w, EACCES, 0 }, + { 0010, atf_fs_access_x, 0, 0 }, + { 0020, atf_fs_access_r, EACCES, 0 }, + { 0020, atf_fs_access_w, 0, 0 }, + { 0020, atf_fs_access_x, EACCES, EACCES }, + { 0040, atf_fs_access_r, 0, 0 }, + { 0040, atf_fs_access_w, EACCES, 0 }, + { 0040, atf_fs_access_x, EACCES, EACCES }, + + { 0100, atf_fs_access_r, EACCES, 0 }, + { 0100, atf_fs_access_w, EACCES, 0 }, + { 0100, atf_fs_access_x, 0, 0 }, + { 0200, atf_fs_access_r, EACCES, 0 }, + { 0200, atf_fs_access_w, 0, 0 }, + { 0200, atf_fs_access_x, EACCES, EACCES }, + { 0400, atf_fs_access_r, 0, 0 }, + { 0400, atf_fs_access_w, EACCES, 0 }, + { 0400, atf_fs_access_x, EACCES, EACCES }, + + { 0, 0, 0, 0 } + }; + struct tests *t; + atf_fs_path_t p; + atf_error_t err; + + RE(atf_fs_path_init_fmt(&p, "the-file")); + + printf("Non-existent file checks\n"); + for (m = &modes[0]; *m != 0; m++) { + err = atf_fs_eaccess(&p, *m); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_REQUIRE_EQ(atf_libc_error_code(err), ENOENT); + atf_error_free(err); + } + + create_file(atf_fs_path_cstring(&p), 0000); + ATF_REQUIRE(chown(atf_fs_path_cstring(&p), geteuid(), getegid()) != -1); + + for (t = &tests[0]; t->amode != 0; t++) { + const int experr = atf_user_is_root() ? t->rerror : t->uerror; + + printf("\n"); + printf("File mode : %04o\n", (unsigned int)t->fmode); + printf("Access mode : 0x%02x\n", t->amode); + + ATF_REQUIRE(chmod(atf_fs_path_cstring(&p), t->fmode) != -1); + + /* First, existence check. */ + err = atf_fs_eaccess(&p, atf_fs_access_f); + ATF_REQUIRE(!atf_is_error(err)); + + /* Now do the specific test case. */ + printf("Expected error: %d\n", experr); + err = atf_fs_eaccess(&p, t->amode); + if (atf_is_error(err)) { + if (atf_error_is(err, "libc")) + printf("Error : %d\n", atf_libc_error_code(err)); + else + printf("Error : Non-libc error\n"); + } else + printf("Error : None\n"); + if (experr == 0) { + ATF_REQUIRE(!atf_is_error(err)); + } else { + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_REQUIRE_EQ(atf_libc_error_code(err), experr); + atf_error_free(err); + } + } + + atf_fs_path_fini(&p); +} + +ATF_TC(getcwd); +ATF_TC_HEAD(getcwd, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_getcwd function"); +} +ATF_TC_BODY(getcwd, tc) +{ + atf_fs_path_t cwd1, cwd2; + + create_dir ("root", 0755); + + RE(atf_fs_getcwd(&cwd1)); + ATF_REQUIRE(chdir("root") != -1); + RE(atf_fs_getcwd(&cwd2)); + + RE(atf_fs_path_append_fmt(&cwd1, "root")); + + ATF_REQUIRE(atf_equal_fs_path_fs_path(&cwd1, &cwd2)); + + atf_fs_path_fini(&cwd2); + atf_fs_path_fini(&cwd1); +} + +ATF_TC(rmdir_empty); +ATF_TC_HEAD(rmdir_empty, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_rmdir function"); +} +ATF_TC_BODY(rmdir_empty, tc) +{ + atf_fs_path_t p; + + RE(atf_fs_path_init_fmt(&p, "test-dir")); + + ATF_REQUIRE(mkdir("test-dir", 0755) != -1); + ATF_REQUIRE(exists(&p)); + RE(atf_fs_rmdir(&p)); + ATF_REQUIRE(!exists(&p)); + + atf_fs_path_fini(&p); +} + +ATF_TC(rmdir_enotempty); +ATF_TC_HEAD(rmdir_enotempty, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_rmdir function"); +} +ATF_TC_BODY(rmdir_enotempty, tc) +{ + atf_fs_path_t p; + atf_error_t err; + + RE(atf_fs_path_init_fmt(&p, "test-dir")); + + ATF_REQUIRE(mkdir("test-dir", 0755) != -1); + ATF_REQUIRE(exists(&p)); + create_file("test-dir/foo", 0644); + + err = atf_fs_rmdir(&p); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_REQUIRE_EQ(atf_libc_error_code(err), ENOTEMPTY); + atf_error_free(err); + + atf_fs_path_fini(&p); +} + +ATF_TC(rmdir_eperm); +ATF_TC_HEAD(rmdir_eperm, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_rmdir function"); +} +ATF_TC_BODY(rmdir_eperm, tc) +{ + atf_fs_path_t p; + atf_error_t err; + + RE(atf_fs_path_init_fmt(&p, "test-dir/foo")); + + ATF_REQUIRE(mkdir("test-dir", 0755) != -1); + ATF_REQUIRE(mkdir("test-dir/foo", 0755) != -1); + ATF_REQUIRE(chmod("test-dir", 0555) != -1); + ATF_REQUIRE(exists(&p)); + + err = atf_fs_rmdir(&p); + if (atf_user_is_root()) { + ATF_REQUIRE(!atf_is_error(err)); + } else { + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_REQUIRE_EQ(atf_libc_error_code(err), EACCES); + atf_error_free(err); + } + + atf_fs_path_fini(&p); +} + +ATF_TC(mkdtemp_ok); +ATF_TC_HEAD(mkdtemp_ok, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkdtemp function, " + "successful execution"); +} +ATF_TC_BODY(mkdtemp_ok, tc) +{ + atf_fs_path_t p1, p2; + atf_fs_stat_t s1, s2; + + RE(atf_fs_path_init_fmt(&p1, "testdir.XXXXXX")); + RE(atf_fs_path_init_fmt(&p2, "testdir.XXXXXX")); + RE(atf_fs_mkdtemp(&p1)); + RE(atf_fs_mkdtemp(&p2)); + ATF_REQUIRE(!atf_equal_fs_path_fs_path(&p1, &p2)); + ATF_REQUIRE(exists(&p1)); + ATF_REQUIRE(exists(&p2)); + + RE(atf_fs_stat_init(&s1, &p1)); + ATF_REQUIRE_EQ(atf_fs_stat_get_type(&s1), atf_fs_stat_dir_type); + ATF_REQUIRE( atf_fs_stat_is_owner_readable(&s1)); + ATF_REQUIRE( atf_fs_stat_is_owner_writable(&s1)); + ATF_REQUIRE( atf_fs_stat_is_owner_executable(&s1)); + ATF_REQUIRE(!atf_fs_stat_is_group_readable(&s1)); + ATF_REQUIRE(!atf_fs_stat_is_group_writable(&s1)); + ATF_REQUIRE(!atf_fs_stat_is_group_executable(&s1)); + ATF_REQUIRE(!atf_fs_stat_is_other_readable(&s1)); + ATF_REQUIRE(!atf_fs_stat_is_other_writable(&s1)); + ATF_REQUIRE(!atf_fs_stat_is_other_executable(&s1)); + + RE(atf_fs_stat_init(&s2, &p2)); + ATF_REQUIRE_EQ(atf_fs_stat_get_type(&s2), atf_fs_stat_dir_type); + ATF_REQUIRE( atf_fs_stat_is_owner_readable(&s2)); + ATF_REQUIRE( atf_fs_stat_is_owner_writable(&s2)); + ATF_REQUIRE( atf_fs_stat_is_owner_executable(&s2)); + ATF_REQUIRE(!atf_fs_stat_is_group_readable(&s2)); + ATF_REQUIRE(!atf_fs_stat_is_group_writable(&s2)); + ATF_REQUIRE(!atf_fs_stat_is_group_executable(&s2)); + ATF_REQUIRE(!atf_fs_stat_is_other_readable(&s2)); + ATF_REQUIRE(!atf_fs_stat_is_other_writable(&s2)); + ATF_REQUIRE(!atf_fs_stat_is_other_executable(&s2)); + + atf_fs_stat_fini(&s2); + atf_fs_stat_fini(&s1); + atf_fs_path_fini(&p2); + atf_fs_path_fini(&p1); +} + +ATF_TC(mkdtemp_err); +ATF_TC_HEAD(mkdtemp_err, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkdtemp function, " + "error conditions"); + atf_tc_set_md_var(tc, "require.user", "unprivileged"); +} +ATF_TC_BODY(mkdtemp_err, tc) +{ + atf_error_t err; + atf_fs_path_t p; + + ATF_REQUIRE(mkdir("dir", 0555) != -1); + + RE(atf_fs_path_init_fmt(&p, "dir/testdir.XXXXXX")); + + err = atf_fs_mkdtemp(&p); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_CHECK_EQ(atf_libc_error_code(err), EACCES); + atf_error_free(err); + + ATF_CHECK(!exists(&p)); + ATF_CHECK(strcmp(atf_fs_path_cstring(&p), "dir/testdir.XXXXXX") == 0); + + atf_fs_path_fini(&p); +} + +static +void +do_umask_check(atf_error_t (*const mk_func)(atf_fs_path_t *), + atf_fs_path_t *path, const mode_t test_mask, + const char *str_mask, const char *exp_name) +{ + char buf[1024]; + int old_umask; + atf_error_t err; + + printf("Creating temporary %s with umask %s\n", exp_name, str_mask); + + old_umask = umask(test_mask); + err = mk_func(path); + (void)umask(old_umask); + + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "invalid_umask")); + atf_error_format(err, buf, sizeof(buf)); + ATF_CHECK(strstr(buf, exp_name) != NULL); + ATF_CHECK(strstr(buf, str_mask) != NULL); + atf_error_free(err); +} + +ATF_TC(mkdtemp_umask); +ATF_TC_HEAD(mkdtemp_umask, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkdtemp function " + "causing an error due to a too strict umask"); +} +ATF_TC_BODY(mkdtemp_umask, tc) +{ + atf_fs_path_t p; + + RE(atf_fs_path_init_fmt(&p, "testdir.XXXXXX")); + + do_umask_check(atf_fs_mkdtemp, &p, 00100, "00100", "directory"); + do_umask_check(atf_fs_mkdtemp, &p, 00200, "00200", "directory"); + do_umask_check(atf_fs_mkdtemp, &p, 00400, "00400", "directory"); + do_umask_check(atf_fs_mkdtemp, &p, 00500, "00500", "directory"); + do_umask_check(atf_fs_mkdtemp, &p, 00600, "00600", "directory"); + + atf_fs_path_fini(&p); +} + +ATF_TC(mkstemp_ok); +ATF_TC_HEAD(mkstemp_ok, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkstemp function, " + "successful execution"); +} +ATF_TC_BODY(mkstemp_ok, tc) +{ + int fd1, fd2; + atf_fs_path_t p1, p2; + atf_fs_stat_t s1, s2; + + RE(atf_fs_path_init_fmt(&p1, "testfile.XXXXXX")); + RE(atf_fs_path_init_fmt(&p2, "testfile.XXXXXX")); + fd1 = fd2 = -1; + RE(atf_fs_mkstemp(&p1, &fd1)); + RE(atf_fs_mkstemp(&p2, &fd2)); + ATF_REQUIRE(!atf_equal_fs_path_fs_path(&p1, &p2)); + ATF_REQUIRE(exists(&p1)); + ATF_REQUIRE(exists(&p2)); + + ATF_CHECK(fd1 != -1); + ATF_CHECK(fd2 != -1); + ATF_CHECK(write(fd1, "foo", 3) == 3); + ATF_CHECK(write(fd2, "bar", 3) == 3); + close(fd1); + close(fd2); + + RE(atf_fs_stat_init(&s1, &p1)); + ATF_CHECK_EQ(atf_fs_stat_get_type(&s1), atf_fs_stat_reg_type); + ATF_CHECK( atf_fs_stat_is_owner_readable(&s1)); + ATF_CHECK( atf_fs_stat_is_owner_writable(&s1)); + ATF_CHECK(!atf_fs_stat_is_owner_executable(&s1)); + ATF_CHECK(!atf_fs_stat_is_group_readable(&s1)); + ATF_CHECK(!atf_fs_stat_is_group_writable(&s1)); + ATF_CHECK(!atf_fs_stat_is_group_executable(&s1)); + ATF_CHECK(!atf_fs_stat_is_other_readable(&s1)); + ATF_CHECK(!atf_fs_stat_is_other_writable(&s1)); + ATF_CHECK(!atf_fs_stat_is_other_executable(&s1)); + + RE(atf_fs_stat_init(&s2, &p2)); + ATF_CHECK_EQ(atf_fs_stat_get_type(&s2), atf_fs_stat_reg_type); + ATF_CHECK( atf_fs_stat_is_owner_readable(&s2)); + ATF_CHECK( atf_fs_stat_is_owner_writable(&s2)); + ATF_CHECK(!atf_fs_stat_is_owner_executable(&s2)); + ATF_CHECK(!atf_fs_stat_is_group_readable(&s2)); + ATF_CHECK(!atf_fs_stat_is_group_writable(&s2)); + ATF_CHECK(!atf_fs_stat_is_group_executable(&s2)); + ATF_CHECK(!atf_fs_stat_is_other_readable(&s2)); + ATF_CHECK(!atf_fs_stat_is_other_writable(&s2)); + ATF_CHECK(!atf_fs_stat_is_other_executable(&s2)); + + atf_fs_stat_fini(&s2); + atf_fs_stat_fini(&s1); + atf_fs_path_fini(&p2); + atf_fs_path_fini(&p1); +} + +ATF_TC(mkstemp_err); +ATF_TC_HEAD(mkstemp_err, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkstemp function, " + "error conditions"); + atf_tc_set_md_var(tc, "require.user", "unprivileged"); +} +ATF_TC_BODY(mkstemp_err, tc) +{ + int fd; + atf_error_t err; + atf_fs_path_t p; + + ATF_REQUIRE(mkdir("dir", 0555) != -1); + + RE(atf_fs_path_init_fmt(&p, "dir/testfile.XXXXXX")); + fd = 1234; + + err = atf_fs_mkstemp(&p, &fd); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_CHECK_EQ(atf_libc_error_code(err), EACCES); + atf_error_free(err); + + ATF_CHECK(!exists(&p)); + ATF_CHECK(strcmp(atf_fs_path_cstring(&p), "dir/testfile.XXXXXX") == 0); + ATF_CHECK_EQ(fd, 1234); + + atf_fs_path_fini(&p); +} + +ATF_TC(mkstemp_umask); +ATF_TC_HEAD(mkstemp_umask, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_fs_mkstemp function " + "causing an error due to a too strict umask"); +} +ATF_TC_BODY(mkstemp_umask, tc) +{ + atf_fs_path_t p; + + RE(atf_fs_path_init_fmt(&p, "testfile.XXXXXX")); + + do_umask_check(mkstemp_discard_fd, &p, 00100, "00100", "regular file"); + do_umask_check(mkstemp_discard_fd, &p, 00200, "00200", "regular file"); + do_umask_check(mkstemp_discard_fd, &p, 00400, "00400", "regular file"); + + atf_fs_path_fini(&p); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Add the tests for the "atf_fs_path" type. */ + ATF_TP_ADD_TC(tp, path_normalize); + ATF_TP_ADD_TC(tp, path_copy); + ATF_TP_ADD_TC(tp, path_is_absolute); + ATF_TP_ADD_TC(tp, path_is_root); + ATF_TP_ADD_TC(tp, path_branch_path); + ATF_TP_ADD_TC(tp, path_leaf_name); + ATF_TP_ADD_TC(tp, path_append); + ATF_TP_ADD_TC(tp, path_to_absolute); + ATF_TP_ADD_TC(tp, path_equal); + + /* Add the tests for the "atf_fs_stat" type. */ + ATF_TP_ADD_TC(tp, stat_mode); + ATF_TP_ADD_TC(tp, stat_type); + ATF_TP_ADD_TC(tp, stat_perms); + + /* Add the tests for the free functions. */ + ATF_TP_ADD_TC(tp, eaccess); + ATF_TP_ADD_TC(tp, exists); + ATF_TP_ADD_TC(tp, getcwd); + ATF_TP_ADD_TC(tp, rmdir_empty); + ATF_TP_ADD_TC(tp, rmdir_enotempty); + ATF_TP_ADD_TC(tp, rmdir_eperm); + ATF_TP_ADD_TC(tp, mkdtemp_ok); + ATF_TP_ADD_TC(tp, mkdtemp_err); + ATF_TP_ADD_TC(tp, mkdtemp_umask); + ATF_TP_ADD_TC(tp, mkstemp_ok); + ATF_TP_ADD_TC(tp, mkstemp_err); + ATF_TP_ADD_TC(tp, mkstemp_umask); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/list.c b/unit/atf-src/atf-c/detail/list.c new file mode 100644 index 0000000..d14216e --- /dev/null +++ b/unit/atf-src/atf-c/detail/list.c @@ -0,0 +1,388 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/list.h" + +#include <stdlib.h> +#include <string.h> + +#include "atf-c/detail/sanity.h" +#include "atf-c/error.h" +#include "atf-c/utils.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +struct list_entry { + struct list_entry *m_prev; + struct list_entry *m_next; + void *m_object; + bool m_managed; +}; + +static +atf_list_citer_t +entry_to_citer(const atf_list_t *l, const struct list_entry *le) +{ + atf_list_citer_t iter; + iter.m_list = l; + iter.m_entry = le; + return iter; +} + +static +atf_list_iter_t +entry_to_iter(atf_list_t *l, struct list_entry *le) +{ + atf_list_iter_t iter; + iter.m_list = l; + iter.m_entry = le; + return iter; +} + +static +struct list_entry * +new_entry(void *object, bool managed) +{ + struct list_entry *le; + + le = (struct list_entry *)malloc(sizeof(*le)); + if (le != NULL) { + le->m_prev = le->m_next = NULL; + le->m_object = object; + le->m_managed = managed; + } else + free(object); + + return le; +} + +static +void +delete_entry(struct list_entry *le) +{ + if (le->m_managed) + free(le->m_object); + + free(le); +} + +static +struct list_entry * +new_entry_and_link(void *object, bool managed, struct list_entry *prev, + struct list_entry *next) +{ + struct list_entry *le; + + le = new_entry(object, managed); + if (le != NULL) { + le->m_prev = prev; + le->m_next = next; + + prev->m_next = le; + next->m_prev = le; + } + + return le; +} + +/* --------------------------------------------------------------------- + * The "atf_list_citer" type. + * --------------------------------------------------------------------- */ + +/* + * Getters. + */ + +const void * +atf_list_citer_data(const atf_list_citer_t citer) +{ + const struct list_entry *le = citer.m_entry; + PRE(le != NULL); + return le->m_object; +} + +atf_list_citer_t +atf_list_citer_next(const atf_list_citer_t citer) +{ + const struct list_entry *le = citer.m_entry; + atf_list_citer_t newciter; + + PRE(le != NULL); + + newciter = citer; + newciter.m_entry = le->m_next; + + return newciter; +} + +bool +atf_equal_list_citer_list_citer(const atf_list_citer_t i1, + const atf_list_citer_t i2) +{ + return i1.m_list == i2.m_list && i1.m_entry == i2.m_entry; +} + +/* --------------------------------------------------------------------- + * The "atf_list_iter" type. + * --------------------------------------------------------------------- */ + +/* + * Getters. + */ + +void * +atf_list_iter_data(const atf_list_iter_t iter) +{ + const struct list_entry *le = iter.m_entry; + PRE(le != NULL); + return le->m_object; +} + +atf_list_iter_t +atf_list_iter_next(const atf_list_iter_t iter) +{ + const struct list_entry *le = iter.m_entry; + atf_list_iter_t newiter; + + PRE(le != NULL); + + newiter = iter; + newiter.m_entry = le->m_next; + + return newiter; +} + +bool +atf_equal_list_iter_list_iter(const atf_list_iter_t i1, + const atf_list_iter_t i2) +{ + return i1.m_list == i2.m_list && i1.m_entry == i2.m_entry; +} + +/* --------------------------------------------------------------------- + * The "atf_list" type. + * --------------------------------------------------------------------- */ + +/* + * Constructors and destructors. + */ + +atf_error_t +atf_list_init(atf_list_t *l) +{ + struct list_entry *lebeg, *leend; + + lebeg = new_entry(NULL, false); + if (lebeg == NULL) { + return atf_no_memory_error(); + } + + leend = new_entry(NULL, false); + if (leend == NULL) { + free(lebeg); + return atf_no_memory_error(); + } + + lebeg->m_next = leend; + lebeg->m_prev = NULL; + + leend->m_next = NULL; + leend->m_prev = lebeg; + + l->m_size = 0; + l->m_begin = lebeg; + l->m_end = leend; + + return atf_no_error(); +} + +void +atf_list_fini(atf_list_t *l) +{ + struct list_entry *le; + size_t freed; + + le = (struct list_entry *)l->m_begin; + freed = 0; + while (le != NULL) { + struct list_entry *lenext; + + lenext = le->m_next; + delete_entry(le); + le = lenext; + + freed++; + } + INV(freed == l->m_size + 2); +} + +/* + * Getters. + */ + +atf_list_iter_t +atf_list_begin(atf_list_t *l) +{ + struct list_entry *le = l->m_begin; + return entry_to_iter(l, le->m_next); +} + +atf_list_citer_t +atf_list_begin_c(const atf_list_t *l) +{ + const struct list_entry *le = l->m_begin; + return entry_to_citer(l, le->m_next); +} + +atf_list_iter_t +atf_list_end(atf_list_t *l) +{ + return entry_to_iter(l, l->m_end); +} + +atf_list_citer_t +atf_list_end_c(const atf_list_t *l) +{ + return entry_to_citer(l, l->m_end); +} + +void * +atf_list_index(atf_list_t *list, const size_t idx) +{ + atf_list_iter_t iter; + + PRE(idx < atf_list_size(list)); + + iter = atf_list_begin(list); + { + size_t pos = 0; + while (pos < idx && + !atf_equal_list_iter_list_iter((iter), atf_list_end(list))) { + iter = atf_list_iter_next(iter); + pos++; + } + } + return atf_list_iter_data(iter); +} + +const void * +atf_list_index_c(const atf_list_t *list, const size_t idx) +{ + atf_list_citer_t iter; + + PRE(idx < atf_list_size(list)); + + iter = atf_list_begin_c(list); + { + size_t pos = 0; + while (pos < idx && + !atf_equal_list_citer_list_citer((iter), + atf_list_end_c(list))) { + iter = atf_list_citer_next(iter); + pos++; + } + } + return atf_list_citer_data(iter); +} + +size_t +atf_list_size(const atf_list_t *l) +{ + return l->m_size; +} + +char ** +atf_list_to_charpp(const atf_list_t *l) +{ + char **array; + atf_list_citer_t iter; + size_t i; + + array = malloc(sizeof(char *) * (atf_list_size(l) + 1)); + if (array == NULL) + goto out; + + i = 0; + atf_list_for_each_c(iter, l) { + array[i] = strdup((const char *)atf_list_citer_data(iter)); + if (array[i] == NULL) { + atf_utils_free_charpp(array); + array = NULL; + goto out; + } + + i++; + } + array[i] = NULL; + +out: + return array; +} + +/* + * Modifiers. + */ + +atf_error_t +atf_list_append(atf_list_t *l, void *data, bool managed) +{ + struct list_entry *le, *next, *prev; + atf_error_t err; + + next = (struct list_entry *)l->m_end; + prev = next->m_prev; + le = new_entry_and_link(data, managed, prev, next); + if (le == NULL) + err = atf_no_memory_error(); + else { + l->m_size++; + err = atf_no_error(); + } + + return err; +} + +void +atf_list_append_list(atf_list_t *l, atf_list_t *src) +{ + struct list_entry *e1, *e2, *ghost1, *ghost2; + + ghost1 = (struct list_entry *)l->m_end; + ghost2 = (struct list_entry *)src->m_begin; + + e1 = ghost1->m_prev; + e2 = ghost2->m_next; + + delete_entry(ghost1); + delete_entry(ghost2); + + e1->m_next = e2; + e2->m_prev = e1; + + l->m_end = src->m_end; + l->m_size += src->m_size; +} diff --git a/unit/atf-src/atf-c/detail/list.h b/unit/atf-src/atf-c/detail/list.h new file mode 100644 index 0000000..b250066 --- /dev/null +++ b/unit/atf-src/atf-c/detail/list.h @@ -0,0 +1,111 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_LIST_H) +#define ATF_C_DETAIL_LIST_H + +#include <stdarg.h> +#include <stdbool.h> +#include <stddef.h> + +#include <atf-c/error_fwd.h> + +/* --------------------------------------------------------------------- + * The "atf_list_citer" type. + * --------------------------------------------------------------------- */ + +struct atf_list_citer { + const struct atf_list *m_list; + const void *m_entry; +}; +typedef struct atf_list_citer atf_list_citer_t; + +/* Getters. */ +const void *atf_list_citer_data(const atf_list_citer_t); +atf_list_citer_t atf_list_citer_next(const atf_list_citer_t); + +/* Operators. */ +bool atf_equal_list_citer_list_citer(const atf_list_citer_t, + const atf_list_citer_t); + +/* --------------------------------------------------------------------- + * The "atf_list_iter" type. + * --------------------------------------------------------------------- */ + +struct atf_list_iter { + struct atf_list *m_list; + void *m_entry; +}; +typedef struct atf_list_iter atf_list_iter_t; + +/* Getters. */ +void *atf_list_iter_data(const atf_list_iter_t); +atf_list_iter_t atf_list_iter_next(const atf_list_iter_t); + +/* Operators. */ +bool atf_equal_list_iter_list_iter(const atf_list_iter_t, + const atf_list_iter_t); + +/* --------------------------------------------------------------------- + * The "atf_list" type. + * --------------------------------------------------------------------- */ + +struct atf_list { + void *m_begin; + void *m_end; + + size_t m_size; +}; +typedef struct atf_list atf_list_t; + +/* Constructors and destructors */ +atf_error_t atf_list_init(atf_list_t *); +void atf_list_fini(atf_list_t *); + +/* Getters. */ +atf_list_iter_t atf_list_begin(atf_list_t *); +atf_list_citer_t atf_list_begin_c(const atf_list_t *); +atf_list_iter_t atf_list_end(atf_list_t *); +atf_list_citer_t atf_list_end_c(const atf_list_t *); +void *atf_list_index(atf_list_t *, const size_t); +const void *atf_list_index_c(const atf_list_t *, const size_t); +size_t atf_list_size(const atf_list_t *); +char **atf_list_to_charpp(const atf_list_t *); + +/* Modifiers. */ +atf_error_t atf_list_append(atf_list_t *, void *, bool); +void atf_list_append_list(atf_list_t *, atf_list_t *); + +/* Macros. */ +#define atf_list_for_each(iter, list) \ + for (iter = atf_list_begin(list); \ + !atf_equal_list_iter_list_iter((iter), atf_list_end(list)); \ + iter = atf_list_iter_next(iter)) +#define atf_list_for_each_c(iter, list) \ + for (iter = atf_list_begin_c(list); \ + !atf_equal_list_citer_list_citer((iter), atf_list_end_c(list)); \ + iter = atf_list_citer_next(iter)) + +#endif /* !defined(ATF_C_DETAIL_LIST_H) */ diff --git a/unit/atf-src/atf-c/detail/list_test.c b/unit/atf-src/atf-c/detail/list_test.c new file mode 100644 index 0000000..82a715d --- /dev/null +++ b/unit/atf-src/atf-c/detail/list_test.c @@ -0,0 +1,365 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/list.h" + +#include <stdio.h> +#include <string.h> + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" +#include "atf-c/utils.h" + +/* --------------------------------------------------------------------- + * Tests for the "atf_list" type. + * --------------------------------------------------------------------- */ + +/* + * Constructors and destructors. + */ + +ATF_TC(list_init); +ATF_TC_HEAD(list_init, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_init function"); +} +ATF_TC_BODY(list_init, tc) +{ + atf_list_t list; + + RE(atf_list_init(&list)); + ATF_REQUIRE_EQ(atf_list_size(&list), 0); + atf_list_fini(&list); +} + +/* + * Getters. + */ + +ATF_TC(list_index); +ATF_TC_HEAD(list_index, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_index function"); +} +ATF_TC_BODY(list_index, tc) +{ + atf_list_t list; + int i1 = 1; + int i2 = 5; + int i3 = 9; + + RE(atf_list_init(&list)); + RE(atf_list_append(&list, &i1, false)); + RE(atf_list_append(&list, &i2, false)); + RE(atf_list_append(&list, &i3, false)); + + ATF_CHECK_EQ(*(int *)atf_list_index(&list, 0), 1); + ATF_CHECK_EQ(*(int *)atf_list_index(&list, 1), 5); + ATF_CHECK_EQ(*(int *)atf_list_index(&list, 2), 9); + + atf_list_fini(&list); +} + +ATF_TC(list_index_c); +ATF_TC_HEAD(list_index_c, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_index_c function"); +} +ATF_TC_BODY(list_index_c, tc) +{ + atf_list_t list; + int i1 = 1; + int i2 = 5; + int i3 = 9; + + RE(atf_list_init(&list)); + RE(atf_list_append(&list, &i1, false)); + RE(atf_list_append(&list, &i2, false)); + RE(atf_list_append(&list, &i3, false)); + + ATF_CHECK_EQ(*(const int *)atf_list_index_c(&list, 0), 1); + ATF_CHECK_EQ(*(const int *)atf_list_index_c(&list, 1), 5); + ATF_CHECK_EQ(*(const int *)atf_list_index_c(&list, 2), 9); + + atf_list_fini(&list); +} + +ATF_TC_WITHOUT_HEAD(list_to_charpp_empty); +ATF_TC_BODY(list_to_charpp_empty, tc) +{ + atf_list_t list; + char **array; + + RE(atf_list_init(&list)); + ATF_REQUIRE((array = atf_list_to_charpp(&list)) != NULL); + atf_list_fini(&list); + + ATF_CHECK_EQ(NULL, array[0]); + atf_utils_free_charpp(array); +} + +ATF_TC_WITHOUT_HEAD(list_to_charpp_some); +ATF_TC_BODY(list_to_charpp_some, tc) +{ + atf_list_t list; + char **array; + + char s1[] = "one"; + char s2[] = "two"; + char s3[] = "three"; + + RE(atf_list_init(&list)); + RE(atf_list_append(&list, s1, false)); + RE(atf_list_append(&list, s2, false)); + RE(atf_list_append(&list, s3, false)); + ATF_REQUIRE((array = atf_list_to_charpp(&list)) != NULL); + atf_list_fini(&list); + + ATF_CHECK_STREQ("one", array[0]); + ATF_CHECK_STREQ("two", array[1]); + ATF_CHECK_STREQ("three", array[2]); + ATF_CHECK_EQ(NULL, array[3]); + atf_utils_free_charpp(array); +} + +/* + * Modifiers. + */ + +ATF_TC(list_append); +ATF_TC_HEAD(list_append, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_append function"); +} +ATF_TC_BODY(list_append, tc) +{ + atf_list_t list; + size_t i; + char buf[] = "Test string"; + + RE(atf_list_init(&list)); + for (i = 0; i < 1024; i++) { + ATF_REQUIRE_EQ(atf_list_size(&list), i); + RE(atf_list_append(&list, buf, false)); + } + atf_list_fini(&list); +} + +ATF_TC(list_append_list); +ATF_TC_HEAD(list_append_list, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_append_list " + "function"); +} +ATF_TC_BODY(list_append_list, tc) +{ + { + atf_list_t l1, l2; + + RE(atf_list_init(&l1)); + RE(atf_list_init(&l2)); + + atf_list_append_list(&l1, &l2); + ATF_CHECK_EQ(atf_list_size(&l1), 0); + + atf_list_fini(&l1); + } + + { + atf_list_t l1, l2; + int item = 5; + + RE(atf_list_init(&l1)); + RE(atf_list_append(&l1, &item, false)); + RE(atf_list_init(&l2)); + + atf_list_append_list(&l1, &l2); + ATF_CHECK_EQ(atf_list_size(&l1), 1); + ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 0), item); + + atf_list_fini(&l1); + } + + { + atf_list_t l1, l2; + int item = 5; + + RE(atf_list_init(&l1)); + RE(atf_list_init(&l2)); + RE(atf_list_append(&l2, &item, false)); + + atf_list_append_list(&l1, &l2); + ATF_CHECK_EQ(atf_list_size(&l1), 1); + ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 0), item); + + atf_list_fini(&l1); + } + + { + atf_list_t l1, l2; + int item1 = 5; + int item2 = 9; + + RE(atf_list_init(&l1)); + RE(atf_list_append(&l1, &item1, false)); + RE(atf_list_init(&l2)); + RE(atf_list_append(&l2, &item2, false)); + + atf_list_append_list(&l1, &l2); + ATF_CHECK_EQ(atf_list_size(&l1), 2); + ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 0), item1); + ATF_CHECK_EQ(*(int *)atf_list_index(&l1, 1), item2); + + atf_list_fini(&l1); + } + + { + atf_list_t l1, l2; + atf_list_citer_t end1, end2; + + RE(atf_list_init(&l1)); + RE(atf_list_init(&l2)); + + end1 = atf_list_end_c(&l1); + end2 = atf_list_end_c(&l2); + /* XXX Shouldn't query m_entry here. */ + ATF_CHECK(end1.m_entry != end2.m_entry); + + atf_list_append_list(&l1, &l2); + ATF_CHECK(atf_list_end_c(&l1).m_entry == end2.m_entry); + + atf_list_fini(&l1); + } +} + +/* + * Macros. + */ + +ATF_TC(list_for_each); +ATF_TC_HEAD(list_for_each, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_for_each macro"); +} +ATF_TC_BODY(list_for_each, tc) +{ + atf_list_t list; + atf_list_iter_t iter; + size_t count, i, size; + int nums[10]; + + printf("Iterating over empty list\n"); + RE(atf_list_init(&list)); + count = 0; + atf_list_for_each(iter, &list) { + count++; + printf("Item count is now %zd\n", count); + } + ATF_REQUIRE_EQ(count, 0); + atf_list_fini(&list); + + for (size = 0; size <= 10; size++) { + printf("Iterating over list of %zd elements\n", size); + RE(atf_list_init(&list)); + for (i = 0; i < size; i++) { + nums[i] = i + 1; + RE(atf_list_append(&list, &nums[i], false)); + } + count = 0; + atf_list_for_each(iter, &list) { + printf("Retrieved item: %d\n", *(int *)atf_list_iter_data(iter)); + count++; + } + ATF_REQUIRE_EQ(count, size); + atf_list_fini(&list); + } +} + +ATF_TC(list_for_each_c); +ATF_TC_HEAD(list_for_each_c, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_list_for_each_c macro"); +} +ATF_TC_BODY(list_for_each_c, tc) +{ + atf_list_t list; + atf_list_citer_t iter; + size_t count, i, size; + int nums[10]; + + printf("Iterating over empty list\n"); + RE(atf_list_init(&list)); + count = 0; + atf_list_for_each_c(iter, &list) { + count++; + printf("Item count is now %zd\n", count); + } + ATF_REQUIRE_EQ(count, 0); + atf_list_fini(&list); + + for (size = 0; size <= 10; size++) { + printf("Iterating over list of %zd elements\n", size); + RE(atf_list_init(&list)); + for (i = 0; i < size; i++) { + nums[i] = i + 1; + RE(atf_list_append(&list, &nums[i], false)); + } + count = 0; + atf_list_for_each_c(iter, &list) { + printf("Retrieved item: %d\n", + *(const int *)atf_list_citer_data(iter)); + count++; + } + ATF_REQUIRE_EQ(count, size); + atf_list_fini(&list); + } +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Constructors and destructors. */ + ATF_TP_ADD_TC(tp, list_init); + + /* Getters. */ + ATF_TP_ADD_TC(tp, list_index); + ATF_TP_ADD_TC(tp, list_index_c); + ATF_TP_ADD_TC(tp, list_to_charpp_empty); + ATF_TP_ADD_TC(tp, list_to_charpp_some); + + /* Modifiers. */ + ATF_TP_ADD_TC(tp, list_append); + ATF_TP_ADD_TC(tp, list_append_list); + + /* Macros. */ + ATF_TP_ADD_TC(tp, list_for_each); + ATF_TP_ADD_TC(tp, list_for_each_c); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/map.c b/unit/atf-src/atf-c/detail/map.c new file mode 100644 index 0000000..45de47f --- /dev/null +++ b/unit/atf-src/atf-c/detail/map.c @@ -0,0 +1,380 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/map.h" + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "atf-c/detail/sanity.h" +#include "atf-c/error.h" +#include "atf-c/utils.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +struct map_entry { + char *m_key; + void *m_value; + bool m_managed; +}; + +static +struct map_entry * +new_entry(const char *key, void *value, bool managed) +{ + struct map_entry *me; + + me = (struct map_entry *)malloc(sizeof(*me)); + if (me != NULL) { + me->m_key = strdup(key); + if (me->m_key == NULL) { + free(me); + me = NULL; + } else { + me->m_value = value; + me->m_managed = managed; + } + } + + return me; +} + +/* --------------------------------------------------------------------- + * The "atf_map_citer" type. + * --------------------------------------------------------------------- */ + +/* + * Getters. + */ + +const char * +atf_map_citer_key(const atf_map_citer_t citer) +{ + const struct map_entry *me = citer.m_entry; + PRE(me != NULL); + return me->m_key; +} + +const void * +atf_map_citer_data(const atf_map_citer_t citer) +{ + const struct map_entry *me = citer.m_entry; + PRE(me != NULL); + return me->m_value; +} + +atf_map_citer_t +atf_map_citer_next(const atf_map_citer_t citer) +{ + atf_map_citer_t newciter; + + newciter = citer; + newciter.m_listiter = atf_list_citer_next(citer.m_listiter); + newciter.m_entry = ((const struct map_entry *) + atf_list_citer_data(newciter.m_listiter)); + + return newciter; +} + +bool +atf_equal_map_citer_map_citer(const atf_map_citer_t i1, + const atf_map_citer_t i2) +{ + return i1.m_map == i2.m_map && i1.m_entry == i2.m_entry; +} + +/* --------------------------------------------------------------------- + * The "atf_map_iter" type. + * --------------------------------------------------------------------- */ + +/* + * Getters. + */ + +const char * +atf_map_iter_key(const atf_map_iter_t iter) +{ + const struct map_entry *me = iter.m_entry; + PRE(me != NULL); + return me->m_key; +} + +void * +atf_map_iter_data(const atf_map_iter_t iter) +{ + const struct map_entry *me = iter.m_entry; + PRE(me != NULL); + return me->m_value; +} + +atf_map_iter_t +atf_map_iter_next(const atf_map_iter_t iter) +{ + atf_map_iter_t newiter; + + newiter = iter; + newiter.m_listiter = atf_list_iter_next(iter.m_listiter); + newiter.m_entry = ((struct map_entry *) + atf_list_iter_data(newiter.m_listiter)); + + return newiter; +} + +bool +atf_equal_map_iter_map_iter(const atf_map_iter_t i1, + const atf_map_iter_t i2) +{ + return i1.m_map == i2.m_map && i1.m_entry == i2.m_entry; +} + +/* --------------------------------------------------------------------- + * The "atf_map" type. + * --------------------------------------------------------------------- */ + +/* + * Constructors and destructors. + */ + +atf_error_t +atf_map_init(atf_map_t *m) +{ + return atf_list_init(&m->m_list); +} + +atf_error_t +atf_map_init_charpp(atf_map_t *m, const char *const *array) +{ + atf_error_t err; + const char *const *ptr = array; + + err = atf_map_init(m); + if (array != NULL) { + while (!atf_is_error(err) && *ptr != NULL) { + const char *key, *value; + + key = *ptr; + INV(key != NULL); + ptr++; + + if ((value = *ptr) == NULL) { + err = atf_libc_error(EINVAL, "List too short; no value for " + "key '%s' provided", key); /* XXX: Not really libc_error */ + break; + } + ptr++; + + err = atf_map_insert(m, key, strdup(value), true); + } + } + + if (atf_is_error(err)) + atf_map_fini(m); + + return err; +} + +void +atf_map_fini(atf_map_t *m) +{ + atf_list_iter_t iter; + + atf_list_for_each(iter, &m->m_list) { + struct map_entry *me = atf_list_iter_data(iter); + + if (me->m_managed) + free(me->m_value); + free(me->m_key); + free(me); + } + atf_list_fini(&m->m_list); +} + +/* + * Getters. + */ + +atf_map_iter_t +atf_map_begin(atf_map_t *m) +{ + atf_map_iter_t iter; + iter.m_map = m; + iter.m_listiter = atf_list_begin(&m->m_list); + iter.m_entry = atf_list_iter_data(iter.m_listiter); + return iter; +} + +atf_map_citer_t +atf_map_begin_c(const atf_map_t *m) +{ + atf_map_citer_t citer; + citer.m_map = m; + citer.m_listiter = atf_list_begin_c(&m->m_list); + citer.m_entry = atf_list_citer_data(citer.m_listiter); + return citer; +} + +atf_map_iter_t +atf_map_end(atf_map_t *m) +{ + atf_map_iter_t iter; + iter.m_map = m; + iter.m_entry = NULL; + iter.m_listiter = atf_list_end(&m->m_list); + return iter; +} + +atf_map_citer_t +atf_map_end_c(const atf_map_t *m) +{ + atf_map_citer_t iter; + iter.m_map = m; + iter.m_entry = NULL; + iter.m_listiter = atf_list_end_c(&m->m_list); + return iter; +} + +atf_map_iter_t +atf_map_find(atf_map_t *m, const char *key) +{ + atf_list_iter_t iter; + + atf_list_for_each(iter, &m->m_list) { + struct map_entry *me = atf_list_iter_data(iter); + + if (strcmp(me->m_key, key) == 0) { + atf_map_iter_t i; + i.m_map = m; + i.m_entry = me; + i.m_listiter = iter; + return i; + } + } + + return atf_map_end(m); +} + +atf_map_citer_t +atf_map_find_c(const atf_map_t *m, const char *key) +{ + atf_list_citer_t iter; + + atf_list_for_each_c(iter, &m->m_list) { + const struct map_entry *me = atf_list_citer_data(iter); + + if (strcmp(me->m_key, key) == 0) { + atf_map_citer_t i; + i.m_map = m; + i.m_entry = me; + i.m_listiter = iter; + return i; + } + } + + return atf_map_end_c(m); +} + +size_t +atf_map_size(const atf_map_t *m) +{ + return atf_list_size(&m->m_list); +} + +char ** +atf_map_to_charpp(const atf_map_t *l) +{ + char **array; + atf_map_citer_t iter; + size_t i; + + array = malloc(sizeof(char *) * (atf_map_size(l) * 2 + 1)); + if (array == NULL) + goto out; + + i = 0; + atf_map_for_each_c(iter, l) { + array[i] = strdup(atf_map_citer_key(iter)); + if (array[i] == NULL) { + atf_utils_free_charpp(array); + array = NULL; + goto out; + } + + array[i + 1] = strdup((const char *)atf_map_citer_data(iter)); + if (array[i + 1] == NULL) { + atf_utils_free_charpp(array); + array = NULL; + goto out; + } + + i += 2; + } + array[i] = NULL; + +out: + return array; +} + +/* + * Modifiers. + */ + +atf_error_t +atf_map_insert(atf_map_t *m, const char *key, void *value, bool managed) +{ + struct map_entry *me; + atf_error_t err; + atf_map_iter_t iter; + + iter = atf_map_find(m, key); + if (atf_equal_map_iter_map_iter(iter, atf_map_end(m))) { + me = new_entry(key, value, managed); + if (me == NULL) + err = atf_no_memory_error(); + else { + err = atf_list_append(&m->m_list, me, false); + if (atf_is_error(err)) { + if (managed) + free(value); + free(me->m_key); + free(me); + } + } + } else { + me = iter.m_entry; + if (me->m_managed) + free(me->m_value); + + INV(strcmp(me->m_key, key) == 0); + me->m_value = value; + me->m_managed = managed; + + err = atf_no_error(); + } + + return err; +} diff --git a/unit/atf-src/atf-c/detail/map.h b/unit/atf-src/atf-c/detail/map.h new file mode 100644 index 0000000..7ca8e8a --- /dev/null +++ b/unit/atf-src/atf-c/detail/map.h @@ -0,0 +1,114 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_MAP_H) +#define ATF_C_DETAIL_MAP_H + +#include <stdarg.h> +#include <stdbool.h> + +#include <atf-c/detail/list.h> +#include <atf-c/error_fwd.h> + +/* --------------------------------------------------------------------- + * The "atf_map_citer" type. + * --------------------------------------------------------------------- */ + +struct atf_map_citer { + const struct atf_map *m_map; + const void *m_entry; + atf_list_citer_t m_listiter; +}; +typedef struct atf_map_citer atf_map_citer_t; + +/* Getters. */ +const char *atf_map_citer_key(const atf_map_citer_t); +const void *atf_map_citer_data(const atf_map_citer_t); +atf_map_citer_t atf_map_citer_next(const atf_map_citer_t); + +/* Operators. */ +bool atf_equal_map_citer_map_citer(const atf_map_citer_t, + const atf_map_citer_t); + +/* --------------------------------------------------------------------- + * The "atf_map_iter" type. + * --------------------------------------------------------------------- */ + +struct atf_map_iter { + struct atf_map *m_map; + void *m_entry; + atf_list_iter_t m_listiter; +}; +typedef struct atf_map_iter atf_map_iter_t; + +/* Getters. */ +const char *atf_map_iter_key(const atf_map_iter_t); +void *atf_map_iter_data(const atf_map_iter_t); +atf_map_iter_t atf_map_iter_next(const atf_map_iter_t); + +/* Operators. */ +bool atf_equal_map_iter_map_iter(const atf_map_iter_t, + const atf_map_iter_t); + +/* --------------------------------------------------------------------- + * The "atf_map" type. + * --------------------------------------------------------------------- */ + +/* A list-based map. Typically very inefficient, but our maps are small + * enough. */ +struct atf_map { + atf_list_t m_list; +}; +typedef struct atf_map atf_map_t; + +/* Constructors and destructors */ +atf_error_t atf_map_init(atf_map_t *); +atf_error_t atf_map_init_charpp(atf_map_t *, const char *const *); +void atf_map_fini(atf_map_t *); + +/* Getters. */ +atf_map_iter_t atf_map_begin(atf_map_t *); +atf_map_citer_t atf_map_begin_c(const atf_map_t *); +atf_map_iter_t atf_map_end(atf_map_t *); +atf_map_citer_t atf_map_end_c(const atf_map_t *); +atf_map_iter_t atf_map_find(atf_map_t *, const char *); +atf_map_citer_t atf_map_find_c(const atf_map_t *, const char *); +size_t atf_map_size(const atf_map_t *); +char **atf_map_to_charpp(const atf_map_t *); + +/* Modifiers. */ +atf_error_t atf_map_insert(atf_map_t *, const char *, void *, bool); + +/* Macros. */ +#define atf_map_for_each(iter, map) \ + for (iter = atf_map_begin(map); \ + !atf_equal_map_iter_map_iter((iter), atf_map_end(map)); \ + iter = atf_map_iter_next(iter)) +#define atf_map_for_each_c(iter, map) \ + for (iter = atf_map_begin_c(map); \ + !atf_equal_map_citer_map_citer((iter), atf_map_end_c(map)); \ + iter = atf_map_citer_next(iter)) + +#endif /* !defined(ATF_C_DETAIL_MAP_H) */ diff --git a/unit/atf-src/atf-c/detail/map_test.c b/unit/atf-src/atf-c/detail/map_test.c new file mode 100644 index 0000000..0982e20 --- /dev/null +++ b/unit/atf-src/atf-c/detail/map_test.c @@ -0,0 +1,421 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/map.h" + +#include <stdio.h> +#include <string.h> + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" +#include "atf-c/utils.h" + +/* --------------------------------------------------------------------- + * Tests for the "atf_map" type. + * --------------------------------------------------------------------- */ + +/* + * Constructors and destructors. + */ + +ATF_TC(map_init); +ATF_TC_HEAD(map_init, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_map_init function"); +} +ATF_TC_BODY(map_init, tc) +{ + atf_map_t map; + + RE(atf_map_init(&map)); + ATF_REQUIRE_EQ(atf_map_size(&map), 0); + atf_map_fini(&map); +} + +ATF_TC_WITHOUT_HEAD(map_init_charpp_null); +ATF_TC_BODY(map_init_charpp_null, tc) +{ + atf_map_t map; + + RE(atf_map_init_charpp(&map, NULL)); + ATF_REQUIRE_EQ(atf_map_size(&map), 0); + atf_map_fini(&map); +} + +ATF_TC_WITHOUT_HEAD(map_init_charpp_empty); +ATF_TC_BODY(map_init_charpp_empty, tc) +{ + const char *const array[] = { NULL }; + atf_map_t map; + + RE(atf_map_init_charpp(&map, array)); + ATF_REQUIRE_EQ(atf_map_size(&map), 0); + atf_map_fini(&map); +} + +ATF_TC_WITHOUT_HEAD(map_init_charpp_some); +ATF_TC_BODY(map_init_charpp_some, tc) +{ + const char *const array[] = { "K1", "V1", "K2", "V2", NULL }; + atf_map_t map; + atf_map_citer_t iter; + + RE(atf_map_init_charpp(&map, array)); + ATF_REQUIRE_EQ(atf_map_size(&map), 2); + + iter = atf_map_find_c(&map, "K1"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K1") == 0); + ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V1") == 0); + + iter = atf_map_find_c(&map, "K2"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K2") == 0); + ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V2") == 0); + + atf_map_fini(&map); +} + +ATF_TC_WITHOUT_HEAD(map_init_charpp_short); +ATF_TC_BODY(map_init_charpp_short, tc) +{ + const char *const array[] = { "K1", "V1", "K2", NULL }; + atf_map_t map; + + atf_error_t err = atf_map_init_charpp(&map, array); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "libc")); +} + +/* + * Getters. + */ + +ATF_TC(find); +ATF_TC_HEAD(find, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_map_find function"); +} +ATF_TC_BODY(find, tc) +{ + atf_map_t map; + char val1[] = "V1"; + char val2[] = "V2"; + atf_map_iter_t iter; + + RE(atf_map_init(&map)); + RE(atf_map_insert(&map, "K1", val1, false)); + RE(atf_map_insert(&map, "K2", val2, false)); + + iter = atf_map_find(&map, "K0"); + ATF_REQUIRE(atf_equal_map_iter_map_iter(iter, atf_map_end(&map))); + + iter = atf_map_find(&map, "K1"); + ATF_REQUIRE(!atf_equal_map_iter_map_iter(iter, atf_map_end(&map))); + ATF_REQUIRE(strcmp(atf_map_iter_key(iter), "K1") == 0); + ATF_REQUIRE(strcmp(atf_map_iter_data(iter), "V1") == 0); + strcpy(atf_map_iter_data(iter), "Z1"); + + iter = atf_map_find(&map, "K1"); + ATF_REQUIRE(!atf_equal_map_iter_map_iter(iter, atf_map_end(&map))); + ATF_REQUIRE(strcmp(atf_map_iter_key(iter), "K1") == 0); + ATF_REQUIRE(strcmp(atf_map_iter_data(iter), "Z1") == 0); + + iter = atf_map_find(&map, "K2"); + ATF_REQUIRE(!atf_equal_map_iter_map_iter(iter, atf_map_end(&map))); + ATF_REQUIRE(strcmp(atf_map_iter_key(iter), "K2") == 0); + ATF_REQUIRE(strcmp(atf_map_iter_data(iter), "V2") == 0); + + atf_map_fini(&map); +} + +ATF_TC(find_c); +ATF_TC_HEAD(find_c, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_map_find_c function"); +} +ATF_TC_BODY(find_c, tc) +{ + atf_map_t map; + char val1[] = "V1"; + char val2[] = "V2"; + atf_map_citer_t iter; + + RE(atf_map_init(&map)); + RE(atf_map_insert(&map, "K1", val1, false)); + RE(atf_map_insert(&map, "K2", val2, false)); + + iter = atf_map_find_c(&map, "K0"); + ATF_REQUIRE(atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + + iter = atf_map_find_c(&map, "K1"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K1") == 0); + ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V1") == 0); + + iter = atf_map_find_c(&map, "K2"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + ATF_REQUIRE(strcmp(atf_map_citer_key(iter), "K2") == 0); + ATF_REQUIRE(strcmp(atf_map_citer_data(iter), "V2") == 0); + + atf_map_fini(&map); +} + +ATF_TC_WITHOUT_HEAD(to_charpp_empty); +ATF_TC_BODY(to_charpp_empty, tc) +{ + atf_map_t map; + char **array; + + RE(atf_map_init(&map)); + ATF_REQUIRE((array = atf_map_to_charpp(&map)) != NULL); + atf_map_fini(&map); + + ATF_CHECK_EQ(NULL, array[0]); + atf_utils_free_charpp(array); +} + +ATF_TC_WITHOUT_HEAD(to_charpp_some); +ATF_TC_BODY(to_charpp_some, tc) +{ + atf_map_t map; + char **array; + + char s1[] = "one"; + char s2[] = "two"; + char s3[] = "three"; + + RE(atf_map_init(&map)); + RE(atf_map_insert(&map, "K1", s1, false)); + RE(atf_map_insert(&map, "K2", s2, false)); + RE(atf_map_insert(&map, "K3", s3, false)); + ATF_REQUIRE((array = atf_map_to_charpp(&map)) != NULL); + atf_map_fini(&map); + + ATF_CHECK_STREQ("K1", array[0]); + ATF_CHECK_STREQ("one", array[1]); + ATF_CHECK_STREQ("K2", array[2]); + ATF_CHECK_STREQ("two", array[3]); + ATF_CHECK_STREQ("K3", array[4]); + ATF_CHECK_STREQ("three", array[5]); + ATF_CHECK_EQ(NULL, array[6]); + atf_utils_free_charpp(array); +} + +/* + * Modifiers. + */ + +ATF_TC(map_insert); +ATF_TC_HEAD(map_insert, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_map_insert function"); +} +ATF_TC_BODY(map_insert, tc) +{ + atf_map_t map; + char buf[] = "1st test string"; + char buf2[] = "2nd test string"; + const char *ptr; + atf_map_citer_t iter; + + RE(atf_map_init(&map)); + + printf("Inserting some values\n"); + ATF_REQUIRE_EQ(atf_map_size(&map), 0); + RE(atf_map_insert(&map, "K1", buf, false)); + ATF_REQUIRE_EQ(atf_map_size(&map), 1); + RE(atf_map_insert(&map, "K2", buf, false)); + ATF_REQUIRE_EQ(atf_map_size(&map), 2); + RE(atf_map_insert(&map, "K3", buf, false)); + ATF_REQUIRE_EQ(atf_map_size(&map), 3); + + printf("Replacing a value\n"); + iter = atf_map_find_c(&map, "K3"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + ptr = atf_map_citer_data(iter); + ATF_REQUIRE_EQ(ptr, buf); + RE(atf_map_insert(&map, "K3", buf2, false)); + ATF_REQUIRE_EQ(atf_map_size(&map), 3); + iter = atf_map_find_c(&map, "K3"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + ptr = atf_map_citer_data(iter); + ATF_REQUIRE_EQ(ptr, buf2); + + atf_map_fini(&map); +} + +/* + * Macros. + */ + +ATF_TC(map_for_each); +ATF_TC_HEAD(map_for_each, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_map_for_each macro"); +} +ATF_TC_BODY(map_for_each, tc) +{ + atf_map_t map; + atf_map_iter_t iter; + size_t count, i, size; + char keys[10][5]; + int nums[10]; + + printf("Iterating over empty map\n"); + RE(atf_map_init(&map)); + count = 0; + atf_map_for_each(iter, &map) { + count++; + printf("Item count is now %zd\n", count); + } + ATF_REQUIRE_EQ(count, 0); + atf_map_fini(&map); + + for (size = 0; size <= 10; size++) { + printf("Iterating over map of %zd elements\n", size); + RE(atf_map_init(&map)); + for (i = 0; i < size; i++) { + nums[i] = i + 1; + snprintf(keys[i], sizeof(keys[i]), "%d", nums[i]); + RE(atf_map_insert(&map, keys[i], &nums[i], false)); + } + count = 0; + atf_map_for_each(iter, &map) { + printf("Retrieved item: %d\n", *(int *)atf_map_iter_data(iter)); + count++; + } + ATF_REQUIRE_EQ(count, size); + atf_map_fini(&map); + } +} + +ATF_TC(map_for_each_c); +ATF_TC_HEAD(map_for_each_c, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_map_for_each_c macro"); +} +ATF_TC_BODY(map_for_each_c, tc) +{ + atf_map_t map; + atf_map_citer_t iter; + size_t count, i, size; + char keys[10][5]; + int nums[10]; + + printf("Iterating over empty map\n"); + RE(atf_map_init(&map)); + count = 0; + atf_map_for_each_c(iter, &map) { + count++; + printf("Item count is now %zd\n", count); + } + ATF_REQUIRE_EQ(count, 0); + atf_map_fini(&map); + + for (size = 0; size <= 10; size++) { + printf("Iterating over map of %zd elements\n", size); + RE(atf_map_init(&map)); + for (i = 0; i < size; i++) { + nums[i] = i + 1; + snprintf(keys[i], sizeof(keys[i]), "%d", nums[i]); + RE(atf_map_insert(&map, keys[i], &nums[i], false)); + } + count = 0; + atf_map_for_each_c(iter, &map) { + printf("Retrieved item: %d\n", + *(const int *)atf_map_citer_data(iter)); + count++; + } + ATF_REQUIRE_EQ(count, size); + atf_map_fini(&map); + } +} + +/* + * Other. + */ + +ATF_TC(stable_keys); +ATF_TC_HEAD(stable_keys, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that the keys do not change " + "even if their original values do"); +} +ATF_TC_BODY(stable_keys, tc) +{ + atf_map_t map; + atf_map_citer_t iter; + char key[] = "K1"; + + RE(atf_map_init(&map)); + + RE(atf_map_insert(&map, key, strdup("test-value"), true)); + iter = atf_map_find_c(&map, "K1"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + iter = atf_map_find_c(&map, "K2"); + ATF_REQUIRE(atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + + strcpy(key, "K2"); + iter = atf_map_find_c(&map, "K1"); + ATF_REQUIRE(!atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + iter = atf_map_find_c(&map, "K2"); + ATF_REQUIRE(atf_equal_map_citer_map_citer(iter, atf_map_end_c(&map))); + + atf_map_fini(&map); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Constructors and destructors. */ + ATF_TP_ADD_TC(tp, map_init); + ATF_TP_ADD_TC(tp, map_init_charpp_null); + ATF_TP_ADD_TC(tp, map_init_charpp_empty); + ATF_TP_ADD_TC(tp, map_init_charpp_some); + ATF_TP_ADD_TC(tp, map_init_charpp_short); + + /* Getters. */ + ATF_TP_ADD_TC(tp, find); + ATF_TP_ADD_TC(tp, find_c); + ATF_TP_ADD_TC(tp, to_charpp_empty); + ATF_TP_ADD_TC(tp, to_charpp_some); + + /* Modifiers. */ + ATF_TP_ADD_TC(tp, map_insert); + + /* Macros. */ + ATF_TP_ADD_TC(tp, map_for_each); + ATF_TP_ADD_TC(tp, map_for_each_c); + + /* Other. */ + ATF_TP_ADD_TC(tp, stable_keys); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/process.c b/unit/atf-src/atf-c/detail/process.c new file mode 100644 index 0000000..8e08b6c --- /dev/null +++ b/unit/atf-src/atf-c/detail/process.c @@ -0,0 +1,670 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/process.h" + +#include <sys/types.h> +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "atf-c/defs.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/error.h" + +/* This prototype is not in the header file because this is a private + * function; however, we need to access it during testing. */ +atf_error_t atf_process_status_init(atf_process_status_t *, int); + +/* --------------------------------------------------------------------- + * The "stream_prepare" auxiliary type. + * --------------------------------------------------------------------- */ + +struct stream_prepare { + const atf_process_stream_t *m_sb; + + bool m_pipefds_ok; + int m_pipefds[2]; +}; +typedef struct stream_prepare stream_prepare_t; + +static +atf_error_t +stream_prepare_init(stream_prepare_t *sp, const atf_process_stream_t *sb) +{ + atf_error_t err; + + const int type = atf_process_stream_type(sb); + + sp->m_sb = sb; + sp->m_pipefds_ok = false; + + if (type == atf_process_stream_type_capture) { + if (pipe(sp->m_pipefds) == -1) + err = atf_libc_error(errno, "Failed to create pipe"); + else { + err = atf_no_error(); + sp->m_pipefds_ok = true; + } + } else + err = atf_no_error(); + + return err; +} + +static +void +stream_prepare_fini(stream_prepare_t *sp) +{ + if (sp->m_pipefds_ok) { + close(sp->m_pipefds[0]); + close(sp->m_pipefds[1]); + } +} + +/* --------------------------------------------------------------------- + * The "atf_process_stream" type. + * --------------------------------------------------------------------- */ + +const int atf_process_stream_type_capture = 1; +const int atf_process_stream_type_connect = 2; +const int atf_process_stream_type_inherit = 3; +const int atf_process_stream_type_redirect_fd = 4; +const int atf_process_stream_type_redirect_path = 5; + +static +bool +stream_is_valid(const atf_process_stream_t *sb) +{ + return (sb->m_type == atf_process_stream_type_capture) || + (sb->m_type == atf_process_stream_type_connect) || + (sb->m_type == atf_process_stream_type_inherit) || + (sb->m_type == atf_process_stream_type_redirect_fd) || + (sb->m_type == atf_process_stream_type_redirect_path); +} + +atf_error_t +atf_process_stream_init_capture(atf_process_stream_t *sb) +{ + sb->m_type = atf_process_stream_type_capture; + + POST(stream_is_valid(sb)); + return atf_no_error(); +} + +atf_error_t +atf_process_stream_init_connect(atf_process_stream_t *sb, + const int src_fd, const int tgt_fd) +{ + PRE(src_fd >= 0); + PRE(tgt_fd >= 0); + PRE(src_fd != tgt_fd); + + sb->m_type = atf_process_stream_type_connect; + sb->m_src_fd = src_fd; + sb->m_tgt_fd = tgt_fd; + + POST(stream_is_valid(sb)); + return atf_no_error(); +} + +atf_error_t +atf_process_stream_init_inherit(atf_process_stream_t *sb) +{ + sb->m_type = atf_process_stream_type_inherit; + + POST(stream_is_valid(sb)); + return atf_no_error(); +} + +atf_error_t +atf_process_stream_init_redirect_fd(atf_process_stream_t *sb, + const int fd) +{ + sb->m_type = atf_process_stream_type_redirect_fd; + sb->m_fd = fd; + + POST(stream_is_valid(sb)); + return atf_no_error(); +} + +atf_error_t +atf_process_stream_init_redirect_path(atf_process_stream_t *sb, + const atf_fs_path_t *path) +{ + sb->m_type = atf_process_stream_type_redirect_path; + sb->m_path = path; + + POST(stream_is_valid(sb)); + return atf_no_error(); +} + +void +atf_process_stream_fini(atf_process_stream_t *sb) +{ + PRE(stream_is_valid(sb)); +} + +int +atf_process_stream_type(const atf_process_stream_t *sb) +{ + PRE(stream_is_valid(sb)); + + return sb->m_type; +} + +/* --------------------------------------------------------------------- + * The "atf_process_status" type. + * --------------------------------------------------------------------- */ + +atf_error_t +atf_process_status_init(atf_process_status_t *s, int status) +{ + s->m_status = status; + + return atf_no_error(); +} + +void +atf_process_status_fini(atf_process_status_t *s ATF_DEFS_ATTRIBUTE_UNUSED) +{ +} + +bool +atf_process_status_exited(const atf_process_status_t *s) +{ + int mutable_status = s->m_status; + return WIFEXITED(mutable_status); +} + +int +atf_process_status_exitstatus(const atf_process_status_t *s) +{ + PRE(atf_process_status_exited(s)); + int mutable_status = s->m_status; + return WEXITSTATUS(mutable_status); +} + +bool +atf_process_status_signaled(const atf_process_status_t *s) +{ + int mutable_status = s->m_status; + return WIFSIGNALED(mutable_status); +} + +int +atf_process_status_termsig(const atf_process_status_t *s) +{ + PRE(atf_process_status_signaled(s)); + int mutable_status = s->m_status; + return WTERMSIG(mutable_status); +} + +bool +atf_process_status_coredump(const atf_process_status_t *s) +{ + PRE(atf_process_status_signaled(s)); +#if defined(WCOREDUMP) + int mutable_status = s->m_status; + return WCOREDUMP(mutable_status); +#else + return false; +#endif +} + +/* --------------------------------------------------------------------- + * The "atf_process_child" type. + * --------------------------------------------------------------------- */ + +static +atf_error_t +atf_process_child_init(atf_process_child_t *c) +{ + c->m_pid = 0; + c->m_stdout = -1; + c->m_stderr = -1; + + return atf_no_error(); +} + +static +void +atf_process_child_fini(atf_process_child_t *c) +{ + if (c->m_stdout != -1) + close(c->m_stdout); + if (c->m_stderr != -1) + close(c->m_stderr); +} + +atf_error_t +atf_process_child_wait(atf_process_child_t *c, atf_process_status_t *s) +{ + atf_error_t err; + int status; + + if (waitpid(c->m_pid, &status, 0) == -1) + err = atf_libc_error(errno, "Failed waiting for process %d", + c->m_pid); + else { + atf_process_child_fini(c); + err = atf_process_status_init(s, status); + } + + return err; +} + +pid_t +atf_process_child_pid(const atf_process_child_t *c) +{ + return c->m_pid; +} + +int +atf_process_child_stdout(atf_process_child_t *c) +{ + PRE(c->m_stdout != -1); + return c->m_stdout; +} + +int +atf_process_child_stderr(atf_process_child_t *c) +{ + PRE(c->m_stderr != -1); + return c->m_stderr; +} + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +static +atf_error_t +safe_dup(const int oldfd, const int newfd) +{ + atf_error_t err; + + if (oldfd != newfd) { + if (dup2(oldfd, newfd) == -1) { + err = atf_libc_error(errno, "Could not allocate file descriptor"); + } else { + close(oldfd); + err = atf_no_error(); + } + } else + err = atf_no_error(); + + return err; +} + +static +atf_error_t +child_connect(const stream_prepare_t *sp, int procfd) +{ + atf_error_t err; + const int type = atf_process_stream_type(sp->m_sb); + + if (type == atf_process_stream_type_capture) { + close(sp->m_pipefds[0]); + err = safe_dup(sp->m_pipefds[1], procfd); + } else if (type == atf_process_stream_type_connect) { + if (dup2(sp->m_sb->m_tgt_fd, sp->m_sb->m_src_fd) == -1) + err = atf_libc_error(errno, "Cannot connect descriptor %d to %d", + sp->m_sb->m_tgt_fd, sp->m_sb->m_src_fd); + else + err = atf_no_error(); + } else if (type == atf_process_stream_type_inherit) { + err = atf_no_error(); + } else if (type == atf_process_stream_type_redirect_fd) { + err = safe_dup(sp->m_sb->m_fd, procfd); + } else if (type == atf_process_stream_type_redirect_path) { + int aux = open(atf_fs_path_cstring(sp->m_sb->m_path), + O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (aux == -1) + err = atf_libc_error(errno, "Could not create %s", + atf_fs_path_cstring(sp->m_sb->m_path)); + else { + err = safe_dup(aux, procfd); + if (atf_is_error(err)) + close(aux); + } + } else { + UNREACHABLE; + err = atf_no_error(); + } + + return err; +} + +static +void +parent_connect(const stream_prepare_t *sp, int *fd) +{ + const int type = atf_process_stream_type(sp->m_sb); + + if (type == atf_process_stream_type_capture) { + close(sp->m_pipefds[1]); + *fd = sp->m_pipefds[0]; + } else if (type == atf_process_stream_type_connect) { + /* Do nothing. */ + } else if (type == atf_process_stream_type_inherit) { + /* Do nothing. */ + } else if (type == atf_process_stream_type_redirect_fd) { + /* Do nothing. */ + } else if (type == atf_process_stream_type_redirect_path) { + /* Do nothing. */ + } else { + UNREACHABLE; + } +} + +static +atf_error_t +do_parent(atf_process_child_t *c, + const pid_t pid, + const stream_prepare_t *outsp, + const stream_prepare_t *errsp) +{ + atf_error_t err; + + err = atf_process_child_init(c); + if (atf_is_error(err)) + goto out; + + c->m_pid = pid; + + parent_connect(outsp, &c->m_stdout); + parent_connect(errsp, &c->m_stderr); + +out: + return err; +} + +static +void +do_child(void (*)(void *), + void *, + const stream_prepare_t *, + const stream_prepare_t *) ATF_DEFS_ATTRIBUTE_NORETURN; + +static +void +do_child(void (*start)(void *), + void *v, + const stream_prepare_t *outsp, + const stream_prepare_t *errsp) +{ + atf_error_t err; + + err = child_connect(outsp, STDOUT_FILENO); + if (atf_is_error(err)) + goto out; + + err = child_connect(errsp, STDERR_FILENO); + if (atf_is_error(err)) + goto out; + + start(v); + UNREACHABLE; + +out: + if (atf_is_error(err)) { + char buf[1024]; + + atf_error_format(err, buf, sizeof(buf)); + fprintf(stderr, "Unhandled error: %s\n", buf); + atf_error_free(err); + + exit(EXIT_FAILURE); + } else + exit(EXIT_SUCCESS); +} + +static +atf_error_t +fork_with_streams(atf_process_child_t *c, + void (*start)(void *), + const atf_process_stream_t *outsb, + const atf_process_stream_t *errsb, + void *v) +{ + atf_error_t err; + stream_prepare_t outsp; + stream_prepare_t errsp; + pid_t pid; + + err = stream_prepare_init(&outsp, outsb); + if (atf_is_error(err)) + goto out; + + err = stream_prepare_init(&errsp, errsb); + if (atf_is_error(err)) + goto err_outpipe; + + pid = fork(); + if (pid == -1) { + err = atf_libc_error(errno, "Failed to fork"); + goto err_errpipe; + } + + if (pid == 0) { + do_child(start, v, &outsp, &errsp); + UNREACHABLE; + abort(); + err = atf_no_error(); + } else { + err = do_parent(c, pid, &outsp, &errsp); + if (atf_is_error(err)) + goto err_errpipe; + } + + goto out; + +err_errpipe: + stream_prepare_fini(&errsp); +err_outpipe: + stream_prepare_fini(&outsp); + +out: + return err; +} + +static +atf_error_t +init_stream_w_default(const atf_process_stream_t *usersb, + atf_process_stream_t *inheritsb, + const atf_process_stream_t **realsb) +{ + atf_error_t err; + + if (usersb == NULL) { + err = atf_process_stream_init_inherit(inheritsb); + if (!atf_is_error(err)) + *realsb = inheritsb; + } else { + err = atf_no_error(); + *realsb = usersb; + } + + return err; +} + +atf_error_t +atf_process_fork(atf_process_child_t *c, + void (*start)(void *), + const atf_process_stream_t *outsb, + const atf_process_stream_t *errsb, + void *v) +{ + atf_error_t err; + atf_process_stream_t inherit_outsb, inherit_errsb; + const atf_process_stream_t *real_outsb, *real_errsb; + + real_outsb = NULL; /* Shut up GCC warning. */ + err = init_stream_w_default(outsb, &inherit_outsb, &real_outsb); + if (atf_is_error(err)) + goto out; + + real_errsb = NULL; /* Shut up GCC warning. */ + err = init_stream_w_default(errsb, &inherit_errsb, &real_errsb); + if (atf_is_error(err)) + goto out_out; + + err = fork_with_streams(c, start, real_outsb, real_errsb, v); + + if (errsb == NULL) + atf_process_stream_fini(&inherit_errsb); +out_out: + if (outsb == NULL) + atf_process_stream_fini(&inherit_outsb); +out: + return err; +} + +static +int +const_execvp(const char *file, const char *const *argv) +{ +#define UNCONST(a) ((void *)(unsigned long)(const void *)(a)) + return execvp(file, UNCONST(argv)); +#undef UNCONST +} + +static +atf_error_t +list_to_array(const atf_list_t *l, const char ***ap) +{ + atf_error_t err; + const char **a; + + a = (const char **)malloc((atf_list_size(l) + 1) * sizeof(const char *)); + if (a == NULL) + err = atf_no_memory_error(); + else { + const char **aiter; + atf_list_citer_t liter; + + aiter = a; + atf_list_for_each_c(liter, l) { + *aiter = (const char *)atf_list_citer_data(liter); + aiter++; + } + *aiter = NULL; + + err = atf_no_error(); + *ap = a; + } + + return err; +} + +struct exec_args { + const atf_fs_path_t *m_prog; + const char *const *m_argv; + void (*m_prehook)(void); +}; + +static +void +do_exec(void *v) +{ + struct exec_args *ea = v; + + if (ea->m_prehook != NULL) + ea->m_prehook(); + + const int ret = const_execvp(atf_fs_path_cstring(ea->m_prog), ea->m_argv); + const int errnocopy = errno; + INV(ret == -1); + fprintf(stderr, "exec(%s) failed: %s\n", + atf_fs_path_cstring(ea->m_prog), strerror(errnocopy)); + exit(EXIT_FAILURE); +} + +atf_error_t +atf_process_exec_array(atf_process_status_t *s, + const atf_fs_path_t *prog, + const char *const *argv, + const atf_process_stream_t *outsb, + const atf_process_stream_t *errsb, + void (*prehook)(void)) +{ + atf_error_t err; + atf_process_child_t c; + struct exec_args ea = { prog, argv, prehook }; + + PRE(outsb == NULL || + atf_process_stream_type(outsb) != atf_process_stream_type_capture); + PRE(errsb == NULL || + atf_process_stream_type(errsb) != atf_process_stream_type_capture); + + err = atf_process_fork(&c, do_exec, outsb, errsb, &ea); + if (atf_is_error(err)) + goto out; + +again: + err = atf_process_child_wait(&c, s); + if (atf_is_error(err)) { + INV(atf_error_is(err, "libc") && atf_libc_error_code(err) == EINTR); + atf_error_free(err); + goto again; + } + +out: + return err; +} + +atf_error_t +atf_process_exec_list(atf_process_status_t *s, + const atf_fs_path_t *prog, + const atf_list_t *argv, + const atf_process_stream_t *outsb, + const atf_process_stream_t *errsb, + void (*prehook)(void)) +{ + atf_error_t err; + const char **argv2; + + PRE(outsb == NULL || + atf_process_stream_type(outsb) != atf_process_stream_type_capture); + PRE(errsb == NULL || + atf_process_stream_type(errsb) != atf_process_stream_type_capture); + + argv2 = NULL; /* Silence GCC warning. */ + err = list_to_array(argv, &argv2); + if (atf_is_error(err)) + goto out; + + err = atf_process_exec_array(s, prog, argv2, outsb, errsb, prehook); + + free(argv2); +out: + return err; +} diff --git a/unit/atf-src/atf-c/detail/process.h b/unit/atf-src/atf-c/detail/process.h new file mode 100644 index 0000000..81158d0 --- /dev/null +++ b/unit/atf-src/atf-c/detail/process.h @@ -0,0 +1,131 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_PROCESS_H) +#define ATF_C_DETAIL_PROCESS_H + +#include <sys/types.h> + +#include <stdbool.h> + +#include <atf-c/detail/fs.h> +#include <atf-c/detail/list.h> +#include <atf-c/error_fwd.h> + +/* --------------------------------------------------------------------- + * The "atf_process_stream" type. + * --------------------------------------------------------------------- */ + +struct atf_process_stream { + int m_type; + + /* Valid if m_type == connect. */ + int m_src_fd; + int m_tgt_fd; + + /* Valid if m_type == redirect_fd. */ + int m_fd; + + /* Valid if m_type == redirect_path. */ + const atf_fs_path_t *m_path; +}; +typedef struct atf_process_stream atf_process_stream_t; + +extern const int atf_process_stream_type_capture; +extern const int atf_process_stream_type_connect; +extern const int atf_process_stream_type_inherit; +extern const int atf_process_stream_type_redirect_fd; +extern const int atf_process_stream_type_redirect_path; + +atf_error_t atf_process_stream_init_capture(atf_process_stream_t *); +atf_error_t atf_process_stream_init_connect(atf_process_stream_t *, + const int, const int); +atf_error_t atf_process_stream_init_inherit(atf_process_stream_t *); +atf_error_t atf_process_stream_init_redirect_fd(atf_process_stream_t *, + const int fd); +atf_error_t atf_process_stream_init_redirect_path(atf_process_stream_t *, + const atf_fs_path_t *); +void atf_process_stream_fini(atf_process_stream_t *); + +int atf_process_stream_type(const atf_process_stream_t *); + +/* --------------------------------------------------------------------- + * The "atf_process_status" type. + * --------------------------------------------------------------------- */ + +struct atf_process_status { + int m_status; +}; +typedef struct atf_process_status atf_process_status_t; + +void atf_process_status_fini(atf_process_status_t *); + +bool atf_process_status_exited(const atf_process_status_t *); +int atf_process_status_exitstatus(const atf_process_status_t *); +bool atf_process_status_signaled(const atf_process_status_t *); +int atf_process_status_termsig(const atf_process_status_t *); +bool atf_process_status_coredump(const atf_process_status_t *); + +/* --------------------------------------------------------------------- + * The "atf_process_child" type. + * --------------------------------------------------------------------- */ + +struct atf_process_child { + pid_t m_pid; + + int m_stdout; + int m_stderr; +}; +typedef struct atf_process_child atf_process_child_t; + +atf_error_t atf_process_child_wait(atf_process_child_t *, + atf_process_status_t *); +pid_t atf_process_child_pid(const atf_process_child_t *); +int atf_process_child_stdout(atf_process_child_t *); +int atf_process_child_stderr(atf_process_child_t *); + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +atf_error_t atf_process_fork(atf_process_child_t *, + void (*)(void *), + const atf_process_stream_t *, + const atf_process_stream_t *, + void *); +atf_error_t atf_process_exec_array(atf_process_status_t *, + const atf_fs_path_t *, + const char *const *, + const atf_process_stream_t *, + const atf_process_stream_t *, + void (*)(void)); +atf_error_t atf_process_exec_list(atf_process_status_t *, + const atf_fs_path_t *, + const atf_list_t *, + const atf_process_stream_t *, + const atf_process_stream_t *, + void (*)(void)); + +#endif /* !defined(ATF_C_DETAIL_PROCESS_H) */ diff --git a/unit/atf-src/atf-c/detail/process_helpers.c b/unit/atf-src/atf-c/detail/process_helpers.c new file mode 100644 index 0000000..47b90bc --- /dev/null +++ b/unit/atf-src/atf-c/detail/process_helpers.c @@ -0,0 +1,113 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include <sys/types.h> + +#include <assert.h> /* NO_CHECK_STYLE */ +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static +int +h_echo(const char *msg) +{ + printf("%s\n", msg); + return EXIT_SUCCESS; +} + +static +int +h_exit_failure(void) +{ + return EXIT_FAILURE; +} + +static +int +h_exit_signal(void) +{ + kill(getpid(), SIGKILL); + assert(0); /* NO_CHECK_STYLE */ + return EXIT_FAILURE; +} + +static +int +h_exit_success(void) +{ + return EXIT_SUCCESS; +} + +static +int +h_stdout_stderr(const char *id) +{ + fprintf(stdout, "Line 1 to stdout for %s\n", id); + fprintf(stdout, "Line 2 to stdout for %s\n", id); + fprintf(stderr, "Line 1 to stderr for %s\n", id); + fprintf(stderr, "Line 2 to stderr for %s\n", id); + + return EXIT_SUCCESS; +} + +static +void +check_args(const int argc, const char *const argv[], const int required) +{ + if (argc < required) { + fprintf(stderr, "Usage: %s helper-name [args]\n", argv[0]); + exit(EXIT_FAILURE); + } +} + +int +main(int argc, const char *const argv[]) +{ + int exitcode; + + check_args(argc, argv, 2); + + if (strcmp(argv[1], "echo") == 0) { + check_args(argc, argv, 3); + exitcode = h_echo(argv[2]); + } else if (strcmp(argv[1], "exit-failure") == 0) + exitcode = h_exit_failure(); + else if (strcmp(argv[1], "exit-signal") == 0) + exitcode = h_exit_signal(); + else if (strcmp(argv[1], "exit-success") == 0) + exitcode = h_exit_success(); + else if (strcmp(argv[1], "stdout-stderr") == 0) { + check_args(argc, argv, 3); + exitcode = h_stdout_stderr(argv[2]); + } else { + fprintf(stderr, "%s: Unknown helper %s\n", argv[0], argv[1]); + exitcode = EXIT_FAILURE; + } + + return exitcode; +} diff --git a/unit/atf-src/atf-c/detail/process_test.c b/unit/atf-src/atf-c/detail/process_test.c new file mode 100644 index 0000000..5ae5565 --- /dev/null +++ b/unit/atf-src/atf-c/detail/process_test.c @@ -0,0 +1,1159 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/process.h" + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/defs.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/detail/test_helpers.h" + +atf_error_t atf_process_status_init(atf_process_status_t *, int); + +/* --------------------------------------------------------------------- + * Auxiliary functions for testing of 'atf_process_fork'. + * --------------------------------------------------------------------- */ + +/* + * Testing of atf_process_fork is quite messy. We want to be able to test + * all the possible combinations of stdout and stderr behavior to ensure + * that the streams are manipulated correctly. + * + * To do this, the do_fork function is a wrapper for atf_process_fork that + * issues stream-specific hooks before fork, while the child is running and + * after the child terminates. We then provide test cases that just call + * do_fork with different hooks. + * + * The hooks are described by base_stream, and we then have one *_stream + * type for ever possible stream behavior. + */ + +enum out_type { stdout_type, stderr_type }; + +struct base_stream { + void (*init)(void *); + void (*process)(void *, atf_process_child_t *); + void (*fini)(void *); + + /* m_sb is initialized by subclasses that need it, but all consumers + * must use m_sb_ptr, which may or may not point to m_sb. This allows + * us to test the interface with a NULL value, which triggers a + * default behavior. */ + atf_process_stream_t m_sb; + atf_process_stream_t *m_sb_ptr; + enum out_type m_type; +}; +#define BASE_STREAM(ihook, phook, fhook, type) \ + { .init = ihook, \ + .process = phook, \ + .fini = fhook, \ + .m_type = type } + +static +void +check_file(const enum out_type type) +{ + switch (type) { + case stdout_type: + ATF_CHECK(atf_utils_grep_file("stdout: msg", "stdout")); + ATF_CHECK(!atf_utils_grep_file("stderr: msg", "stdout")); + break; + case stderr_type: + ATF_CHECK(atf_utils_grep_file("stderr: msg", "stderr")); + ATF_CHECK(!atf_utils_grep_file("stdout: msg", "stderr")); + break; + default: + UNREACHABLE; + } +} + +struct capture_stream { + struct base_stream m_base; + + char *m_msg; +}; +#define CAPTURE_STREAM(type) \ + { .m_base = BASE_STREAM(capture_stream_init, \ + capture_stream_process, \ + capture_stream_fini, \ + type) } + +static +void +capture_stream_init(void *v) +{ + struct capture_stream *s = v; + + s->m_base.m_sb_ptr = &s->m_base.m_sb; + RE(atf_process_stream_init_capture(&s->m_base.m_sb)); + s->m_msg = NULL; +} + +static +void +capture_stream_process(void *v, atf_process_child_t *c) +{ + struct capture_stream *s = v; + + switch (s->m_base.m_type) { + case stdout_type: + s->m_msg = atf_utils_readline(atf_process_child_stdout(c)); + break; + case stderr_type: + s->m_msg = atf_utils_readline(atf_process_child_stderr(c)); + break; + default: + UNREACHABLE; + } +} + +static +void +capture_stream_fini(void *v) +{ + struct capture_stream *s = v; + + switch (s->m_base.m_type) { + case stdout_type: + ATF_CHECK(atf_utils_grep_string("stdout: msg", s->m_msg)); + ATF_CHECK(!atf_utils_grep_string("stderr: msg", s->m_msg)); + break; + case stderr_type: + ATF_CHECK(!atf_utils_grep_string("stdout: msg", s->m_msg)); + ATF_CHECK(atf_utils_grep_string("stderr: msg", s->m_msg)); + break; + default: + UNREACHABLE; + } + + free(s->m_msg); + atf_process_stream_fini(&s->m_base.m_sb); +} + +struct connect_stream { + struct base_stream m_base; + + int m_fd; +}; +#define CONNECT_STREAM(type) \ + { .m_base = BASE_STREAM(connect_stream_init, \ + NULL, \ + connect_stream_fini, \ + type) } + +static +void +connect_stream_init(void *v) +{ + struct connect_stream *s = v; + int src_fd; + + switch (s->m_base.m_type) { + case stdout_type: + src_fd = STDOUT_FILENO; + s->m_fd = open("stdout", O_WRONLY | O_CREAT | O_TRUNC, 0644); + break; + case stderr_type: + src_fd = STDERR_FILENO; + s->m_fd = open("stderr", O_WRONLY | O_CREAT | O_TRUNC, 0644); + break; + default: + UNREACHABLE; + src_fd = -1; + } + ATF_REQUIRE(s->m_fd != -1); + + s->m_base.m_sb_ptr = &s->m_base.m_sb; + RE(atf_process_stream_init_connect(&s->m_base.m_sb, src_fd, s->m_fd)); +} + +static +void +connect_stream_fini(void *v) +{ + struct connect_stream *s = v; + + ATF_REQUIRE(close(s->m_fd) != -1); + + atf_process_stream_fini(&s->m_base.m_sb); + + check_file(s->m_base.m_type); +} + +struct inherit_stream { + struct base_stream m_base; + int m_fd; + + int m_old_fd; +}; +#define INHERIT_STREAM(type) \ + { .m_base = BASE_STREAM(inherit_stream_init, \ + NULL, \ + inherit_stream_fini, \ + type) } + +static +void +inherit_stream_init(void *v) +{ + struct inherit_stream *s = v; + const char *name; + + s->m_base.m_sb_ptr = &s->m_base.m_sb; + RE(atf_process_stream_init_inherit(&s->m_base.m_sb)); + + switch (s->m_base.m_type) { + case stdout_type: + s->m_fd = STDOUT_FILENO; + name = "stdout"; + break; + case stderr_type: + s->m_fd = STDERR_FILENO; + name = "stderr"; + break; + default: + UNREACHABLE; + name = NULL; + } + + s->m_old_fd = dup(s->m_fd); + ATF_REQUIRE(s->m_old_fd != -1); + ATF_REQUIRE(close(s->m_fd) != -1); + ATF_REQUIRE_EQ(open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644), + s->m_fd); +} + +static +void +inherit_stream_fini(void *v) +{ + struct inherit_stream *s = v; + + ATF_REQUIRE(dup2(s->m_old_fd, s->m_fd) != -1); + ATF_REQUIRE(close(s->m_old_fd) != -1); + + atf_process_stream_fini(&s->m_base.m_sb); + + check_file(s->m_base.m_type); +} + +#define default_stream inherit_stream +#define DEFAULT_STREAM(type) \ + { .m_base = BASE_STREAM(default_stream_init, \ + NULL, \ + default_stream_fini, \ + type) } + +static +void +default_stream_init(void *v) +{ + struct inherit_stream *s = v; + + inherit_stream_init(v); + s->m_base.m_sb_ptr = NULL; +} + +static +void +default_stream_fini(void *v) +{ + inherit_stream_fini(v); +} + +struct redirect_fd_stream { + struct base_stream m_base; + + int m_fd; +}; +#define REDIRECT_FD_STREAM(type) \ + { .m_base = BASE_STREAM(redirect_fd_stream_init, \ + NULL, \ + redirect_fd_stream_fini, \ + type) } + +static +void +redirect_fd_stream_init(void *v) +{ + struct redirect_fd_stream *s = v; + + switch (s->m_base.m_type) { + case stdout_type: + s->m_fd = open("stdout", O_WRONLY | O_CREAT | O_TRUNC, 0644); + break; + case stderr_type: + s->m_fd = open("stderr", O_WRONLY | O_CREAT | O_TRUNC, 0644); + break; + default: + UNREACHABLE; + } + ATF_REQUIRE(s->m_fd != -1); + + s->m_base.m_sb_ptr = &s->m_base.m_sb; + RE(atf_process_stream_init_redirect_fd(&s->m_base.m_sb, s->m_fd)); +} + +static +void +redirect_fd_stream_fini(void *v) +{ + struct redirect_fd_stream *s = v; + + ATF_REQUIRE(close(s->m_fd) != -1); + + atf_process_stream_fini(&s->m_base.m_sb); + + check_file(s->m_base.m_type); +} + +struct redirect_path_stream { + struct base_stream m_base; + + atf_fs_path_t m_path; +}; +#define REDIRECT_PATH_STREAM(type) \ + { .m_base = BASE_STREAM(redirect_path_stream_init, \ + NULL, \ + redirect_path_stream_fini, \ + type) } + +static +void +redirect_path_stream_init(void *v) +{ + struct redirect_path_stream *s = v; + + switch (s->m_base.m_type) { + case stdout_type: + RE(atf_fs_path_init_fmt(&s->m_path, "stdout")); + break; + case stderr_type: + RE(atf_fs_path_init_fmt(&s->m_path, "stderr")); + break; + default: + UNREACHABLE; + } + + s->m_base.m_sb_ptr = &s->m_base.m_sb; + RE(atf_process_stream_init_redirect_path(&s->m_base.m_sb, &s->m_path)); +} + +static +void +redirect_path_stream_fini(void *v) +{ + struct redirect_path_stream *s = v; + + atf_process_stream_fini(&s->m_base.m_sb); + + atf_fs_path_fini(&s->m_path); + + check_file(s->m_base.m_type); +} + +static void child_print(void *) ATF_DEFS_ATTRIBUTE_NORETURN; + +struct child_print_data { + const char *m_msg; +}; + +static +void +child_print(void *v) +{ + struct child_print_data *cpd = v; + + fprintf(stdout, "stdout: %s\n", cpd->m_msg); + fprintf(stderr, "stderr: %s\n", cpd->m_msg); + + exit(EXIT_SUCCESS); +} + +static +void +do_fork(const struct base_stream *outfs, void *out, + const struct base_stream *errfs, void *err) +{ + atf_process_child_t child; + atf_process_status_t status; + struct child_print_data cpd = { "msg" }; + + outfs->init(out); + errfs->init(err); + + RE(atf_process_fork(&child, child_print, outfs->m_sb_ptr, + errfs->m_sb_ptr, &cpd)); + if (outfs->process != NULL) + outfs->process(out, &child); + if (errfs->process != NULL) + errfs->process(err, &child); + RE(atf_process_child_wait(&child, &status)); + + outfs->fini(out); + errfs->fini(err); + + atf_process_status_fini(&status); +} + +/* --------------------------------------------------------------------- + * Test cases for the "stream" type. + * --------------------------------------------------------------------- */ + +ATF_TC(stream_init_capture); +ATF_TC_HEAD(stream_init_capture, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the " + "atf_process_stream_init_capture function"); +} +ATF_TC_BODY(stream_init_capture, tc) +{ + atf_process_stream_t sb; + + RE(atf_process_stream_init_capture(&sb)); + + ATF_CHECK_EQ(atf_process_stream_type(&sb), + atf_process_stream_type_capture); + + atf_process_stream_fini(&sb); +} + +ATF_TC(stream_init_connect); +ATF_TC_HEAD(stream_init_connect, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the " + "atf_process_stream_init_connect function"); +} +ATF_TC_BODY(stream_init_connect, tc) +{ + atf_process_stream_t sb; + + RE(atf_process_stream_init_connect(&sb, 1, 2)); + + ATF_CHECK_EQ(atf_process_stream_type(&sb), + atf_process_stream_type_connect); + + atf_process_stream_fini(&sb); +} + +ATF_TC(stream_init_inherit); +ATF_TC_HEAD(stream_init_inherit, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the " + "atf_process_stream_init_inherit function"); +} +ATF_TC_BODY(stream_init_inherit, tc) +{ + atf_process_stream_t sb; + + RE(atf_process_stream_init_inherit(&sb)); + + ATF_CHECK_EQ(atf_process_stream_type(&sb), + atf_process_stream_type_inherit); + + atf_process_stream_fini(&sb); +} + +ATF_TC(stream_init_redirect_fd); +ATF_TC_HEAD(stream_init_redirect_fd, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the " + "atf_process_stream_init_redirect_fd function"); +} +ATF_TC_BODY(stream_init_redirect_fd, tc) +{ + atf_process_stream_t sb; + + RE(atf_process_stream_init_redirect_fd(&sb, 1)); + + ATF_CHECK_EQ(atf_process_stream_type(&sb), + atf_process_stream_type_redirect_fd); + + atf_process_stream_fini(&sb); +} + +ATF_TC(stream_init_redirect_path); +ATF_TC_HEAD(stream_init_redirect_path, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the " + "atf_process_stream_init_redirect_path function"); +} +ATF_TC_BODY(stream_init_redirect_path, tc) +{ + atf_process_stream_t sb; + atf_fs_path_t path; + + RE(atf_fs_path_init_fmt(&path, "foo")); + RE(atf_process_stream_init_redirect_path(&sb, &path)); + + ATF_CHECK_EQ(atf_process_stream_type(&sb), + atf_process_stream_type_redirect_path); + + atf_process_stream_fini(&sb); + atf_fs_path_fini(&path); +} + +/* --------------------------------------------------------------------- + * Test cases for the "status" type. + * --------------------------------------------------------------------- */ + +static void child_exit_success(void) ATF_DEFS_ATTRIBUTE_NORETURN; +static void child_exit_failure(void) ATF_DEFS_ATTRIBUTE_NORETURN; +static void child_sigkill(void) ATF_DEFS_ATTRIBUTE_NORETURN; +static void child_sigquit(void) ATF_DEFS_ATTRIBUTE_NORETURN; +static void child_sigterm(void) ATF_DEFS_ATTRIBUTE_NORETURN; + +void +child_exit_success(void) +{ + exit(EXIT_SUCCESS); +} + +void +child_exit_failure(void) +{ + exit(EXIT_FAILURE); +} + +void +child_sigkill(void) +{ + kill(getpid(), SIGKILL); + abort(); +} + +void +child_sigquit(void) +{ + kill(getpid(), SIGQUIT); + abort(); +} + +void +child_sigterm(void) +{ + kill(getpid(), SIGTERM); + abort(); +} + +static +int +fork_and_wait_child(void (*child_func)(void)) +{ + pid_t pid; + int status; + + pid = fork(); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + status = 0; /* Silence compiler warnings */ + child_func(); + UNREACHABLE; + } else { + ATF_REQUIRE(waitpid(pid, &status, 0) != 0); + } + + return status; +} + +ATF_TC(status_exited); +ATF_TC_HEAD(status_exited, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the status type for processes " + "that exit cleanly"); +} +ATF_TC_BODY(status_exited, tc) +{ + { + const int rawstatus = fork_and_wait_child(child_exit_success); + atf_process_status_t s; + RE(atf_process_status_init(&s, rawstatus)); + ATF_CHECK(atf_process_status_exited(&s)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&s), EXIT_SUCCESS); + ATF_CHECK(!atf_process_status_signaled(&s)); + atf_process_status_fini(&s); + } + + { + const int rawstatus = fork_and_wait_child(child_exit_failure); + atf_process_status_t s; + RE(atf_process_status_init(&s, rawstatus)); + ATF_CHECK(atf_process_status_exited(&s)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&s), EXIT_FAILURE); + ATF_CHECK(!atf_process_status_signaled(&s)); + atf_process_status_fini(&s); + } +} + +ATF_TC(status_signaled); +ATF_TC_HEAD(status_signaled, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the status type for processes " + "that end due to a signal"); +} +ATF_TC_BODY(status_signaled, tc) +{ + { + const int rawstatus = fork_and_wait_child(child_sigkill); + atf_process_status_t s; + RE(atf_process_status_init(&s, rawstatus)); + ATF_CHECK(!atf_process_status_exited(&s)); + ATF_CHECK(atf_process_status_signaled(&s)); + ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGKILL); + ATF_CHECK(!atf_process_status_coredump(&s)); + atf_process_status_fini(&s); + } + + { + const int rawstatus = fork_and_wait_child(child_sigterm); + atf_process_status_t s; + RE(atf_process_status_init(&s, rawstatus)); + ATF_CHECK(!atf_process_status_exited(&s)); + ATF_CHECK(atf_process_status_signaled(&s)); + ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGTERM); + ATF_CHECK(!atf_process_status_coredump(&s)); + atf_process_status_fini(&s); + } +} + +ATF_TC(status_coredump); +ATF_TC_HEAD(status_coredump, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the status type for processes " + "that crash"); +} +ATF_TC_BODY(status_coredump, tc) +{ + struct rlimit rl; + rl.rlim_cur = RLIM_INFINITY; + rl.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &rl) == -1) + atf_tc_skip("Cannot unlimit the core file size; check limits " + "manually"); + + const int rawstatus = fork_and_wait_child(child_sigquit); + atf_process_status_t s; + RE(atf_process_status_init(&s, rawstatus)); + ATF_CHECK(!atf_process_status_exited(&s)); + ATF_CHECK(atf_process_status_signaled(&s)); + ATF_CHECK_EQ(atf_process_status_termsig(&s), SIGQUIT); + ATF_CHECK(atf_process_status_coredump(&s)); + atf_process_status_fini(&s); +} + +/* --------------------------------------------------------------------- + * Test cases for the "child" type. + * --------------------------------------------------------------------- */ + +static void child_report_pid(void *) ATF_DEFS_ATTRIBUTE_NORETURN; + +static +void +child_report_pid(void *v ATF_DEFS_ATTRIBUTE_UNUSED) +{ + const pid_t pid = getpid(); + if (write(STDOUT_FILENO, &pid, sizeof(pid)) != sizeof(pid)) + abort(); + fprintf(stderr, "Reporting %d to parent\n", (int)getpid()); + exit(EXIT_SUCCESS); +} + +ATF_TC(child_pid); +ATF_TC_HEAD(child_pid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the correctness of the pid " + "stored in the child type"); +} +ATF_TC_BODY(child_pid, tc) +{ + atf_process_stream_t outsb, errsb; + atf_process_child_t child; + atf_process_status_t status; + pid_t pid; + + RE(atf_process_stream_init_capture(&outsb)); + RE(atf_process_stream_init_inherit(&errsb)); + + RE(atf_process_fork(&child, child_report_pid, &outsb, &errsb, NULL)); + ATF_CHECK_EQ(read(atf_process_child_stdout(&child), &pid, sizeof(pid)), + sizeof(pid)); + printf("Expected PID: %d\n", (int)atf_process_child_pid(&child)); + printf("Actual PID: %d\n", (int)pid); + ATF_CHECK_EQ(atf_process_child_pid(&child), pid); + + RE(atf_process_child_wait(&child, &status)); + atf_process_status_fini(&status); + + atf_process_stream_fini(&outsb); + atf_process_stream_fini(&errsb); +} + +static +void +child_loop(void *v ATF_DEFS_ATTRIBUTE_UNUSED) +{ + for (;;) + sleep(1); +} + +static +void +nop_signal(int sig ATF_DEFS_ATTRIBUTE_UNUSED) +{ +} + +static +void +child_spawn_loop_and_wait_eintr(void *v ATF_DEFS_ATTRIBUTE_UNUSED) +{ + atf_process_child_t child; + atf_process_status_t status; + struct sigaction sighup, old_sighup; + +#define RE_ABORT(expr) \ + do { \ + atf_error_t _aux_err = expr; \ + if (atf_is_error(_aux_err)) { \ + atf_error_free(_aux_err); \ + abort(); \ + } \ + } while (0) + + { + atf_process_stream_t outsb, errsb; + + RE_ABORT(atf_process_stream_init_capture(&outsb)); + RE_ABORT(atf_process_stream_init_inherit(&errsb)); + RE_ABORT(atf_process_fork(&child, child_loop, &outsb, &errsb, NULL)); + atf_process_stream_fini(&outsb); + atf_process_stream_fini(&errsb); + } + + sighup.sa_handler = nop_signal; + sigemptyset(&sighup.sa_mask); + sighup.sa_flags = 0; + if (sigaction(SIGHUP, &sighup, &old_sighup) == -1) + abort(); + + printf("waiting\n"); + fflush(stdout); + + fprintf(stderr, "Child entering wait(2)\n"); + atf_error_t err = atf_process_child_wait(&child, &status); + fprintf(stderr, "Child's wait(2) terminated\n"); + if (!atf_is_error(err)) { + fprintf(stderr, "wait completed successfully (not interrupted)\n"); + abort(); + } + if (!atf_error_is(err, "libc")) { + fprintf(stderr, "wait did not raise libc_error\n"); + abort(); + } + if (atf_libc_error_code(err) != EINTR) { + fprintf(stderr, "libc_error is not EINTR\n"); + abort(); + } + atf_error_free(err); + + sigaction(SIGHUP, &old_sighup, NULL); + + fprintf(stderr, "Child is killing subchild\n"); + kill(atf_process_child_pid(&child), SIGTERM); + + RE_ABORT(atf_process_child_wait(&child, &status)); + atf_process_status_fini(&status); + +#undef RE_ABORT + + exit(EXIT_SUCCESS); +} + +ATF_TC(child_wait_eintr); +ATF_TC_HEAD(child_wait_eintr, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the interruption of the wait " + "method by an external signal, and the return of " + "an EINTR error"); + atf_tc_set_md_var(tc, "timeout", "30"); +} +ATF_TC_BODY(child_wait_eintr, tc) +{ + atf_process_child_t child; + atf_process_status_t status; + + { + atf_process_stream_t outsb, errsb; + + RE(atf_process_stream_init_capture(&outsb)); + RE(atf_process_stream_init_inherit(&errsb)); + RE(atf_process_fork(&child, child_spawn_loop_and_wait_eintr, + &outsb, &errsb, NULL)); + atf_process_stream_fini(&outsb); + atf_process_stream_fini(&errsb); + } + + { + /* Wait until the child process performs the wait call. This is + * racy, because the message we get from it is sent *before* + * doing the real system call... but I can't figure any other way + * to do this. */ + char buf[16]; + printf("Waiting for child to issue wait(2)\n"); + ATF_REQUIRE(read(atf_process_child_stdout(&child), buf, + sizeof(buf)) > 0); + sleep(1); + } + + printf("Interrupting child's wait(2) call\n"); + kill(atf_process_child_pid(&child), SIGHUP); + + printf("Waiting for child's completion\n"); + RE(atf_process_child_wait(&child, &status)); + ATF_REQUIRE(atf_process_status_exited(&status)); + ATF_REQUIRE_EQ(atf_process_status_exitstatus(&status), EXIT_SUCCESS); + atf_process_status_fini(&status); +} + +/* --------------------------------------------------------------------- + * Tests cases for the free functions. + * --------------------------------------------------------------------- */ + +static +void +do_exec(const atf_tc_t *tc, const char *helper_name, atf_process_status_t *s, + void (*prehook)(void)) +{ + atf_fs_path_t process_helpers; + const char *argv[3]; + + get_process_helpers_path(tc, true, &process_helpers); + + argv[0] = atf_fs_path_cstring(&process_helpers); + argv[1] = helper_name; + argv[2] = NULL; + printf("Executing %s %s\n", argv[0], argv[1]); + + RE(atf_process_exec_array(s, &process_helpers, argv, NULL, NULL, prehook)); + atf_fs_path_fini(&process_helpers); +} + +static +void +check_line(int fd, const char *exp) +{ + char *line = atf_utils_readline(fd); + ATF_CHECK(line != NULL); + ATF_CHECK_STREQ_MSG(exp, line, "read: '%s', expected: '%s'", line, exp); + free(line); +} + +ATF_TC(exec_failure); +ATF_TC_HEAD(exec_failure, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests execing a command"); +} +ATF_TC_BODY(exec_failure, tc) +{ + atf_process_status_t status; + + do_exec(tc, "exit-failure", &status, NULL); + ATF_CHECK(atf_process_status_exited(&status)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&status), EXIT_FAILURE); + atf_process_status_fini(&status); +} + +ATF_TC(exec_list); +ATF_TC_HEAD(exec_list, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests execing a command"); +} +ATF_TC_BODY(exec_list, tc) +{ + atf_fs_path_t process_helpers; + atf_list_t argv; + atf_process_status_t status; + + RE(atf_list_init(&argv)); + + get_process_helpers_path(tc, true, &process_helpers); + atf_list_append(&argv, strdup(atf_fs_path_cstring(&process_helpers)), true); + atf_list_append(&argv, strdup("echo"), true); + atf_list_append(&argv, strdup("test-message"), true); + { + atf_fs_path_t outpath; + atf_process_stream_t outsb; + + RE(atf_fs_path_init_fmt(&outpath, "stdout")); + RE(atf_process_stream_init_redirect_path(&outsb, &outpath)); + RE(atf_process_exec_list(&status, &process_helpers, &argv, &outsb, + NULL, NULL)); + atf_process_stream_fini(&outsb); + atf_fs_path_fini(&outpath); + } + atf_list_fini(&argv); + + ATF_CHECK(atf_process_status_exited(&status)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&status), EXIT_SUCCESS); + + { + int fd = open("stdout", O_RDONLY); + ATF_CHECK(fd != -1); + check_line(fd, "test-message"); + close(fd); + } + + atf_process_status_fini(&status); + atf_fs_path_fini(&process_helpers); +} + +static void +exit_early(void) +{ + exit(80); +} + +ATF_TC(exec_prehook); +ATF_TC_HEAD(exec_prehook, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests execing a command with a prehook"); +} +ATF_TC_BODY(exec_prehook, tc) +{ + atf_process_status_t status; + + do_exec(tc, "exit-success", &status, exit_early); + ATF_CHECK(atf_process_status_exited(&status)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&status), 80); + atf_process_status_fini(&status); +} + +ATF_TC(exec_success); +ATF_TC_HEAD(exec_success, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests execing a command"); +} +ATF_TC_BODY(exec_success, tc) +{ + atf_process_status_t status; + + do_exec(tc, "exit-success", &status, NULL); + ATF_CHECK(atf_process_status_exited(&status)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&status), EXIT_SUCCESS); + atf_process_status_fini(&status); +} + +static const int exit_v_null = 1; +static const int exit_v_notnull = 2; + +static +void +child_cookie(void *v) +{ + if (v == NULL) + exit(exit_v_null); + else + exit(exit_v_notnull); + + UNREACHABLE; +} + +ATF_TC(fork_cookie); +ATF_TC_HEAD(fork_cookie, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests forking a child, with " + "a null and non-null data cookie"); +} +ATF_TC_BODY(fork_cookie, tc) +{ + atf_process_stream_t outsb, errsb; + + RE(atf_process_stream_init_inherit(&outsb)); + RE(atf_process_stream_init_inherit(&errsb)); + + { + atf_process_child_t child; + atf_process_status_t status; + + RE(atf_process_fork(&child, child_cookie, &outsb, &errsb, NULL)); + RE(atf_process_child_wait(&child, &status)); + + ATF_CHECK(atf_process_status_exited(&status)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&status), exit_v_null); + + atf_process_status_fini(&status); + } + + { + atf_process_child_t child; + atf_process_status_t status; + int dummy_int; + + RE(atf_process_fork(&child, child_cookie, &outsb, &errsb, &dummy_int)); + RE(atf_process_child_wait(&child, &status)); + + ATF_CHECK(atf_process_status_exited(&status)); + ATF_CHECK_EQ(atf_process_status_exitstatus(&status), exit_v_notnull); + + atf_process_status_fini(&status); + } + + atf_process_stream_fini(&errsb); + atf_process_stream_fini(&outsb); +} + +#define TC_FORK_STREAMS(outlc, outuc, errlc, erruc) \ + ATF_TC(fork_out_ ## outlc ## _err_ ## errlc); \ + ATF_TC_HEAD(fork_out_ ## outlc ## _err_ ## errlc, tc) \ + { \ + atf_tc_set_md_var(tc, "descr", "Tests forking a child, with " \ + "stdout " #outlc " and stderr " #errlc); \ + } \ + ATF_TC_BODY(fork_out_ ## outlc ## _err_ ## errlc, tc) \ + { \ + struct outlc ## _stream out = outuc ## _STREAM(stdout_type); \ + struct errlc ## _stream err = erruc ## _STREAM(stderr_type); \ + do_fork(&out.m_base, &out, &err.m_base, &err); \ + } + +TC_FORK_STREAMS(capture, CAPTURE, capture, CAPTURE); +TC_FORK_STREAMS(capture, CAPTURE, connect, CONNECT); +TC_FORK_STREAMS(capture, CAPTURE, default, DEFAULT); +TC_FORK_STREAMS(capture, CAPTURE, inherit, INHERIT); +TC_FORK_STREAMS(capture, CAPTURE, redirect_fd, REDIRECT_FD); +TC_FORK_STREAMS(capture, CAPTURE, redirect_path, REDIRECT_PATH); +TC_FORK_STREAMS(connect, CONNECT, capture, CAPTURE); +TC_FORK_STREAMS(connect, CONNECT, connect, CONNECT); +TC_FORK_STREAMS(connect, CONNECT, default, DEFAULT); +TC_FORK_STREAMS(connect, CONNECT, inherit, INHERIT); +TC_FORK_STREAMS(connect, CONNECT, redirect_fd, REDIRECT_FD); +TC_FORK_STREAMS(connect, CONNECT, redirect_path, REDIRECT_PATH); +TC_FORK_STREAMS(default, DEFAULT, capture, CAPTURE); +TC_FORK_STREAMS(default, DEFAULT, connect, CONNECT); +TC_FORK_STREAMS(default, DEFAULT, default, DEFAULT); +TC_FORK_STREAMS(default, DEFAULT, inherit, INHERIT); +TC_FORK_STREAMS(default, DEFAULT, redirect_fd, REDIRECT_FD); +TC_FORK_STREAMS(default, DEFAULT, redirect_path, REDIRECT_PATH); +TC_FORK_STREAMS(inherit, INHERIT, capture, CAPTURE); +TC_FORK_STREAMS(inherit, INHERIT, connect, CONNECT); +TC_FORK_STREAMS(inherit, INHERIT, default, DEFAULT); +TC_FORK_STREAMS(inherit, INHERIT, inherit, INHERIT); +TC_FORK_STREAMS(inherit, INHERIT, redirect_fd, REDIRECT_FD); +TC_FORK_STREAMS(inherit, INHERIT, redirect_path, REDIRECT_PATH); +TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, capture, CAPTURE); +TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, connect, CONNECT); +TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, default, DEFAULT); +TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, inherit, INHERIT); +TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, redirect_fd, REDIRECT_FD); +TC_FORK_STREAMS(redirect_fd, REDIRECT_FD, redirect_path, REDIRECT_PATH); +TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, capture, CAPTURE); +TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, connect, CONNECT); +TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, default, DEFAULT); +TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, inherit, INHERIT); +TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, redirect_fd, REDIRECT_FD); +TC_FORK_STREAMS(redirect_path, REDIRECT_PATH, redirect_path, REDIRECT_PATH); + +#undef TC_FORK_STREAMS + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Add the tests for the "stream" type. */ + ATF_TP_ADD_TC(tp, stream_init_capture); + ATF_TP_ADD_TC(tp, stream_init_connect); + ATF_TP_ADD_TC(tp, stream_init_inherit); + ATF_TP_ADD_TC(tp, stream_init_redirect_fd); + ATF_TP_ADD_TC(tp, stream_init_redirect_path); + + /* Add the tests for the "status" type. */ + ATF_TP_ADD_TC(tp, status_exited); + ATF_TP_ADD_TC(tp, status_signaled); + ATF_TP_ADD_TC(tp, status_coredump); + + /* Add the tests for the "child" type. */ + ATF_TP_ADD_TC(tp, child_pid); + ATF_TP_ADD_TC(tp, child_wait_eintr); + + /* Add the tests for the free functions. */ + ATF_TP_ADD_TC(tp, exec_failure); + ATF_TP_ADD_TC(tp, exec_list); + ATF_TP_ADD_TC(tp, exec_prehook); + ATF_TP_ADD_TC(tp, exec_success); + ATF_TP_ADD_TC(tp, fork_cookie); + ATF_TP_ADD_TC(tp, fork_out_capture_err_capture); + ATF_TP_ADD_TC(tp, fork_out_capture_err_connect); + ATF_TP_ADD_TC(tp, fork_out_capture_err_default); + ATF_TP_ADD_TC(tp, fork_out_capture_err_inherit); + ATF_TP_ADD_TC(tp, fork_out_capture_err_redirect_fd); + ATF_TP_ADD_TC(tp, fork_out_capture_err_redirect_path); + ATF_TP_ADD_TC(tp, fork_out_connect_err_capture); + ATF_TP_ADD_TC(tp, fork_out_connect_err_connect); + ATF_TP_ADD_TC(tp, fork_out_connect_err_default); + ATF_TP_ADD_TC(tp, fork_out_connect_err_inherit); + ATF_TP_ADD_TC(tp, fork_out_connect_err_redirect_fd); + ATF_TP_ADD_TC(tp, fork_out_connect_err_redirect_path); + ATF_TP_ADD_TC(tp, fork_out_default_err_capture); + ATF_TP_ADD_TC(tp, fork_out_default_err_connect); + ATF_TP_ADD_TC(tp, fork_out_default_err_default); + ATF_TP_ADD_TC(tp, fork_out_default_err_inherit); + ATF_TP_ADD_TC(tp, fork_out_default_err_redirect_fd); + ATF_TP_ADD_TC(tp, fork_out_default_err_redirect_path); + ATF_TP_ADD_TC(tp, fork_out_inherit_err_capture); + ATF_TP_ADD_TC(tp, fork_out_inherit_err_connect); + ATF_TP_ADD_TC(tp, fork_out_inherit_err_default); + ATF_TP_ADD_TC(tp, fork_out_inherit_err_inherit); + ATF_TP_ADD_TC(tp, fork_out_inherit_err_redirect_fd); + ATF_TP_ADD_TC(tp, fork_out_inherit_err_redirect_path); + ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_capture); + ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_connect); + ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_default); + ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_inherit); + ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_redirect_fd); + ATF_TP_ADD_TC(tp, fork_out_redirect_fd_err_redirect_path); + ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_capture); + ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_connect); + ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_default); + ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_inherit); + ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_redirect_fd); + ATF_TP_ADD_TC(tp, fork_out_redirect_path_err_redirect_path); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/sanity.c b/unit/atf-src/atf-c/detail/sanity.c new file mode 100644 index 0000000..cfc8676 --- /dev/null +++ b/unit/atf-src/atf-c/detail/sanity.c @@ -0,0 +1,74 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/sanity.h" + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <err.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +static +void +fail(const char *fmt, ...) +{ + va_list ap; + char buf[4096]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + warnx("%s", buf); + warnx("%s", ""); + warnx("This is probably a bug in this application or one of the " + "libraries it uses. If you believe this problem is caused " + "by, or is related to " PACKAGE_STRING ", please report it " + "to " PACKAGE_BUGREPORT " and provide as many details as " + "possible describing how you got to this condition."); + + abort(); +} + +void +atf_sanity_inv(const char *file, int line, const char *cond) +{ + fail("Invariant check failed at %s:%d: %s", file, line, cond); +} + +void +atf_sanity_pre(const char *file, int line, const char *cond) +{ + fail("Precondition check failed at %s:%d: %s", file, line, cond); +} + +void +atf_sanity_post(const char *file, int line, const char *cond) +{ + fail("Postcondition check failed at %s:%d: %s", file, line, cond); +} diff --git a/unit/atf-src/atf-c/detail/sanity.h b/unit/atf-src/atf-c/detail/sanity.h new file mode 100644 index 0000000..33b5528 --- /dev/null +++ b/unit/atf-src/atf-c/detail/sanity.h @@ -0,0 +1,72 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_SANITY_H) +#define ATF_C_DETAIL_SANITY_H + +void atf_sanity_inv(const char *, int, const char *); +void atf_sanity_pre(const char *, int, const char *); +void atf_sanity_post(const char *, int, const char *); + +#if !defined(NDEBUG) + +#define INV(x) \ + do { \ + if (!(x)) \ + atf_sanity_inv(__FILE__, __LINE__, #x); \ + } while (0) +#define PRE(x) \ + do { \ + if (!(x)) \ + atf_sanity_pre(__FILE__, __LINE__, #x); \ + } while (0) +#define POST(x) \ + do { \ + if (!(x)) \ + atf_sanity_post(__FILE__, __LINE__, #x); \ + } while (0) + +#else /* defined(NDEBUG) */ + +#define INV(x) \ + do { \ + (void)(x); \ + } while (0) + +#define PRE(x) \ + do { \ + (void)(x); \ + } while (0) + +#define POST(x) \ + do { \ + (void)(x); \ + } while (0) + +#endif /* !defined(NDEBUG) */ + +#define UNREACHABLE INV(0) + +#endif /* !defined(ATF_C_DETAIL_SANITY_H) */ diff --git a/unit/atf-src/atf-c/detail/sanity_test.c b/unit/atf-src/atf-c/detail/sanity_test.c new file mode 100644 index 0000000..a986c32 --- /dev/null +++ b/unit/atf-src/atf-c/detail/sanity_test.c @@ -0,0 +1,230 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/sanity.h" + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <sys/types.h> +#include <sys/wait.h> + +#include <signal.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/detail/dynstr.h" +#include "atf-c/detail/process.h" +#include "atf-c/detail/test_helpers.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +enum type { inv, pre, post, unreachable }; + +struct test_data { + enum type m_type; + bool m_cond; +}; + +static void do_test_child(void *) ATF_DEFS_ATTRIBUTE_NORETURN; + +static +void +do_test_child(void *v) +{ + struct test_data *td = v; + + switch (td->m_type) { + case inv: + INV(td->m_cond); + break; + + case pre: + PRE(td->m_cond); + break; + + case post: + POST(td->m_cond); + break; + + case unreachable: + if (!td->m_cond) + UNREACHABLE; + break; + } + + exit(EXIT_SUCCESS); +} + +static +void +do_test(enum type t, bool cond) +{ + atf_process_child_t child; + atf_process_status_t status; + int nlines; + char *lines[3]; + + { + atf_process_stream_t outsb, errsb; + struct test_data td = { t, cond }; + + RE(atf_process_stream_init_inherit(&outsb)); + RE(atf_process_stream_init_capture(&errsb)); + RE(atf_process_fork(&child, do_test_child, &outsb, &errsb, &td)); + atf_process_stream_fini(&errsb); + atf_process_stream_fini(&outsb); + } + + nlines = 0; + while (nlines < 3 && (lines[nlines] = + atf_utils_readline(atf_process_child_stderr(&child))) != NULL) + nlines++; + ATF_REQUIRE(nlines == 0 || nlines == 3); + + RE(atf_process_child_wait(&child, &status)); + if (!cond) { + ATF_REQUIRE(atf_process_status_signaled(&status)); + ATF_REQUIRE(atf_process_status_termsig(&status) == SIGABRT); + } else { + ATF_REQUIRE(atf_process_status_exited(&status)); + ATF_REQUIRE(atf_process_status_exitstatus(&status) == EXIT_SUCCESS); + } + atf_process_status_fini(&status); + + if (!cond) { + switch (t) { + case inv: + ATF_REQUIRE(atf_utils_grep_string("Invariant", lines[0])); + break; + + case pre: + ATF_REQUIRE(atf_utils_grep_string("Precondition", lines[0])); + break; + + case post: + ATF_REQUIRE(atf_utils_grep_string("Postcondition", lines[0])); + break; + + case unreachable: + ATF_REQUIRE(atf_utils_grep_string("Invariant", lines[0])); + break; + } + + ATF_REQUIRE(atf_utils_grep_string(__FILE__, lines[0])); + ATF_REQUIRE(atf_utils_grep_string(PACKAGE_BUGREPORT, lines[2])); + } + + while (nlines > 0) { + nlines--; + free(lines[nlines]); + } +} + +static +void +require_ndebug(void) +{ +#if defined(NDEBUG) + atf_tc_skip("Sanity checks not available; code built with -DNDEBUG"); +#endif +} + +/* --------------------------------------------------------------------- + * Test cases for the free functions. + * --------------------------------------------------------------------- */ + +ATF_TC(inv); +ATF_TC_HEAD(inv, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the INV macro"); +} +ATF_TC_BODY(inv, tc) +{ + require_ndebug(); + + do_test(inv, false); + do_test(inv, true); +} + +ATF_TC(pre); +ATF_TC_HEAD(pre, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the PRE macro"); +} +ATF_TC_BODY(pre, tc) +{ + require_ndebug(); + + do_test(pre, false); + do_test(pre, true); +} + +ATF_TC(post); +ATF_TC_HEAD(post, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the POST macro"); +} +ATF_TC_BODY(post, tc) +{ + require_ndebug(); + + do_test(post, false); + do_test(post, true); +} + +ATF_TC(unreachable); +ATF_TC_HEAD(unreachable, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the UNREACHABLE macro"); +} +ATF_TC_BODY(unreachable, tc) +{ + require_ndebug(); + + do_test(unreachable, false); + do_test(unreachable, true); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, inv); + ATF_TP_ADD_TC(tp, pre); + ATF_TP_ADD_TC(tp, post); + ATF_TP_ADD_TC(tp, unreachable); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/test_helpers.c b/unit/atf-src/atf-c/detail/test_helpers.c new file mode 100644 index 0000000..827d46e --- /dev/null +++ b/unit/atf-src/atf-c/detail/test_helpers.c @@ -0,0 +1,147 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/test_helpers.h" + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/build.h" +#include "atf-c/check.h" +#include "atf-c/detail/dynstr.h" +#include "atf-c/detail/env.h" +#include "atf-c/detail/fs.h" +#include "atf-c/detail/process.h" +#include "atf-c/error.h" + +bool +build_check_c_o(const char *path) +{ + bool success; + atf_dynstr_t iflag; + const char *optargs[4]; + + RE(atf_dynstr_init_fmt(&iflag, "-I%s", atf_env_get_with_default( + "ATF_INCLUDEDIR", ATF_INCLUDEDIR))); + + optargs[0] = atf_dynstr_cstring(&iflag); + optargs[1] = "-Wall"; + optargs[2] = "-Werror"; + optargs[3] = NULL; + + RE(atf_check_build_c_o(path, "test.o", optargs, &success)); + + atf_dynstr_fini(&iflag); + + return success; +} + +bool +build_check_c_o_srcdir(const atf_tc_t *tc, const char *sfile) +{ + atf_fs_path_t path; + + RE(atf_fs_path_init_fmt(&path, "%s/%s", + atf_tc_get_config_var(tc, "srcdir"), sfile)); + const bool result = build_check_c_o(atf_fs_path_cstring(&path)); + atf_fs_path_fini(&path); + return result; +} + +void +header_check(const char *hdrname) +{ + FILE *srcfile; + char failmsg[128]; + + srcfile = fopen("test.c", "w"); + ATF_REQUIRE(srcfile != NULL); + fprintf(srcfile, "#include <%s>\n", hdrname); + fclose(srcfile); + + snprintf(failmsg, sizeof(failmsg), + "Header check failed; %s is not self-contained", hdrname); + + if (!build_check_c_o("test.c")) + atf_tc_fail("%s", failmsg); +} + +void +get_process_helpers_path(const atf_tc_t *tc, const bool is_detail, + atf_fs_path_t *path) +{ + RE(atf_fs_path_init_fmt(path, "%s/%sprocess_helpers", + atf_tc_get_config_var(tc, "srcdir"), + is_detail ? "" : "detail/")); +} + +struct run_h_tc_data { + atf_tc_t *m_tc; + const char *m_resname; +}; + +static +void +run_h_tc_child(void *v) +{ + struct run_h_tc_data *data = (struct run_h_tc_data *)v; + + RE(atf_tc_run(data->m_tc, data->m_resname)); +} + +/* TODO: Investigate if it's worth to add this functionality as part of + * the public API. I.e. a function to easily run a test case body in a + * subprocess. */ +void +run_h_tc(atf_tc_t *tc, const char *outname, const char *errname, + const char *resname) +{ + atf_fs_path_t outpath, errpath; + atf_process_stream_t outb, errb; + atf_process_child_t child; + atf_process_status_t status; + + RE(atf_fs_path_init_fmt(&outpath, outname)); + RE(atf_fs_path_init_fmt(&errpath, errname)); + + struct run_h_tc_data data = { tc, resname }; + + RE(atf_process_stream_init_redirect_path(&outb, &outpath)); + RE(atf_process_stream_init_redirect_path(&errb, &errpath)); + RE(atf_process_fork(&child, run_h_tc_child, &outb, &errb, &data)); + atf_process_stream_fini(&errb); + atf_process_stream_fini(&outb); + + RE(atf_process_child_wait(&child, &status)); + ATF_CHECK(atf_process_status_exited(&status)); + atf_process_status_fini(&status); + + atf_fs_path_fini(&errpath); + atf_fs_path_fini(&outpath); +} diff --git a/unit/atf-src/atf-c/detail/test_helpers.h b/unit/atf-src/atf-c/detail/test_helpers.h new file mode 100644 index 0000000..a601c29 --- /dev/null +++ b/unit/atf-src/atf-c/detail/test_helpers.h @@ -0,0 +1,75 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if defined(ATF_C_DETAIL_TEST_HELPERS_H) +# error "Cannot include test_helpers.h more than once." +#else +# define ATF_C_DETAIL_TEST_HELPERS_H +#endif + +#include <stdbool.h> + +#include <atf-c.h> + +#include <atf-c/error_fwd.h> +#include <atf-c/tc.h> + +struct atf_dynstr; +struct atf_fs_path; + +#define CE(stm) ATF_CHECK(!atf_is_error(stm)) +#define RE(stm) ATF_REQUIRE(!atf_is_error(stm)) + +#define HEADER_TC(name, hdrname) \ + ATF_TC(name); \ + ATF_TC_HEAD(name, tc) \ + { \ + atf_tc_set_md_var(tc, "descr", "Tests that the " hdrname " file can " \ + "be included on its own, without any prerequisites"); \ + } \ + ATF_TC_BODY(name, tc) \ + { \ + header_check(hdrname); \ + } + +#define BUILD_TC(name, sfile, descr, failmsg) \ + ATF_TC(name); \ + ATF_TC_HEAD(name, tc) \ + { \ + atf_tc_set_md_var(tc, "descr", descr); \ + } \ + ATF_TC_BODY(name, tc) \ + { \ + if (!build_check_c_o_srcdir(tc, sfile)) \ + atf_tc_fail("%s", failmsg); \ + } + +bool build_check_c_o(const char *); +bool build_check_c_o_srcdir(const atf_tc_t *, const char *); +void header_check(const char *); +void get_process_helpers_path(const atf_tc_t *, const bool, + struct atf_fs_path *); +bool read_line(int, struct atf_dynstr *); +void run_h_tc(atf_tc_t *, const char *, const char *, const char *); diff --git a/unit/atf-src/atf-c/detail/text.c b/unit/atf-src/atf-c/detail/text.c new file mode 100644 index 0000000..3b1d7e7 --- /dev/null +++ b/unit/atf-src/atf-c/detail/text.c @@ -0,0 +1,180 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/text.h" + +#include <errno.h> +#include <limits.h> +#include <string.h> +#include <stdlib.h> + +#include "atf-c/detail/dynstr.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/error.h" + +atf_error_t +atf_text_for_each_word(const char *instr, const char *sep, + atf_error_t (*func)(const char *, void *), + void *data) +{ + atf_error_t err; + char *str, *str2, *last; + + str = strdup(instr); + if (str == NULL) { + err = atf_no_memory_error(); + goto out; + } + + err = atf_no_error(); + str2 = strtok_r(str, sep, &last); + while (str2 != NULL && !atf_is_error(err)) { + err = func(str2, data); + str2 = strtok_r(NULL, sep, &last); + } + + free(str); +out: + return err; +} + +atf_error_t +atf_text_format(char **dest, const char *fmt, ...) +{ + atf_error_t err; + va_list ap; + + va_start(ap, fmt); + err = atf_text_format_ap(dest, fmt, ap); + va_end(ap); + + return err; +} + +atf_error_t +atf_text_format_ap(char **dest, const char *fmt, va_list ap) +{ + atf_error_t err; + atf_dynstr_t tmp; + va_list ap2; + + va_copy(ap2, ap); + err = atf_dynstr_init_ap(&tmp, fmt, ap2); + va_end(ap2); + if (!atf_is_error(err)) + *dest = atf_dynstr_fini_disown(&tmp); + + return err; +} + +atf_error_t +atf_text_split(const char *str, const char *delim, atf_list_t *words) +{ + atf_error_t err; + const char *end; + const char *iter; + + err = atf_list_init(words); + if (atf_is_error(err)) + goto err; + + end = str + strlen(str); + INV(*end == '\0'); + iter = str; + while (iter < end) { + const char *ptr; + + INV(iter != NULL); + ptr = strstr(iter, delim); + if (ptr == NULL) + ptr = end; + + INV(ptr >= iter); + if (ptr > iter) { + atf_dynstr_t word; + + err = atf_dynstr_init_raw(&word, iter, ptr - iter); + if (atf_is_error(err)) + goto err_list; + + err = atf_list_append(words, atf_dynstr_fini_disown(&word), true); + if (atf_is_error(err)) + goto err_list; + } + + iter = ptr + strlen(delim); + } + + INV(!atf_is_error(err)); + return err; + +err_list: + atf_list_fini(words); +err: + return err; +} + +atf_error_t +atf_text_to_bool(const char *str, bool *b) +{ + atf_error_t err; + + if (strcasecmp(str, "yes") == 0 || + strcasecmp(str, "true") == 0) { + *b = true; + err = atf_no_error(); + } else if (strcasecmp(str, "no") == 0 || + strcasecmp(str, "false") == 0) { + *b = false; + err = atf_no_error(); + } else { + /* XXX Not really a libc error. */ + err = atf_libc_error(EINVAL, "Cannot convert string '%s' " + "to boolean", str); + } + + return err; +} + +atf_error_t +atf_text_to_long(const char *str, long *l) +{ + atf_error_t err; + char *endptr; + long tmp; + + errno = 0; + tmp = strtol(str, &endptr, 10); + if (str[0] == '\0' || *endptr != '\0') + err = atf_libc_error(EINVAL, "'%s' is not a number", str); + else if (errno == ERANGE || (tmp == LONG_MAX || tmp == LONG_MIN)) + err = atf_libc_error(ERANGE, "'%s' is out of range", str); + else { + *l = tmp; + err = atf_no_error(); + } + + return err; +} diff --git a/unit/atf-src/atf-c/detail/text.h b/unit/atf-src/atf-c/detail/text.h new file mode 100644 index 0000000..b2820b6 --- /dev/null +++ b/unit/atf-src/atf-c/detail/text.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_TEXT_H) +#define ATF_C_DETAIL_TEXT_H + +#include <stdarg.h> +#include <stdbool.h> + +#include <atf-c/detail/list.h> +#include <atf-c/error_fwd.h> + +atf_error_t atf_text_for_each_word(const char *, const char *, + atf_error_t (*)(const char *, void *), + void *); +atf_error_t atf_text_format(char **, const char *, ...); +atf_error_t atf_text_format_ap(char **, const char *, va_list); +atf_error_t atf_text_split(const char *, const char *, atf_list_t *); +atf_error_t atf_text_to_bool(const char *, bool *); +atf_error_t atf_text_to_long(const char *, long *); + +#endif /* !defined(ATF_C_DETAIL_TEXT_H) */ diff --git a/unit/atf-src/atf-c/detail/text_test.c b/unit/atf-src/atf-c/detail/text_test.c new file mode 100644 index 0000000..5d6c8e5 --- /dev/null +++ b/unit/atf-src/atf-c/detail/text_test.c @@ -0,0 +1,421 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/text.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <atf-c.h> + +#include "atf-c/detail/sanity.h" +#include "atf-c/detail/test_helpers.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +#define REQUIRE_ERROR(exp) \ + do { \ + atf_error_t err = exp; \ + ATF_REQUIRE(atf_is_error(err)); \ + atf_error_free(err); \ + } while (0) + +static +size_t +array_size(const char *words[]) +{ + size_t count; + const char **word; + + count = 0; + for (word = words; *word != NULL; word++) + count++; + + return count; +} + +static +void +check_split(const char *str, const char *delim, const char *words[]) +{ + atf_list_t list; + const char **word; + size_t i; + + printf("Splitting '%s' with delimiter '%s'\n", str, delim); + CE(atf_text_split(str, delim, &list)); + + printf("Expecting %zd words\n", array_size(words)); + ATF_CHECK_EQ(atf_list_size(&list), array_size(words)); + + for (word = words, i = 0; *word != NULL; word++, i++) { + printf("Word at position %zd should be '%s'\n", i, words[i]); + ATF_CHECK_STREQ((const char *)atf_list_index_c(&list, i), words[i]); + } + + atf_list_fini(&list); +} + +static +atf_error_t +word_acum(const char *word, void *data) +{ + char *acum = data; + + strcat(acum, word); + + return atf_no_error(); +} + +static +atf_error_t +word_count(const char *word ATF_DEFS_ATTRIBUTE_UNUSED, void *data) +{ + size_t *counter = data; + + (*counter)++; + + return atf_no_error(); +} + +struct fail_at { + int failpos; + int curpos; +}; + +static +atf_error_t +word_fail_at(const char *word ATF_DEFS_ATTRIBUTE_UNUSED, void *data) +{ + struct fail_at *fa = data; + atf_error_t err; + + if (fa->failpos == fa->curpos) + err = atf_no_memory_error(); /* Just a random error. */ + else { + fa->curpos++; + err = atf_no_error(); + } + + return err; +} + +/* --------------------------------------------------------------------- + * Test cases for the free functions. + * --------------------------------------------------------------------- */ + +ATF_TC(for_each_word); +ATF_TC_HEAD(for_each_word, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_text_for_each_word" + "function"); +} +ATF_TC_BODY(for_each_word, tc) +{ + size_t cnt; + char acum[1024]; + + cnt = 0; + strcpy(acum, ""); + RE(atf_text_for_each_word("1 2 3", " ", word_count, &cnt)); + RE(atf_text_for_each_word("1 2 3", " ", word_acum, acum)); + ATF_REQUIRE(cnt == 3); + ATF_REQUIRE(strcmp(acum, "123") == 0); + + cnt = 0; + strcpy(acum, ""); + RE(atf_text_for_each_word("1 2 3", ".", word_count, &cnt)); + RE(atf_text_for_each_word("1 2 3", ".", word_acum, acum)); + ATF_REQUIRE(cnt == 1); + ATF_REQUIRE(strcmp(acum, "1 2 3") == 0); + + cnt = 0; + strcpy(acum, ""); + RE(atf_text_for_each_word("1 2 3 4 5", " ", word_count, &cnt)); + RE(atf_text_for_each_word("1 2 3 4 5", " ", word_acum, acum)); + ATF_REQUIRE(cnt == 5); + ATF_REQUIRE(strcmp(acum, "12345") == 0); + + cnt = 0; + strcpy(acum, ""); + RE(atf_text_for_each_word("1 2.3.4 5", " .", word_count, &cnt)); + RE(atf_text_for_each_word("1 2.3.4 5", " .", word_acum, acum)); + ATF_REQUIRE(cnt == 5); + ATF_REQUIRE(strcmp(acum, "12345") == 0); + + { + struct fail_at fa; + fa.failpos = 3; + fa.curpos = 0; + atf_error_t err = atf_text_for_each_word("a b c d e", " ", + word_fail_at, &fa); + ATF_REQUIRE(atf_is_error(err)); + ATF_REQUIRE(atf_error_is(err, "no_memory")); + ATF_REQUIRE(fa.curpos == 3); + atf_error_free(err); + } +} + +ATF_TC(format); +ATF_TC_HEAD(format, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of free-form " + "strings using a variable parameters list"); +} +ATF_TC_BODY(format, tc) +{ + char *str; + atf_error_t err; + + err = atf_text_format(&str, "%s %s %d", "Test", "string", 1); + ATF_REQUIRE(!atf_is_error(err)); + ATF_REQUIRE(strcmp(str, "Test string 1") == 0); + free(str); +} + +static +void +format_ap(char **dest, const char *fmt, ...) +{ + va_list ap; + atf_error_t err; + + va_start(ap, fmt); + err = atf_text_format_ap(dest, fmt, ap); + va_end(ap); + + ATF_REQUIRE(!atf_is_error(err)); +} + +ATF_TC(format_ap); +ATF_TC_HEAD(format_ap, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of free-form " + "strings using a va_list argument"); +} +ATF_TC_BODY(format_ap, tc) +{ + char *str; + + format_ap(&str, "%s %s %d", "Test", "string", 1); + ATF_REQUIRE(strcmp(str, "Test string 1") == 0); + free(str); +} + +ATF_TC(split); +ATF_TC_HEAD(split, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the split function"); +} +ATF_TC_BODY(split, tc) +{ + { + const char *words[] = { NULL }; + check_split("", " ", words); + } + + { + const char *words[] = { NULL }; + check_split(" ", " ", words); + } + + { + const char *words[] = { NULL }; + check_split(" ", " ", words); + } + + { + const char *words[] = { "a", "b", NULL }; + check_split("a b", " ", words); + } + + { + const char *words[] = { "a", "b", "c", "d", NULL }; + check_split("a b c d", " ", words); + } + + { + const char *words[] = { "foo", "bar", NULL }; + check_split("foo bar", " ", words); + } + + { + const char *words[] = { "foo", "bar", "baz", "foobar", NULL }; + check_split("foo bar baz foobar", " ", words); + } + + { + const char *words[] = { "foo", "bar", NULL }; + check_split(" foo bar", " ", words); + } + + { + const char *words[] = { "foo", "bar", NULL }; + check_split("foo bar", " ", words); + } + + { + const char *words[] = { "foo", "bar", NULL }; + check_split("foo bar ", " ", words); + } + + { + const char *words[] = { "foo", "bar", NULL }; + check_split(" foo bar ", " ", words); + } +} + +ATF_TC(split_delims); +ATF_TC_HEAD(split_delims, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the split function using " + "different delimiters"); +} +ATF_TC_BODY(split_delims, tc) +{ + + { + const char *words[] = { NULL }; + check_split("", "/", words); + } + + { + const char *words[] = { " ", NULL }; + check_split(" ", "/", words); + } + + { + const char *words[] = { " ", NULL }; + check_split(" ", "/", words); + } + + { + const char *words[] = { "a", "b", NULL }; + check_split("a/b", "/", words); + } + + { + const char *words[] = { "a", "bcd", "ef", NULL }; + check_split("aLONGDELIMbcdLONGDELIMef", "LONGDELIM", words); + } +} + +ATF_TC(to_bool); +ATF_TC_HEAD(to_bool, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_text_to_bool function"); +} +ATF_TC_BODY(to_bool, tc) +{ + bool b; + + RE(atf_text_to_bool("true", &b)); ATF_REQUIRE(b); + RE(atf_text_to_bool("TRUE", &b)); ATF_REQUIRE(b); + RE(atf_text_to_bool("yes", &b)); ATF_REQUIRE(b); + RE(atf_text_to_bool("YES", &b)); ATF_REQUIRE(b); + + RE(atf_text_to_bool("false", &b)); ATF_REQUIRE(!b); + RE(atf_text_to_bool("FALSE", &b)); ATF_REQUIRE(!b); + RE(atf_text_to_bool("no", &b)); ATF_REQUIRE(!b); + RE(atf_text_to_bool("NO", &b)); ATF_REQUIRE(!b); + + b = false; + REQUIRE_ERROR(atf_text_to_bool("", &b)); + ATF_REQUIRE(!b); + b = true; + REQUIRE_ERROR(atf_text_to_bool("", &b)); + ATF_REQUIRE(b); + + b = false; + REQUIRE_ERROR(atf_text_to_bool("tru", &b)); + ATF_REQUIRE(!b); + b = true; + REQUIRE_ERROR(atf_text_to_bool("tru", &b)); + ATF_REQUIRE(b); + + b = false; + REQUIRE_ERROR(atf_text_to_bool("true2", &b)); + ATF_REQUIRE(!b); + b = true; + REQUIRE_ERROR(atf_text_to_bool("true2", &b)); + ATF_REQUIRE(b); + + b = false; + REQUIRE_ERROR(atf_text_to_bool("fals", &b)); + ATF_REQUIRE(!b); + b = true; + REQUIRE_ERROR(atf_text_to_bool("fals", &b)); + ATF_REQUIRE(b); + + b = false; + REQUIRE_ERROR(atf_text_to_bool("false2", &b)); + ATF_REQUIRE(!b); + b = true; + REQUIRE_ERROR(atf_text_to_bool("false2", &b)); + ATF_REQUIRE(b); +} + +ATF_TC(to_long); +ATF_TC_HEAD(to_long, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the atf_text_to_long function"); +} +ATF_TC_BODY(to_long, tc) +{ + long l; + + RE(atf_text_to_long("0", &l)); ATF_REQUIRE_EQ(l, 0); + RE(atf_text_to_long("-5", &l)); ATF_REQUIRE_EQ(l, -5); + RE(atf_text_to_long("5", &l)); ATF_REQUIRE_EQ(l, 5); + RE(atf_text_to_long("123456789", &l)); ATF_REQUIRE_EQ(l, 123456789); + + l = 1212; + REQUIRE_ERROR(atf_text_to_long("", &l)); + ATF_REQUIRE_EQ(l, 1212); + REQUIRE_ERROR(atf_text_to_long("foo", &l)); + ATF_REQUIRE_EQ(l, 1212); + REQUIRE_ERROR(atf_text_to_long("1234x", &l)); + ATF_REQUIRE_EQ(l, 1212); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, for_each_word); + ATF_TP_ADD_TC(tp, format); + ATF_TP_ADD_TC(tp, format_ap); + ATF_TP_ADD_TC(tp, split); + ATF_TP_ADD_TC(tp, split_delims); + ATF_TP_ADD_TC(tp, to_bool); + ATF_TP_ADD_TC(tp, to_long); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/tp_main.c b/unit/atf-src/atf-c/detail/tp_main.c new file mode 100644 index 0000000..229615b --- /dev/null +++ b/unit/atf-src/atf-c/detail/tp_main.c @@ -0,0 +1,615 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "atf-c/detail/dynstr.h" +#include "atf-c/detail/env.h" +#include "atf-c/detail/fs.h" +#include "atf-c/detail/map.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/error.h" +#include "atf-c/tc.h" +#include "atf-c/tp.h" +#include "atf-c/utils.h" + +#if defined(HAVE_GNU_GETOPT) +# define GETOPT_POSIX "+" +#else +# define GETOPT_POSIX "" +#endif + +static const char *progname = NULL; + +/* This prototype is provided by macros.h during instantiation of the test + * program, so it can be kept private. Don't know if that's the best idea + * though. */ +int atf_tp_main(int, char **, atf_error_t (*)(atf_tp_t *)); + +enum tc_part { + BODY, + CLEANUP, +}; + +/* --------------------------------------------------------------------- + * The "usage" and "user" error types. + * --------------------------------------------------------------------- */ + +#define FREE_FORM_ERROR(name) \ + struct name ## _error_data { \ + char m_what[2048]; \ + }; \ + \ + static \ + void \ + name ## _format(const atf_error_t err, char *buf, size_t buflen) \ + { \ + const struct name ## _error_data *data; \ + \ + PRE(atf_error_is(err, #name)); \ + \ + data = atf_error_data(err); \ + snprintf(buf, buflen, "%s", data->m_what); \ + } \ + \ + static \ + atf_error_t \ + name ## _error(const char *fmt, ...) \ + { \ + atf_error_t err; \ + struct name ## _error_data data; \ + va_list ap; \ + \ + va_start(ap, fmt); \ + vsnprintf(data.m_what, sizeof(data.m_what), fmt, ap); \ + va_end(ap); \ + \ + err = atf_error_new(#name, &data, sizeof(data), name ## _format); \ + \ + return err; \ + } + +FREE_FORM_ERROR(usage); +FREE_FORM_ERROR(user); + +/* --------------------------------------------------------------------- + * Printing functions. + * --------------------------------------------------------------------- */ + +static +void +print_error(const atf_error_t err) +{ + char buf[4096]; + + PRE(atf_is_error(err)); + + atf_error_format(err, buf, sizeof(buf)); + fprintf(stderr, "%s: ERROR: %s\n", progname, buf); + + if (atf_error_is(err, "usage")) + fprintf(stderr, "%s: See atf-test-program(1) for usage details.\n", + progname); +} + +static +void +print_warning(const char *message) +{ + fprintf(stderr, "%s: WARNING: %s\n", progname, message); +} + +/* --------------------------------------------------------------------- + * Options handling. + * --------------------------------------------------------------------- */ + +struct params { + bool m_do_list; + atf_fs_path_t m_srcdir; + char *m_tcname; + enum tc_part m_tcpart; + atf_fs_path_t m_resfile; + atf_map_t m_config; +}; + +static +atf_error_t +argv0_to_dir(const char *argv0, atf_fs_path_t *dir) +{ + atf_error_t err; + atf_fs_path_t temp; + + err = atf_fs_path_init_fmt(&temp, "%s", argv0); + if (atf_is_error(err)) + goto out; + + err = atf_fs_path_branch_path(&temp, dir); + + atf_fs_path_fini(&temp); +out: + return err; +} + +static +atf_error_t +params_init(struct params *p, const char *argv0) +{ + atf_error_t err; + + p->m_do_list = false; + p->m_tcname = NULL; + p->m_tcpart = BODY; + + err = argv0_to_dir(argv0, &p->m_srcdir); + if (atf_is_error(err)) + return err; + + err = atf_fs_path_init_fmt(&p->m_resfile, "/dev/stdout"); + if (atf_is_error(err)) { + atf_fs_path_fini(&p->m_srcdir); + return err; + } + + err = atf_map_init(&p->m_config); + if (atf_is_error(err)) { + atf_fs_path_fini(&p->m_resfile); + atf_fs_path_fini(&p->m_srcdir); + return err; + } + + return err; +} + +static +void +params_fini(struct params *p) +{ + atf_map_fini(&p->m_config); + atf_fs_path_fini(&p->m_resfile); + atf_fs_path_fini(&p->m_srcdir); + if (p->m_tcname != NULL) + free(p->m_tcname); +} + +static +atf_error_t +parse_vflag(char *arg, atf_map_t *config) +{ + atf_error_t err; + char *split; + + split = strchr(arg, '='); + if (split == NULL) { + err = usage_error("-v requires an argument of the form var=value"); + goto out; + } + + *split = '\0'; + split++; + + err = atf_map_insert(config, arg, split, false); + +out: + return err; +} + +static +atf_error_t +replace_path_param(atf_fs_path_t *param, const char *value) +{ + atf_error_t err; + atf_fs_path_t temp; + + err = atf_fs_path_init_fmt(&temp, "%s", value); + if (!atf_is_error(err)) { + atf_fs_path_fini(param); + *param = temp; + } + + return err; +} + +/* --------------------------------------------------------------------- + * Test case listing. + * --------------------------------------------------------------------- */ + +static +void +list_tcs(const atf_tp_t *tp) +{ + const atf_tc_t *const *tcs; + const atf_tc_t *const *tcsptr; + + printf("Content-Type: application/X-atf-tp; version=\"1\"\n\n"); + + tcs = atf_tp_get_tcs(tp); + INV(tcs != NULL); /* Should be checked. */ + for (tcsptr = tcs; *tcsptr != NULL; tcsptr++) { + const atf_tc_t *tc = *tcsptr; + char **vars = atf_tc_get_md_vars(tc); + char **ptr; + + INV(vars != NULL); /* Should be checked. */ + + if (tcsptr != tcs) /* Not first. */ + printf("\n"); + + for (ptr = vars; *ptr != NULL; ptr += 2) { + if (strcmp(*ptr, "ident") == 0) { + printf("ident: %s\n", *(ptr + 1)); + break; + } + } + + for (ptr = vars; *ptr != NULL; ptr += 2) { + if (strcmp(*ptr, "ident") != 0) { + printf("%s: %s\n", *ptr, *(ptr + 1)); + } + } + + atf_utils_free_charpp(vars); + } +#define UNCONST(a) ((void *)(unsigned long)(const void *)(a)) + free(UNCONST(tcs)); +#undef UNCONST +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +static +atf_error_t +handle_tcarg(const char *tcarg, char **tcname, enum tc_part *tcpart) +{ + atf_error_t err; + + err = atf_no_error(); + + *tcname = strdup(tcarg); + if (*tcname == NULL) { + err = atf_no_memory_error(); + goto out; + } + + char *delim = strchr(*tcname, ':'); + if (delim != NULL) { + *delim = '\0'; + + delim++; + if (strcmp(delim, "body") == 0) { + *tcpart = BODY; + } else if (strcmp(delim, "cleanup") == 0) { + *tcpart = CLEANUP; + } else { + err = usage_error("Invalid test case part `%s'", delim); + goto out; + } + } + +out: + return err; +} + +static +atf_error_t +process_params(int argc, char **argv, struct params *p) +{ + atf_error_t err; + int ch; + int old_opterr; + + err = params_init(p, argv[0]); + if (atf_is_error(err)) + goto out; + + old_opterr = opterr; + opterr = 0; + while (!atf_is_error(err) && + (ch = getopt(argc, argv, GETOPT_POSIX ":lr:s:v:")) != -1) { + switch (ch) { + case 'l': + p->m_do_list = true; + break; + + case 'r': + err = replace_path_param(&p->m_resfile, optarg); + break; + + case 's': + err = replace_path_param(&p->m_srcdir, optarg); + break; + + case 'v': + err = parse_vflag(optarg, &p->m_config); + break; + + case ':': + err = usage_error("Option -%c requires an argument.", optopt); + break; + + case '?': + default: + err = usage_error("Unknown option -%c.", optopt); + } + } + argc -= optind; + argv += optind; + + /* Clear getopt state just in case the test wants to use it. */ + opterr = old_opterr; + optind = 1; +#if defined(HAVE_OPTRESET) + optreset = 1; +#endif + + if (!atf_is_error(err)) { + if (p->m_do_list) { + if (argc > 0) + err = usage_error("Cannot provide test case names with -l"); + } else { + if (argc == 0) + err = usage_error("Must provide a test case name"); + else if (argc == 1) + err = handle_tcarg(argv[0], &p->m_tcname, &p->m_tcpart); + else if (argc > 1) { + err = usage_error("Cannot provide more than one test case " + "name"); + } + } + } + + if (atf_is_error(err)) + params_fini(p); + +out: + return err; +} + +static +atf_error_t +srcdir_strip_libtool(atf_fs_path_t *srcdir) +{ + atf_error_t err; + atf_fs_path_t parent; + + err = atf_fs_path_branch_path(srcdir, &parent); + if (atf_is_error(err)) + goto out; + + atf_fs_path_fini(srcdir); + *srcdir = parent; + + INV(!atf_is_error(err)); +out: + return err; +} + +static +atf_error_t +handle_srcdir(struct params *p) +{ + atf_error_t err; + atf_dynstr_t leafname; + atf_fs_path_t exe, srcdir; + bool b; + + err = atf_fs_path_copy(&srcdir, &p->m_srcdir); + if (atf_is_error(err)) + goto out; + + if (!atf_fs_path_is_absolute(&srcdir)) { + atf_fs_path_t srcdirabs; + + err = atf_fs_path_to_absolute(&srcdir, &srcdirabs); + if (atf_is_error(err)) + goto out_srcdir; + + atf_fs_path_fini(&srcdir); + srcdir = srcdirabs; + } + + err = atf_fs_path_leaf_name(&srcdir, &leafname); + if (atf_is_error(err)) + goto out_srcdir; + else { + const bool libs = atf_equal_dynstr_cstring(&leafname, ".libs"); + atf_dynstr_fini(&leafname); + + if (libs) { + err = srcdir_strip_libtool(&srcdir); + if (atf_is_error(err)) + goto out; + } + } + + err = atf_fs_path_copy(&exe, &srcdir); + if (atf_is_error(err)) + goto out_srcdir; + + err = atf_fs_path_append_fmt(&exe, "%s", progname); + if (atf_is_error(err)) + goto out_exe; + + err = atf_fs_exists(&exe, &b); + if (!atf_is_error(err)) { + if (b) { + err = atf_map_insert(&p->m_config, "srcdir", + strdup(atf_fs_path_cstring(&srcdir)), true); + } else { + err = user_error("Cannot find the test program in the source " + "directory `%s'", atf_fs_path_cstring(&srcdir)); + } + } + +out_exe: + atf_fs_path_fini(&exe); +out_srcdir: + atf_fs_path_fini(&srcdir); +out: + return err; +} + +static +atf_error_t +run_tc(const atf_tp_t *tp, struct params *p, int *exitcode) +{ + atf_error_t err; + + err = atf_no_error(); + + if (!atf_tp_has_tc(tp, p->m_tcname)) { + err = usage_error("Unknown test case `%s'", p->m_tcname); + goto out; + } + + if (!atf_env_has("__RUNNING_INSIDE_ATF_RUN") || strcmp(atf_env_get( + "__RUNNING_INSIDE_ATF_RUN"), "internal-yes-value") != 0) + { + print_warning("Running test cases outside of kyua(1) is unsupported"); + print_warning("No isolation nor timeout control is being applied; you " + "may get unexpected failures; see atf-test-case(4)"); + } + + switch (p->m_tcpart) { + case BODY: + err = atf_tp_run(tp, p->m_tcname, atf_fs_path_cstring(&p->m_resfile)); + if (atf_is_error(err)) { + /* TODO: Handle error */ + *exitcode = EXIT_FAILURE; + atf_error_free(err); + } else { + *exitcode = EXIT_SUCCESS; + } + + break; + + case CLEANUP: + err = atf_tp_cleanup(tp, p->m_tcname); + if (atf_is_error(err)) { + /* TODO: Handle error */ + *exitcode = EXIT_FAILURE; + atf_error_free(err); + } else { + *exitcode = EXIT_SUCCESS; + } + + break; + + default: + UNREACHABLE; + } + + INV(!atf_is_error(err)); +out: + return err; +} + +static +atf_error_t +controlled_main(int argc, char **argv, + atf_error_t (*add_tcs_hook)(atf_tp_t *), + int *exitcode) +{ + atf_error_t err; + struct params p; + atf_tp_t tp; + char **raw_config; + + err = process_params(argc, argv, &p); + if (atf_is_error(err)) + goto out; + + err = handle_srcdir(&p); + if (atf_is_error(err)) + goto out_p; + + raw_config = atf_map_to_charpp(&p.m_config); + if (raw_config == NULL) { + err = atf_no_memory_error(); + goto out_p; + } + err = atf_tp_init(&tp, (const char* const*)raw_config); + atf_utils_free_charpp(raw_config); + if (atf_is_error(err)) + goto out_p; + + err = add_tcs_hook(&tp); + if (atf_is_error(err)) + goto out_tp; + + if (p.m_do_list) { + list_tcs(&tp); + INV(!atf_is_error(err)); + *exitcode = EXIT_SUCCESS; + } else { + err = run_tc(&tp, &p, exitcode); + } + +out_tp: + atf_tp_fini(&tp); +out_p: + params_fini(&p); +out: + return err; +} + +int +atf_tp_main(int argc, char **argv, atf_error_t (*add_tcs_hook)(atf_tp_t *)) +{ + atf_error_t err; + int exitcode; + + progname = strrchr(argv[0], '/'); + if (progname == NULL) + progname = argv[0]; + else + progname++; + + /* Libtool workaround: if running from within the source tree (binaries + * that are not installed yet), skip the "lt-" prefix added to files in + * the ".libs" directory to show the real (not temporary) name. */ + if (strncmp(progname, "lt-", 3) == 0) + progname += 3; + + exitcode = EXIT_FAILURE; /* Silence GCC warning. */ + err = controlled_main(argc, argv, add_tcs_hook, &exitcode); + if (atf_is_error(err)) { + print_error(err); + atf_error_free(err); + exitcode = EXIT_FAILURE; + } + + return exitcode; +} diff --git a/unit/atf-src/atf-c/detail/user.c b/unit/atf-src/atf-c/detail/user.c new file mode 100644 index 0000000..bbb4ded --- /dev/null +++ b/unit/atf-src/atf-c/detail/user.c @@ -0,0 +1,75 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/user.h" + +#include <sys/param.h> +#include <sys/types.h> +#include <limits.h> +#include <unistd.h> + +#include "atf-c/detail/sanity.h" + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +uid_t +atf_user_euid(void) +{ + return geteuid(); +} + +bool +atf_user_is_member_of_group(gid_t gid) +{ + static gid_t groups[NGROUPS_MAX]; + static int ngroups = -1; + bool found; + int i; + + if (ngroups == -1) { + ngroups = getgroups(NGROUPS_MAX, groups); + INV(ngroups >= 0); + } + + found = false; + for (i = 0; !found && i < ngroups; i++) + if (groups[i] == gid) + found = true; + return found; +} + +bool +atf_user_is_root(void) +{ + return geteuid() == 0; +} + +bool +atf_user_is_unprivileged(void) +{ + return geteuid() != 0; +} diff --git a/unit/atf-src/atf-c/detail/user.h b/unit/atf-src/atf-c/detail/user.h new file mode 100644 index 0000000..14a3e96 --- /dev/null +++ b/unit/atf-src/atf-c/detail/user.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_DETAIL_USER_H) +#define ATF_C_DETAIL_USER_H + +#include <sys/types.h> + +#include <stdbool.h> + +/* TODO: Would be nice to have an atf_user_t type and transform all of + * the functions below to methods. */ + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +uid_t atf_user_euid(void); +bool atf_user_is_member_of_group(gid_t); +bool atf_user_is_root(void); +bool atf_user_is_unprivileged(void); + +#endif /* !defined(ATF_C_DETAIL_USER_H) */ diff --git a/unit/atf-src/atf-c/detail/user_test.c b/unit/atf-src/atf-c/detail/user_test.c new file mode 100644 index 0000000..6a0f6e1 --- /dev/null +++ b/unit/atf-src/atf-c/detail/user_test.c @@ -0,0 +1,146 @@ +/* Copyright (c) 2007 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/detail/user.h" + +#include <sys/param.h> +#include <sys/types.h> + +#include <limits.h> +#include <stdio.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" + +/* --------------------------------------------------------------------- + * Test cases for the free functions. + * --------------------------------------------------------------------- */ + +ATF_TC(euid); +ATF_TC_HEAD(euid, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_user_euid function"); +} +ATF_TC_BODY(euid, tc) +{ + ATF_REQUIRE_EQ(atf_user_euid(), geteuid()); +} + +ATF_TC(is_member_of_group); +ATF_TC_HEAD(is_member_of_group, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_user_is_member_of_group " + "function"); +} +ATF_TC_BODY(is_member_of_group, tc) +{ + gid_t gids[NGROUPS_MAX]; + gid_t g, maxgid; + int ngids; + const gid_t maxgid_limit = 1 << 16; + + { + int i; + + ngids = getgroups(NGROUPS_MAX, gids); + if (ngids == -1) + atf_tc_fail("Call to getgroups failed"); + maxgid = 0; + for (i = 0; i < ngids; i++) { + printf("User group %d is %u\n", i, gids[i]); + if (maxgid < gids[i]) + maxgid = gids[i]; + } + printf("User belongs to %d groups\n", ngids); + printf("Last GID is %u\n", maxgid); + } + + if (maxgid > maxgid_limit) { + printf("Test truncated from %u groups to %u to keep the run time " + "reasonable enough\n", maxgid, maxgid_limit); + maxgid = maxgid_limit; + } + + for (g = 0; g < maxgid; g++) { + bool found = false; + int i; + + for (i = 0; !found && i < ngids; i++) { + if (gids[i] == g) + found = true; + } + + if (found) { + printf("Checking if user belongs to group %d\n", g); + ATF_REQUIRE(atf_user_is_member_of_group(g)); + } else { + printf("Checking if user does not belong to group %d\n", g); + ATF_REQUIRE(!atf_user_is_member_of_group(g)); + } + } +} + +ATF_TC(is_root); +ATF_TC_HEAD(is_root, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_user_is_root function"); +} +ATF_TC_BODY(is_root, tc) +{ + if (geteuid() == 0) + ATF_REQUIRE(atf_user_is_root()); + else + ATF_REQUIRE(!atf_user_is_root()); +} + +ATF_TC(is_unprivileged); +ATF_TC_HEAD(is_unprivileged, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_user_is_unprivileged " + "function"); +} +ATF_TC_BODY(is_unprivileged, tc) +{ + if (geteuid() != 0) + ATF_REQUIRE(atf_user_is_unprivileged()); + else + ATF_REQUIRE(!atf_user_is_unprivileged()); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, euid); + ATF_TP_ADD_TC(tp, is_member_of_group); + ATF_TP_ADD_TC(tp, is_root); + ATF_TP_ADD_TC(tp, is_unprivileged); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/detail/version_helper.c b/unit/atf-src/atf-c/detail/version_helper.c new file mode 100644 index 0000000..fd48466 --- /dev/null +++ b/unit/atf-src/atf-c/detail/version_helper.c @@ -0,0 +1,43 @@ +/* + * Copyright 2014 Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Google Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(HAVE_CONFIG_H) +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> + +int +main(void) +{ + printf("%s\n", PACKAGE_VERSION); + return EXIT_SUCCESS; +} diff --git a/unit/atf-src/atf-c/error.c b/unit/atf-src/atf-c/error.c new file mode 100644 index 0000000..071daf6 --- /dev/null +++ b/unit/atf-src/atf-c/error.c @@ -0,0 +1,263 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/error.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "atf-c/detail/sanity.h" + +/* Theoretically, there can only be a single error intance at any given + * point in time, because errors are raised at one point and must be + * handled immediately. If another error has to be raised during the + * handling process, something else has to be done with the previous + * error. + * + * This is per-thread information and will break threaded tests, but we + * currently do not have any threading support; therefore, this is fine. */ +static bool error_on_flight = false; + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +void +error_format(const atf_error_t err, char *buf, size_t buflen) +{ + PRE(err != NULL); + snprintf(buf, buflen, "Error '%s'", err->m_type); +} + +static +bool +error_init(atf_error_t err, const char *type, void *data, size_t datalen, + void (*format)(const atf_error_t, char *, size_t)) +{ + bool ok; + + PRE(data != NULL || datalen == 0); + PRE(datalen != 0 || data == NULL); + + err->m_free = false; + err->m_type = type; + err->m_format = (format == NULL) ? error_format : format; + + ok = true; + if (data == NULL) { + err->m_data = NULL; + } else { + err->m_data = malloc(datalen); + if (err->m_data == NULL) { + ok = false; + } else + memcpy(err->m_data, data, datalen); + } + + return ok; +} + +/* --------------------------------------------------------------------- + * The "atf_error" type. + * --------------------------------------------------------------------- */ + +atf_error_t +atf_error_new(const char *type, void *data, size_t datalen, + void (*format)(const atf_error_t, char *, size_t)) +{ + atf_error_t err; + + PRE(!error_on_flight); + PRE(data != NULL || datalen == 0); + PRE(datalen != 0 || data == NULL); + + err = malloc(sizeof(*err)); + if (err == NULL) + err = atf_no_memory_error(); + else { + if (!error_init(err, type, data, datalen, format)) { + free(err); + err = atf_no_memory_error(); + } else { + err->m_free = true; + error_on_flight = true; + } + } + + INV(err != NULL); + POST(error_on_flight); + return err; +} + +void +atf_error_free(atf_error_t err) +{ + bool freeit; + + PRE(error_on_flight); + PRE(err != NULL); + + freeit = err->m_free; + + if (err->m_data != NULL) + free(err->m_data); + + if (freeit) + free(err); + + error_on_flight = false; +} + +atf_error_t +atf_no_error(void) +{ + return NULL; +} + +bool +atf_is_error(const atf_error_t err) +{ + return err != NULL; +} + +bool +atf_error_is(const atf_error_t err, const char *type) +{ + PRE(err != NULL); + + return strcmp(err->m_type, type) == 0; +} + +const void * +atf_error_data(const atf_error_t err) +{ + PRE(err != NULL); + + return err->m_data; +} + +void +atf_error_format(const atf_error_t err, char *buf, size_t buflen) +{ + PRE(err != NULL); + err->m_format(err, buf, buflen); +} + +/* --------------------------------------------------------------------- + * Common error types. + * --------------------------------------------------------------------- */ + +/* + * The "libc" error. + */ + +struct atf_libc_error_data { + int m_errno; + char m_what[4096]; +}; +typedef struct atf_libc_error_data atf_libc_error_data_t; + +static +void +libc_format(const atf_error_t err, char *buf, size_t buflen) +{ + const atf_libc_error_data_t *data; + + PRE(atf_error_is(err, "libc")); + + data = atf_error_data(err); + snprintf(buf, buflen, "%s: %s", data->m_what, strerror(data->m_errno)); +} + +atf_error_t +atf_libc_error(int syserrno, const char *fmt, ...) +{ + atf_error_t err; + atf_libc_error_data_t data; + va_list ap; + + data.m_errno = syserrno; + va_start(ap, fmt); + vsnprintf(data.m_what, sizeof(data.m_what), fmt, ap); + va_end(ap); + + err = atf_error_new("libc", &data, sizeof(data), libc_format); + + return err; +} + +int +atf_libc_error_code(const atf_error_t err) +{ + const struct atf_libc_error_data *data; + + PRE(atf_error_is(err, "libc")); + + data = atf_error_data(err); + + return data->m_errno; +} + +const char * +atf_libc_error_msg(const atf_error_t err) +{ + const struct atf_libc_error_data *data; + + PRE(atf_error_is(err, "libc")); + + data = atf_error_data(err); + + return data->m_what; +} + +/* + * The "no_memory" error. + */ + +static struct atf_error no_memory_error; + +static +void +no_memory_format(const atf_error_t err, char *buf, size_t buflen) +{ + PRE(atf_error_is(err, "no_memory")); + + snprintf(buf, buflen, "Not enough memory"); +} + +atf_error_t +atf_no_memory_error(void) +{ + PRE(!error_on_flight); + + error_init(&no_memory_error, "no_memory", NULL, 0, + no_memory_format); + + error_on_flight = true; + return &no_memory_error; +} diff --git a/unit/atf-src/atf-c/error.h b/unit/atf-src/atf-c/error.h new file mode 100644 index 0000000..260c0d0 --- /dev/null +++ b/unit/atf-src/atf-c/error.h @@ -0,0 +1,67 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_ERROR_H) +#define ATF_C_ERROR_H + +#include <atf-c/error_fwd.h> + +#include <stdbool.h> +#include <stddef.h> + +/* --------------------------------------------------------------------- + * The "atf_error" type. + * --------------------------------------------------------------------- */ + +struct atf_error { + bool m_free; + const char *m_type; + void *m_data; + + void (*m_format)(struct atf_error *, char *, size_t); +}; + +atf_error_t atf_error_new(const char *, void *, size_t, + void (*)(const atf_error_t, char *, size_t)); +void atf_error_free(atf_error_t); + +atf_error_t atf_no_error(void); +bool atf_is_error(const atf_error_t); + +bool atf_error_is(const atf_error_t, const char *); +const void *atf_error_data(const atf_error_t); +void atf_error_format(const atf_error_t, char *, size_t); + +/* --------------------------------------------------------------------- + * Common error types. + * --------------------------------------------------------------------- */ + +atf_error_t atf_libc_error(int, const char *, ...); +int atf_libc_error_code(const atf_error_t); +const char *atf_libc_error_msg(const atf_error_t); + +atf_error_t atf_no_memory_error(void); + +#endif /* !defined(ATF_C_ERROR_H) */ diff --git a/unit/atf-src/atf-c/error_fwd.h b/unit/atf-src/atf-c/error_fwd.h new file mode 100644 index 0000000..d73e164 --- /dev/null +++ b/unit/atf-src/atf-c/error_fwd.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_ERROR_FWD_H) +#define ATF_C_ERROR_FWD_H + +/* --------------------------------------------------------------------- + * The "atf_error" type. + * --------------------------------------------------------------------- */ + +struct atf_error; +typedef struct atf_error *atf_error_t; + +#endif /* !defined(ATF_C_ERROR_FWD_H) */ diff --git a/unit/atf-src/atf-c/error_test.c b/unit/atf-src/atf-c/error_test.c new file mode 100644 index 0000000..8bfa124 --- /dev/null +++ b/unit/atf-src/atf-c/error_test.c @@ -0,0 +1,297 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/error.h" + +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <atf-c.h> + +#include "atf-c/defs.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +void +test_format(const atf_error_t err ATF_DEFS_ATTRIBUTE_UNUSED, + char *buf, size_t buflen) +{ + snprintf(buf, buflen, "Test formatting function"); +} + +/* --------------------------------------------------------------------- + * Tests for the "atf_error" type. + * --------------------------------------------------------------------- */ + +ATF_TC(error_new); +ATF_TC_HEAD(error_new, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of an error " + "object"); +} +ATF_TC_BODY(error_new, tc) +{ + atf_error_t err; + int data; + + err = atf_error_new("test_error", NULL, 0, NULL); + ATF_REQUIRE(atf_error_is(err, "test_error")); + ATF_REQUIRE(!atf_error_is(err, "unknown_error")); + ATF_REQUIRE(atf_error_data(err) == NULL); + atf_error_free(err); + + data = 5; + err = atf_error_new("test_data_error", &data, sizeof(data), NULL); + ATF_REQUIRE(atf_error_is(err, "test_data_error")); + ATF_REQUIRE(!atf_error_is(err, "unknown_error")); + ATF_REQUIRE(atf_error_data(err) != NULL); + ATF_REQUIRE_EQ(*((const int *)atf_error_data(err)), 5); + atf_error_free(err); +} + +ATF_TC(error_new_wo_memory); +ATF_TC_HEAD(error_new_wo_memory, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that an unavailable memory error " + "raised when constructing an error object " + "is properly converted to the no_memory " + "static error type"); +} +ATF_TC_BODY(error_new_wo_memory, tc) +{ + atf_error_t err; + void *invalid; + + invalid = (void *)1; + + err = atf_error_new("test_error", invalid, SIZE_MAX, NULL); + ATF_REQUIRE(atf_error_is(err, "no_memory")); + ATF_REQUIRE(atf_error_data(err) == NULL); + atf_error_free(err); +} + +ATF_TC(no_error); +ATF_TC_HEAD(no_error, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks that constructing a non-error " + "object works"); +} +ATF_TC_BODY(no_error, tc) +{ + atf_error_t err; + + err = atf_no_error(); + ATF_REQUIRE(!atf_is_error(err)); +} + +ATF_TC(is_error); +ATF_TC_HEAD(is_error, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the is_error method to determine " + "if an error object holds success or an error"); +} +ATF_TC_BODY(is_error, tc) +{ + atf_error_t err; + + err = atf_no_error(); + ATF_REQUIRE(!atf_is_error(err)); + + err = atf_error_new("test_error", NULL, 0, NULL); + ATF_REQUIRE(atf_is_error(err)); + atf_error_free(err); +} + +ATF_TC(format); +ATF_TC_HEAD(format, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the default formatting function " + "and the ability to change it"); +} +ATF_TC_BODY(format, tc) +{ + atf_error_t err; + char buf[1024]; + + printf("Testing default formatting function\n"); + err = atf_error_new("test_error", NULL, 0, NULL); + atf_error_format(err, buf, sizeof(buf)); + printf("Error string is: %s\n", buf); + ATF_REQUIRE(strcmp(buf, "Error 'test_error'") == 0); + atf_error_free(err); + + printf("Testing custom formatting function\n"); + err = atf_error_new("test_error", NULL, 0, test_format); + atf_error_format(err, buf, sizeof(buf)); + printf("Error string is: %s\n", buf); + ATF_REQUIRE(strcmp(buf, "Test formatting function") == 0); + atf_error_free(err); +} + +/* --------------------------------------------------------------------- + * Tests for the "libc" error. + * --------------------------------------------------------------------- */ + +ATF_TC(libc_new); +ATF_TC_HEAD(libc_new, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of libc errors"); +} +ATF_TC_BODY(libc_new, tc) +{ + atf_error_t err; + + err = atf_libc_error(ENOMEM, "Test message 1"); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_REQUIRE_EQ(atf_libc_error_code(err), ENOMEM); + ATF_REQUIRE(strcmp(atf_libc_error_msg(err), "Test message 1") == 0); + atf_error_free(err); + + err = atf_libc_error(EPERM, "%s message %d", "Test", 2); + ATF_REQUIRE(atf_error_is(err, "libc")); + ATF_REQUIRE_EQ(atf_libc_error_code(err), EPERM); + ATF_REQUIRE(strcmp(atf_libc_error_msg(err), "Test message 2") == 0); + atf_error_free(err); +} + +ATF_TC(libc_format); +ATF_TC_HEAD(libc_format, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the formatting of libc errors"); +} +ATF_TC_BODY(libc_format, tc) +{ + atf_error_t err; + char buf[1024]; + + err = atf_libc_error(ENOMEM, "Test message 1"); + atf_error_format(err, buf, sizeof(buf)); + ATF_REQUIRE(strstr(buf, strerror(ENOMEM)) != NULL); + ATF_REQUIRE(strstr(buf, "Test message 1") != NULL); + atf_error_free(err); + + err = atf_libc_error(EPERM, "Test message 2"); + atf_error_format(err, buf, sizeof(buf)); + ATF_REQUIRE(strstr(buf, strerror(EPERM)) != NULL); + ATF_REQUIRE(strstr(buf, "Test message 2") != NULL); + atf_error_free(err); + + err = atf_libc_error(EPERM, "%s message %d", "Test", 3); + atf_error_format(err, buf, sizeof(buf)); + ATF_REQUIRE(strstr(buf, strerror(EPERM)) != NULL); + ATF_REQUIRE(strstr(buf, "Test message 3") != NULL); + atf_error_free(err); +} + +/* --------------------------------------------------------------------- + * Tests for the "no_memory" error. + * --------------------------------------------------------------------- */ + +ATF_TC(no_memory_new); +ATF_TC_HEAD(no_memory_new, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of no_memory " + "errors"); +} +ATF_TC_BODY(no_memory_new, tc) +{ + atf_error_t err; + + err = atf_no_memory_error(); + ATF_REQUIRE(atf_error_is(err, "no_memory")); + ATF_REQUIRE(atf_error_data(err) == NULL); + atf_error_free(err); +} + +ATF_TC(no_memory_format); +ATF_TC_HEAD(no_memory_format, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the formatting of no_memory " + "errors"); +} +ATF_TC_BODY(no_memory_format, tc) +{ + atf_error_t err; + char buf[1024]; + + err = atf_no_memory_error(); + atf_error_format(err, buf, sizeof(buf)); + ATF_REQUIRE(strcmp(buf, "Not enough memory") == 0); + atf_error_free(err); +} + +ATF_TC(no_memory_twice); +ATF_TC_HEAD(no_memory_twice, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks the construction of no_memory " + "errors multiple times, as this error is initialized " + "statically"); +} +ATF_TC_BODY(no_memory_twice, tc) +{ + { + atf_error_t err = atf_no_memory_error(); + ATF_REQUIRE(atf_error_is(err, "no_memory")); + ATF_REQUIRE(atf_error_data(err) == NULL); + atf_error_free(err); + } + + { + atf_error_t err = atf_no_memory_error(); + ATF_REQUIRE(atf_error_is(err, "no_memory")); + ATF_REQUIRE(atf_error_data(err) == NULL); + atf_error_free(err); + } +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Add the tests for the "atf_error" type. */ + ATF_TP_ADD_TC(tp, error_new); + ATF_TP_ADD_TC(tp, error_new_wo_memory); + ATF_TP_ADD_TC(tp, no_error); + ATF_TP_ADD_TC(tp, is_error); + ATF_TP_ADD_TC(tp, format); + + /* Add the tests for the "libc" error. */ + ATF_TP_ADD_TC(tp, libc_new); + ATF_TP_ADD_TC(tp, libc_format); + + /* Add the tests for the "no_memory" error. */ + ATF_TP_ADD_TC(tp, no_memory_new); + ATF_TP_ADD_TC(tp, no_memory_format); + ATF_TP_ADD_TC(tp, no_memory_twice); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/h_build.h b/unit/atf-src/atf-c/h_build.h new file mode 100644 index 0000000..c5ad1c7 --- /dev/null +++ b/unit/atf-src/atf-c/h_build.h @@ -0,0 +1,410 @@ +/* Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if defined(ATF_C_H_BUILD_H) +# error "Cannot include h_build.h more than once." +#else +# define ATF_C_H_BUILD_H +#endif + +/* --------------------------------------------------------------------- + * Test case data. + * --------------------------------------------------------------------- */ + +static struct c_o_test { + const char *msg; + const char *cc; + const char *cflags; + const char *cppflags; + const char *sfile; + const char *ofile; + bool hasoptargs; + const char *const optargs[16]; + const char *const expargv[16]; +} c_o_tests[] = { + { + "No flags", + "cc", + "", + "", + "test.c", + "test.o", + false, + { + NULL + }, + { + "cc", "-o", "test.o", "-c", "test.c", NULL + }, + }, + + { + "Multi-word program name", + "cc -foo", + "", + "", + "test.c", + "test.o", + false, + { + NULL + }, + { + "cc", "-foo", "-o", "test.o", "-c", "test.c", NULL + }, + }, + + { + "Some cflags", + "cc", + "-f1 -f2 -f3 -f4-f5", + "", + "test.c", + "test.o", + false, + { + NULL + }, + { + "cc", "-f1", "-f2", "-f3", "-f4-f5", "-o", "test.o", + "-c", "test.c", NULL + }, + }, + + { + "Some cppflags", + "cc", + "", + "-f1 -f2 -f3 -f4-f5", + "test.c", + "test.o", + false, + { + NULL + }, + { + "cc", "-f1", "-f2", "-f3", "-f4-f5", "-o", "test.o", + "-c", "test.c", NULL + }, + }, + + { + "Some cflags and cppflags", + "cc", + "-f2", + "-f1", + "test.c", + "test.o", + false, + { + NULL + }, + { + "cc", "-f1", "-f2", "-o", "test.o", "-c", "test.c", NULL + }, + }, + + { + "Some optional arguments", + "cc", + "", + "", + "test.c", + "test.o", + true, + { + "-o1", "-o2", NULL + }, + { + "cc", "-o1", "-o2", "-o", "test.o", "-c", "test.c", NULL + }, + }, + + { + "Some cflags, cppflags and optional arguments", + "cc", + "-f2", + "-f1", + "test.c", + "test.o", + true, + { + "-o1", "-o2", NULL + }, + { + "cc", "-f1", "-f2", "-o1", "-o2", "-o", "test.o", + "-c", "test.c", NULL + }, + }, + + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + false, + { NULL }, + { NULL }, + }, +}; + +static struct cpp_test { + const char *msg; + const char *cpp; + const char *cppflags; + const char *sfile; + const char *ofile; + bool hasoptargs; + const char *const optargs[16]; + const char *const expargv[16]; +} cpp_tests[] = { + { + "No flags", + "cpp", + "", + "test.c", + "test.out", + false, + { + NULL + }, + { + "cpp", "-o", "test.out", "test.c", NULL + }, + }, + + { + "Multi-word program name", + "cpp -foo", + "", + "test.c", + "test.out", + false, + { + NULL + }, + { + "cpp", "-foo", "-o", "test.out", "test.c", NULL + }, + }, + + { + "Some cppflags", + "cpp", + "-f1 -f2 -f3 -f4-f5", + "test.c", + "test.out", + false, + { + NULL + }, + { + "cpp", "-f1", "-f2", "-f3", "-f4-f5", "-o", "test.out", + "test.c", NULL + }, + }, + + { + "Some optional arguments", + "cpp", + "", + "test.c", + "test.out", + true, + { + "-o1", "-o2", NULL + }, + { + "cpp", "-o1", "-o2", "-o", "test.out", "test.c", NULL + }, + }, + + { + "Some cppflags and optional arguments", + "cpp", + "-f1", + "test.c", + "test.out", + true, + { + "-o1", "-o2", NULL + }, + { + "cpp", "-f1", "-o1", "-o2", "-o", "test.out", "test.c", NULL + }, + }, + + { + NULL, + NULL, + NULL, + NULL, + NULL, + false, + { NULL }, + { NULL }, + }, +}; + +static struct cxx_o_test { + const char *msg; + const char *cxx; + const char *cxxflags; + const char *cppflags; + const char *sfile; + const char *ofile; + bool hasoptargs; + const char *const optargs[16]; + const char *const expargv[16]; +} cxx_o_tests[] = { + { + "No flags", + "c++", + "", + "", + "test.c", + "test.o", + false, + { + NULL + }, + { + "c++", "-o", "test.o", "-c", "test.c", NULL + }, + }, + + { + "Multi-word program name", + "c++ -foo", + "", + "", + "test.c", + "test.o", + false, + { + NULL + }, + { + "c++", "-foo", "-o", "test.o", "-c", "test.c", NULL + }, + }, + + { + "Some cxxflags", + "c++", + "-f1 -f2 -f3 -f4-f5", + "", + "test.c", + "test.o", + false, + { + NULL + }, + { + "c++", "-f1", "-f2", "-f3", "-f4-f5", "-o", "test.o", + "-c", "test.c", NULL + }, + }, + + { + "Some cppflags", + "c++", + "", + "-f1 -f2 -f3 -f4-f5", + "test.c", + "test.o", + false, + { + NULL + }, + { + "c++", "-f1", "-f2", "-f3", "-f4-f5", "-o", "test.o", + "-c", "test.c", NULL + }, + }, + + { + "Some cxxflags and cppflags", + "c++", + "-f2", + "-f1", + "test.c", + "test.o", + false, + { + NULL + }, + { + "c++", "-f1", "-f2", "-o", "test.o", "-c", "test.c", NULL + }, + }, + + { + "Some optional arguments", + "c++", + "", + "", + "test.c", + "test.o", + true, + { + "-o1", "-o2", NULL + }, + { + "c++", "-o1", "-o2", "-o", "test.o", "-c", "test.c", NULL + }, + }, + + { + "Some cxxflags, cppflags and optional arguments", + "c++", + "-f2", + "-f1", + "test.c", + "test.o", + true, + { + "-o1", "-o2", NULL + }, + { + "c++", "-f1", "-f2", "-o1", "-o2", "-o", "test.o", + "-c", "test.c", NULL + }, + }, + + { + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + false, + { NULL }, + { NULL }, + }, +}; diff --git a/unit/atf-src/atf-c/macros.h b/unit/atf-src/atf-c/macros.h new file mode 100644 index 0000000..485a159 --- /dev/null +++ b/unit/atf-src/atf-c/macros.h @@ -0,0 +1,212 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_MACROS_H) +#define ATF_C_MACROS_H + +#include <string.h> + +#include <atf-c/defs.h> +#include <atf-c/error.h> +#include <atf-c/tc.h> +#include <atf-c/tp.h> +#include <atf-c/utils.h> + +#define ATF_TC_NAME(tc) \ + (atfu_ ## tc ## _tc) + +#define ATF_TC_PACK_NAME(tc) \ + (atfu_ ## tc ## _tc_pack) + +#define ATF_TC_WITHOUT_HEAD(tc) \ + static void atfu_ ## tc ## _body(const atf_tc_t *); \ + static atf_tc_t atfu_ ## tc ## _tc; \ + static atf_tc_pack_t atfu_ ## tc ## _tc_pack = { \ + .m_ident = #tc, \ + .m_head = NULL, \ + .m_body = atfu_ ## tc ## _body, \ + .m_cleanup = NULL, \ + } + +#define ATF_TC(tc) \ + static void atfu_ ## tc ## _head(atf_tc_t *); \ + static void atfu_ ## tc ## _body(const atf_tc_t *); \ + static atf_tc_t atfu_ ## tc ## _tc; \ + static atf_tc_pack_t atfu_ ## tc ## _tc_pack = { \ + .m_ident = #tc, \ + .m_head = atfu_ ## tc ## _head, \ + .m_body = atfu_ ## tc ## _body, \ + .m_cleanup = NULL, \ + } + +#define ATF_TC_WITH_CLEANUP(tc) \ + static void atfu_ ## tc ## _head(atf_tc_t *); \ + static void atfu_ ## tc ## _body(const atf_tc_t *); \ + static void atfu_ ## tc ## _cleanup(const atf_tc_t *); \ + static atf_tc_t atfu_ ## tc ## _tc; \ + static atf_tc_pack_t atfu_ ## tc ## _tc_pack = { \ + .m_ident = #tc, \ + .m_head = atfu_ ## tc ## _head, \ + .m_body = atfu_ ## tc ## _body, \ + .m_cleanup = atfu_ ## tc ## _cleanup, \ + } + +#define ATF_TC_HEAD(tc, tcptr) \ + static \ + void \ + atfu_ ## tc ## _head(atf_tc_t *tcptr ATF_DEFS_ATTRIBUTE_UNUSED) + +#define ATF_TC_HEAD_NAME(tc) \ + (atfu_ ## tc ## _head) + +#define ATF_TC_BODY(tc, tcptr) \ + static \ + void \ + atfu_ ## tc ## _body(const atf_tc_t *tcptr ATF_DEFS_ATTRIBUTE_UNUSED) + +#define ATF_TC_BODY_NAME(tc) \ + (atfu_ ## tc ## _body) + +#define ATF_TC_CLEANUP(tc, tcptr) \ + static \ + void \ + atfu_ ## tc ## _cleanup(const atf_tc_t *tcptr ATF_DEFS_ATTRIBUTE_UNUSED) + +#define ATF_TC_CLEANUP_NAME(tc) \ + (atfu_ ## tc ## _cleanup) + +#define ATF_TP_ADD_TCS(tps) \ + static atf_error_t atfu_tp_add_tcs(atf_tp_t *); \ + int atf_tp_main(int, char **, atf_error_t (*)(atf_tp_t *)); \ + \ + int \ + main(int argc, char **argv) \ + { \ + return atf_tp_main(argc, argv, atfu_tp_add_tcs); \ + } \ + static \ + atf_error_t \ + atfu_tp_add_tcs(atf_tp_t *tps) + +#define ATF_TP_ADD_TC(tp, tc) \ + do { \ + atf_error_t atfu_err; \ + char **atfu_config = atf_tp_get_config(tp); \ + if (atfu_config == NULL) \ + return atf_no_memory_error(); \ + atfu_err = atf_tc_init_pack(&atfu_ ## tc ## _tc, \ + &atfu_ ## tc ## _tc_pack, \ + (const char *const *)atfu_config); \ + atf_utils_free_charpp(atfu_config); \ + if (atf_is_error(atfu_err)) \ + return atfu_err; \ + atfu_err = atf_tp_add_tc(tp, &atfu_ ## tc ## _tc); \ + if (atf_is_error(atfu_err)) \ + return atfu_err; \ + } while (0) + +#define ATF_REQUIRE_MSG(expression, fmt, ...) \ + do { \ + if (!(expression)) \ + atf_tc_fail_requirement(__FILE__, __LINE__, fmt, ##__VA_ARGS__); \ + } while(0) + +#define ATF_CHECK_MSG(expression, fmt, ...) \ + do { \ + if (!(expression)) \ + atf_tc_fail_check(__FILE__, __LINE__, fmt, ##__VA_ARGS__); \ + } while(0) + +#define ATF_REQUIRE(expression) \ + do { \ + if (!(expression)) \ + atf_tc_fail_requirement(__FILE__, __LINE__, "%s", \ + #expression " not met"); \ + } while(0) + +#define ATF_CHECK(expression) \ + do { \ + if (!(expression)) \ + atf_tc_fail_check(__FILE__, __LINE__, "%s", \ + #expression " not met"); \ + } while(0) + +#define ATF_REQUIRE_EQ(expected, actual) \ + ATF_REQUIRE_MSG((expected) == (actual), "%s != %s", #expected, #actual) + +#define ATF_CHECK_EQ(expected, actual) \ + ATF_CHECK_MSG((expected) == (actual), "%s != %s", #expected, #actual) + +#define ATF_REQUIRE_EQ_MSG(expected, actual, fmt, ...) \ + ATF_REQUIRE_MSG((expected) == (actual), "%s != %s: " fmt, \ + #expected, #actual, ##__VA_ARGS__) + +#define ATF_CHECK_EQ_MSG(expected, actual, fmt, ...) \ + ATF_CHECK_MSG((expected) == (actual), "%s != %s: " fmt, \ + #expected, #actual, ##__VA_ARGS__) + +#define ATF_REQUIRE_STREQ(expected, actual) \ + ATF_REQUIRE_MSG(strcmp(expected, actual) == 0, "%s != %s (%s != %s)", \ + #expected, #actual, expected, actual) + +#define ATF_CHECK_STREQ(expected, actual) \ + ATF_CHECK_MSG(strcmp(expected, actual) == 0, "%s != %s (%s != %s)", \ + #expected, #actual, expected, actual) + +#define ATF_REQUIRE_STREQ_MSG(expected, actual, fmt, ...) \ + ATF_REQUIRE_MSG(strcmp(expected, actual) == 0, \ + "%s != %s (%s != %s): " fmt, \ + #expected, #actual, expected, actual, ##__VA_ARGS__) + +#define ATF_CHECK_STREQ_MSG(expected, actual, fmt, ...) \ + ATF_CHECK_MSG(strcmp(expected, actual) == 0, \ + "%s != %s (%s != %s): " fmt, \ + #expected, #actual, expected, actual, ##__VA_ARGS__) + +#define ATF_REQUIRE_MATCH(regexp, string) \ + ATF_REQUIRE_MSG(atf_utils_grep_string("%s", string, regexp), \ + "'%s' not matched in '%s'", regexp, string); + +#define ATF_CHECK_MATCH(regexp, string) \ + ATF_CHECK_MSG(atf_utils_grep_string("%s", string, regexp), \ + "'%s' not matched in '%s'", regexp, string); + +#define ATF_REQUIRE_MATCH_MSG(regexp, string, fmt, ...) \ + ATF_REQUIRE_MSG(atf_utils_grep_string("%s", string, regexp), \ + "'%s' not matched in '%s': " fmt, regexp, string, \ + ##__VA_ARGS__); + +#define ATF_CHECK_MATCH_MSG(regexp, string, fmt, ...) \ + ATF_CHECK_MSG(atf_utils_grep_string("%s", string, regexp), \ + "'%s' not matched in '%s': " fmt, regexp, string, \ + ##__VA_ARGS__); + +#define ATF_CHECK_ERRNO(exp_errno, bool_expr) \ + atf_tc_check_errno(__FILE__, __LINE__, exp_errno, #bool_expr, bool_expr) + +#define ATF_REQUIRE_ERRNO(exp_errno, bool_expr) \ + atf_tc_require_errno(__FILE__, __LINE__, exp_errno, #bool_expr, bool_expr) + +#endif /* !defined(ATF_C_MACROS_H) */ diff --git a/unit/atf-src/atf-c/macros_h_test.c b/unit/atf-src/atf-c/macros_h_test.c new file mode 100644 index 0000000..9525fca --- /dev/null +++ b/unit/atf-src/atf-c/macros_h_test.c @@ -0,0 +1,102 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include <atf-c/macros.h> + +void atf_require_inside_if(void); +void atf_require_equal_inside_if(void); +void atf_check_errno_semicolons(void); +void atf_require_errno_semicolons(void); + +void +atf_require_inside_if(void) +{ + /* Make sure that ATF_REQUIRE can be used inside an if statement that + * does not have braces. Earlier versions of it generated an error + * if there was an else clause because they confused the compiler + * by defining an unprotected nested if. */ + if (true) + ATF_REQUIRE(true); + else + ATF_REQUIRE(true); +} + +void +atf_require_equal_inside_if(void) +{ + /* Make sure that ATF_REQUIRE_EQUAL can be used inside an if statement + * that does not have braces. Earlier versions of it generated an + * error if there was an else clause because they confused the + * compiler by defining an unprotected nested if. */ + if (true) + ATF_REQUIRE_EQ(true, true); + else + ATF_REQUIRE_EQ(true, true); +} + +void +atf_check_errno_semicolons(void) +{ + /* Check that ATF_CHECK_ERRNO does not contain a semicolon that would + * cause an empty-statement that confuses some compilers. */ + ATF_CHECK_ERRNO(1, 1 == 1); + ATF_CHECK_ERRNO(2, 2 == 2); +} + +void +atf_require_errno_semicolons(void) +{ + /* Check that ATF_REQUIRE_ERRNO does not contain a semicolon that would + * cause an empty-statement that confuses some compilers. */ + ATF_REQUIRE_ERRNO(1, 1 == 1); + ATF_REQUIRE_ERRNO(2, 2 == 2); +} + +/* Test case names should not be expanded during instatiation so that they + * can have the exact same name as macros. */ +#define TEST_MACRO_1 invalid + name +#define TEST_MACRO_2 invalid + name +#define TEST_MACRO_3 invalid + name +ATF_TC(TEST_MACRO_1); +ATF_TC_HEAD(TEST_MACRO_1, tc) { if (tc != NULL) {} } +ATF_TC_BODY(TEST_MACRO_1, tc) { if (tc != NULL) {} } +atf_tc_t *test_name_1 = &ATF_TC_NAME(TEST_MACRO_1); +atf_tc_pack_t *test_pack_1 = &ATF_TC_PACK_NAME(TEST_MACRO_1); +void (*head_1)(atf_tc_t *) = ATF_TC_HEAD_NAME(TEST_MACRO_1); +void (*body_1)(const atf_tc_t *) = ATF_TC_BODY_NAME(TEST_MACRO_1); +ATF_TC_WITH_CLEANUP(TEST_MACRO_2); +ATF_TC_HEAD(TEST_MACRO_2, tc) { if (tc != NULL) {} } +ATF_TC_BODY(TEST_MACRO_2, tc) { if (tc != NULL) {} } +ATF_TC_CLEANUP(TEST_MACRO_2, tc) { if (tc != NULL) {} } +atf_tc_t *test_name_2 = &ATF_TC_NAME(TEST_MACRO_2); +atf_tc_pack_t *test_pack_2 = &ATF_TC_PACK_NAME(TEST_MACRO_2); +void (*head_2)(atf_tc_t *) = ATF_TC_HEAD_NAME(TEST_MACRO_2); +void (*body_2)(const atf_tc_t *) = ATF_TC_BODY_NAME(TEST_MACRO_2); +void (*cleanup_2)(const atf_tc_t *) = ATF_TC_CLEANUP_NAME(TEST_MACRO_2); +ATF_TC_WITHOUT_HEAD(TEST_MACRO_3); +ATF_TC_BODY(TEST_MACRO_3, tc) { if (tc != NULL) {} } +atf_tc_t *test_name_3 = &ATF_TC_NAME(TEST_MACRO_3); +atf_tc_pack_t *test_pack_3 = &ATF_TC_PACK_NAME(TEST_MACRO_3); +void (*body_3)(const atf_tc_t *) = ATF_TC_BODY_NAME(TEST_MACRO_3); diff --git a/unit/atf-src/atf-c/macros_test.c b/unit/atf-src/atf-c/macros_test.c new file mode 100644 index 0000000..83578cd --- /dev/null +++ b/unit/atf-src/atf-c/macros_test.c @@ -0,0 +1,893 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/macros.h" + +#include <sys/types.h> +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/detail/fs.h" +#include "atf-c/detail/process.h" +#include "atf-c/detail/test_helpers.h" +#include "atf-c/detail/text.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +void +create_ctl_file(const char *name) +{ + atf_fs_path_t p; + + RE(atf_fs_path_init_fmt(&p, "%s", name)); + ATF_REQUIRE(open(atf_fs_path_cstring(&p), + O_CREAT | O_WRONLY | O_TRUNC, 0644) != -1); + atf_fs_path_fini(&p); +} + +static +bool +exists(const char *p) +{ + bool b; + atf_fs_path_t pp; + + RE(atf_fs_path_init_fmt(&pp, "%s", p)); + RE(atf_fs_exists(&pp, &b)); + atf_fs_path_fini(&pp); + + return b; +} + +static +void +init_and_run_h_tc(const char *name, void (*head)(atf_tc_t *), + void (*body)(const atf_tc_t *)) +{ + atf_tc_t tc; + const char *const config[] = { NULL }; + + RE(atf_tc_init(&tc, name, head, body, NULL, config)); + run_h_tc(&tc, "output", "error", "result"); + atf_tc_fini(&tc); +} + +/* --------------------------------------------------------------------- + * Helper test cases. + * --------------------------------------------------------------------- */ + +#define H_DEF(id, macro) \ + ATF_TC_HEAD(h_ ## id, tc) \ + { \ + atf_tc_set_md_var(tc, "descr", "Helper test case"); \ + } \ + ATF_TC_BODY(h_ ## id, tc) \ + { \ + create_ctl_file("before"); \ + macro; \ + create_ctl_file("after"); \ + } + +#define H_CHECK_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_check_ ## id) +#define H_CHECK_BODY_NAME(id) ATF_TC_BODY_NAME(h_check_ ## id) +#define H_CHECK(id, condition) \ + H_DEF(check_ ## id, ATF_CHECK(condition)) + +#define H_CHECK_MSG_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_check_msg_ ## id) +#define H_CHECK_MSG_BODY_NAME(id) ATF_TC_BODY_NAME(h_check_msg_ ## id) +#define H_CHECK_MSG(id, condition, msg) \ + H_DEF(check_msg_ ## id, ATF_CHECK_MSG(condition, msg)) + +#define H_CHECK_EQ_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_check_eq_ ## id) +#define H_CHECK_EQ_BODY_NAME(id) ATF_TC_BODY_NAME(h_check_eq_ ## id) +#define H_CHECK_EQ(id, v1, v2) \ + H_DEF(check_eq_ ## id, ATF_CHECK_EQ(v1, v2)) + +#define H_CHECK_STREQ_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_check_streq_ ## id) +#define H_CHECK_STREQ_BODY_NAME(id) ATF_TC_BODY_NAME(h_check_streq_ ## id) +#define H_CHECK_STREQ(id, v1, v2) \ + H_DEF(check_streq_ ## id, ATF_CHECK_STREQ(v1, v2)) + +#define H_CHECK_MATCH_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_check_match_ ## id) +#define H_CHECK_MATCH_BODY_NAME(id) ATF_TC_BODY_NAME(h_check_match_ ## id) +#define H_CHECK_MATCH(id, v1, v2) \ + H_DEF(check_match_ ## id, ATF_CHECK_MATCH(v1, v2)) + +#define H_CHECK_EQ_MSG_HEAD_NAME(id) \ + ATF_TC_HEAD_NAME(h_check_eq_msg_ ## id) +#define H_CHECK_EQ_MSG_BODY_NAME(id) \ + ATF_TC_BODY_NAME(h_check_eq_msg_ ## id) +#define H_CHECK_EQ_MSG(id, v1, v2, msg) \ + H_DEF(check_eq_msg_ ## id, ATF_CHECK_EQ_MSG(v1, v2, msg)) + +#define H_CHECK_STREQ_MSG_HEAD_NAME(id) \ + ATF_TC_HEAD_NAME(h_check_streq_msg_ ## id) +#define H_CHECK_STREQ_MSG_BODY_NAME(id) \ + ATF_TC_BODY_NAME(h_check_streq_msg_ ## id) +#define H_CHECK_STREQ_MSG(id, v1, v2, msg) \ + H_DEF(check_streq_msg_ ## id, ATF_CHECK_STREQ_MSG(v1, v2, msg)) + +#define H_CHECK_MATCH_MSG_HEAD_NAME(id) \ + ATF_TC_HEAD_NAME(h_check_match_msg_ ## id) +#define H_CHECK_MATCH_MSG_BODY_NAME(id) \ + ATF_TC_BODY_NAME(h_check_match_msg_ ## id) +#define H_CHECK_MATCH_MSG(id, v1, v2, msg) \ + H_DEF(check_match_msg_ ## id, ATF_CHECK_MATCH_MSG(v1, v2, msg)) + +#define H_CHECK_ERRNO_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_check_errno_ ## id) +#define H_CHECK_ERRNO_BODY_NAME(id) ATF_TC_BODY_NAME(h_check_errno_ ## id) +#define H_CHECK_ERRNO(id, exp_errno, bool_expr) \ + H_DEF(check_errno_ ## id, ATF_CHECK_ERRNO(exp_errno, bool_expr)) + +#define H_REQUIRE_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_require_ ## id) +#define H_REQUIRE_BODY_NAME(id) ATF_TC_BODY_NAME(h_require_ ## id) +#define H_REQUIRE(id, condition) \ + H_DEF(require_ ## id, ATF_REQUIRE(condition)) + +#define H_REQUIRE_MSG_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_require_msg_ ## id) +#define H_REQUIRE_MSG_BODY_NAME(id) ATF_TC_BODY_NAME(h_require_msg_ ## id) +#define H_REQUIRE_MSG(id, condition, msg) \ + H_DEF(require_msg_ ## id, ATF_REQUIRE_MSG(condition, msg)) + +#define H_REQUIRE_EQ_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_require_eq_ ## id) +#define H_REQUIRE_EQ_BODY_NAME(id) ATF_TC_BODY_NAME(h_require_eq_ ## id) +#define H_REQUIRE_EQ(id, v1, v2) \ + H_DEF(require_eq_ ## id, ATF_REQUIRE_EQ(v1, v2)) + +#define H_REQUIRE_STREQ_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_require_streq_ ## id) +#define H_REQUIRE_STREQ_BODY_NAME(id) ATF_TC_BODY_NAME(h_require_streq_ ## id) +#define H_REQUIRE_STREQ(id, v1, v2) \ + H_DEF(require_streq_ ## id, ATF_REQUIRE_STREQ(v1, v2)) + +#define H_REQUIRE_MATCH_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_require_match_ ## id) +#define H_REQUIRE_MATCH_BODY_NAME(id) ATF_TC_BODY_NAME(h_require_match_ ## id) +#define H_REQUIRE_MATCH(id, v1, v2) \ + H_DEF(require_match_ ## id, ATF_REQUIRE_MATCH(v1, v2)) + +#define H_REQUIRE_EQ_MSG_HEAD_NAME(id) \ + ATF_TC_HEAD_NAME(h_require_eq_msg_ ## id) +#define H_REQUIRE_EQ_MSG_BODY_NAME(id) \ + ATF_TC_BODY_NAME(h_require_eq_msg_ ## id) +#define H_REQUIRE_EQ_MSG(id, v1, v2, msg) \ + H_DEF(require_eq_msg_ ## id, ATF_REQUIRE_EQ_MSG(v1, v2, msg)) + +#define H_REQUIRE_STREQ_MSG_HEAD_NAME(id) \ + ATF_TC_HEAD_NAME(h_require_streq_msg_ ## id) +#define H_REQUIRE_STREQ_MSG_BODY_NAME(id) \ + ATF_TC_BODY_NAME(h_require_streq_msg_ ## id) +#define H_REQUIRE_STREQ_MSG(id, v1, v2, msg) \ + H_DEF(require_streq_msg_ ## id, ATF_REQUIRE_STREQ_MSG(v1, v2, msg)) + +#define H_REQUIRE_MATCH_MSG_HEAD_NAME(id) \ + ATF_TC_HEAD_NAME(h_require_match_msg_ ## id) +#define H_REQUIRE_MATCH_MSG_BODY_NAME(id) \ + ATF_TC_BODY_NAME(h_require_match_msg_ ## id) +#define H_REQUIRE_MATCH_MSG(id, v1, v2, msg) \ + H_DEF(require_match_msg_ ## id, ATF_REQUIRE_MATCH_MSG(v1, v2, msg)) + +#define H_REQUIRE_ERRNO_HEAD_NAME(id) ATF_TC_HEAD_NAME(h_require_errno_ ## id) +#define H_REQUIRE_ERRNO_BODY_NAME(id) ATF_TC_BODY_NAME(h_require_errno_ ## id) +#define H_REQUIRE_ERRNO(id, exp_errno, bool_expr) \ + H_DEF(require_errno_ ## id, ATF_REQUIRE_ERRNO(exp_errno, bool_expr)) + +/* --------------------------------------------------------------------- + * Test cases for the ATF_{CHECK,REQUIRE}_ERRNO macros. + * --------------------------------------------------------------------- */ + +static int +errno_fail_stub(const int raised_errno) +{ + errno = raised_errno; + return -1; +} + +static int +errno_ok_stub(void) +{ + return 0; +} + +H_CHECK_ERRNO(no_error, -1, errno_ok_stub() == -1); +H_CHECK_ERRNO(errno_ok, 2, errno_fail_stub(2) == -1); +H_CHECK_ERRNO(errno_fail, 3, errno_fail_stub(4) == -1); + +H_REQUIRE_ERRNO(no_error, -1, errno_ok_stub() == -1); +H_REQUIRE_ERRNO(errno_ok, 2, errno_fail_stub(2) == -1); +H_REQUIRE_ERRNO(errno_fail, 3, errno_fail_stub(4) == -1); + +ATF_TC(check_errno); +ATF_TC_HEAD(check_errno, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the ATF_CHECK_ERRNO macro"); +} +ATF_TC_BODY(check_errno, tc) +{ + struct test { + void (*head)(atf_tc_t *); + void (*body)(const atf_tc_t *); + bool ok; + const char *exp_regex; + } *t, tests[] = { + { H_CHECK_ERRNO_HEAD_NAME(no_error), + H_CHECK_ERRNO_BODY_NAME(no_error), + false, "Expected true value in errno_ok_stub\\(\\) == -1" }, + { H_CHECK_ERRNO_HEAD_NAME(errno_ok), + H_CHECK_ERRNO_BODY_NAME(errno_ok), + true, NULL }, + { H_CHECK_ERRNO_HEAD_NAME(errno_fail), + H_CHECK_ERRNO_BODY_NAME(errno_fail), + false, "Expected errno 3, got 4, in errno_fail_stub\\(4\\) == -1" }, + { NULL, NULL, false, NULL } + }; + + for (t = &tests[0]; t->head != NULL; t++) { + init_and_run_h_tc("h_check_errno", t->head, t->body); + + ATF_REQUIRE(exists("before")); + ATF_REQUIRE(exists("after")); + + if (t->ok) { + ATF_REQUIRE(atf_utils_grep_file("^passed", "result")); + } else { + ATF_REQUIRE(atf_utils_grep_file("^failed", "result")); + ATF_REQUIRE(atf_utils_grep_file( + "macros_test.c:[0-9]+: %s$", "error", t->exp_regex)); + } + + ATF_REQUIRE(unlink("before") != -1); + ATF_REQUIRE(unlink("after") != -1); + } +} + +ATF_TC(require_errno); +ATF_TC_HEAD(require_errno, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the ATF_REQUIRE_ERRNO macro"); +} +ATF_TC_BODY(require_errno, tc) +{ + struct test { + void (*head)(atf_tc_t *); + void (*body)(const atf_tc_t *); + bool ok; + const char *exp_regex; + } *t, tests[] = { + { H_REQUIRE_ERRNO_HEAD_NAME(no_error), + H_REQUIRE_ERRNO_BODY_NAME(no_error), + false, "Expected true value in errno_ok_stub\\(\\) == -1" }, + { H_REQUIRE_ERRNO_HEAD_NAME(errno_ok), + H_REQUIRE_ERRNO_BODY_NAME(errno_ok), + true, NULL }, + { H_REQUIRE_ERRNO_HEAD_NAME(errno_fail), + H_REQUIRE_ERRNO_BODY_NAME(errno_fail), + false, "Expected errno 3, got 4, in errno_fail_stub\\(4\\) == -1" }, + { NULL, NULL, false, NULL } + }; + + for (t = &tests[0]; t->head != NULL; t++) { + init_and_run_h_tc("h_require_errno", t->head, t->body); + + ATF_REQUIRE(exists("before")); + if (t->ok) { + ATF_REQUIRE(atf_utils_grep_file("^passed", "result")); + ATF_REQUIRE(exists("after")); + } else { + ATF_REQUIRE(atf_utils_grep_file( + "^failed: .*macros_test.c:[0-9]+: %s$", "result", + t->exp_regex)); + ATF_REQUIRE(!exists("after")); + } + + ATF_REQUIRE(unlink("before") != -1); + if (t->ok) + ATF_REQUIRE(unlink("after") != -1); + } +} + +/* --------------------------------------------------------------------- + * Test cases for the ATF_CHECK and ATF_CHECK_MSG macros. + * --------------------------------------------------------------------- */ + +H_CHECK(0, 0); +H_CHECK(1, 1); +H_CHECK_MSG(0, 0, "expected a false value"); +H_CHECK_MSG(1, 1, "expected a true value"); + +ATF_TC(check); +ATF_TC_HEAD(check, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the ATF_CHECK and " + "ATF_CHECK_MSG macros"); +} +ATF_TC_BODY(check, tc) +{ + struct test { + void (*head)(atf_tc_t *); + void (*body)(const atf_tc_t *); + bool value; + const char *msg; + bool ok; + } *t, tests[] = { + { H_CHECK_HEAD_NAME(0), H_CHECK_BODY_NAME(0), 0, + "0 not met", false }, + { H_CHECK_HEAD_NAME(1), H_CHECK_BODY_NAME(1), 1, + "1 not met", true }, + { H_CHECK_MSG_HEAD_NAME(0), H_CHECK_MSG_BODY_NAME(0), 0, + "expected a false value", false }, + { H_CHECK_MSG_HEAD_NAME(1), H_CHECK_MSG_BODY_NAME(1), 1, + "expected a true value", true }, + { NULL, NULL, false, NULL, false } + }; + + for (t = &tests[0]; t->head != NULL; t++) { + printf("Checking with a %d value\n", t->value); + + init_and_run_h_tc("h_check", t->head, t->body); + + ATF_REQUIRE(exists("before")); + ATF_REQUIRE(exists("after")); + + if (t->ok) { + ATF_REQUIRE(atf_utils_grep_file("^passed", "result")); + } else { + ATF_REQUIRE(atf_utils_grep_file("^failed", "result")); + ATF_REQUIRE(atf_utils_grep_file("Check failed: .*" + "macros_test.c:[0-9]+: %s$", "error", t->msg)); + } + + ATF_REQUIRE(unlink("before") != -1); + ATF_REQUIRE(unlink("after") != -1); + } +} + +/* --------------------------------------------------------------------- + * Test cases for the ATF_CHECK_*EQ_ macros. + * --------------------------------------------------------------------- */ + +struct check_eq_test { + void (*head)(atf_tc_t *); + void (*body)(const atf_tc_t *); + const char *v1; + const char *v2; + const char *msg; + bool ok; +}; + +static +void +do_check_eq_tests(const struct check_eq_test *tests) +{ + const struct check_eq_test *t; + + for (t = &tests[0]; t->head != NULL; t++) { + printf("Checking with %s, %s and expecting %s\n", t->v1, t->v2, + t->ok ? "true" : "false"); + + init_and_run_h_tc("h_check", t->head, t->body); + + ATF_CHECK(exists("before")); + ATF_CHECK(exists("after")); + + if (t->ok) { + ATF_REQUIRE(atf_utils_grep_file("^passed", "result")); + } else { + ATF_REQUIRE(atf_utils_grep_file("^failed", "result")); + ATF_CHECK(atf_utils_grep_file("Check failed: .*" + "macros_test.c:[0-9]+: %s$", "error", t->msg)); + } + + ATF_CHECK(unlink("before") != -1); + ATF_CHECK(unlink("after") != -1); + } +} + +H_CHECK_EQ(1_1, 1, 1); +H_CHECK_EQ(1_2, 1, 2); +H_CHECK_EQ(2_1, 2, 1); +H_CHECK_EQ(2_2, 2, 2); +H_CHECK_EQ_MSG(1_1, 1, 1, "1 does not match 1"); +H_CHECK_EQ_MSG(1_2, 1, 2, "1 does not match 2"); +H_CHECK_EQ_MSG(2_1, 2, 1, "2 does not match 1"); +H_CHECK_EQ_MSG(2_2, 2, 2, "2 does not match 2"); + +ATF_TC(check_eq); +ATF_TC_HEAD(check_eq, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the ATF_CHECK_EQ and " + "ATF_CHECK_EQ_MSG macros"); +} +ATF_TC_BODY(check_eq, tc) +{ + struct check_eq_test tests[] = { + { H_CHECK_EQ_HEAD_NAME(1_1), H_CHECK_EQ_BODY_NAME(1_1), + "1", "1", "1 != 1", true }, + { H_CHECK_EQ_HEAD_NAME(1_2), H_CHECK_EQ_BODY_NAME(1_2), + "1", "2", "1 != 2", false }, + { H_CHECK_EQ_HEAD_NAME(2_1), H_CHECK_EQ_BODY_NAME(2_1), + "2", "1", "2 != 1", false }, + { H_CHECK_EQ_HEAD_NAME(2_2), H_CHECK_EQ_BODY_NAME(2_2), + "2", "2", "2 != 2", true }, + { H_CHECK_EQ_MSG_HEAD_NAME(1_1), H_CHECK_EQ_MSG_BODY_NAME(1_1), + "1", "1", "1 != 1: 1 does not match 1", true }, + { H_CHECK_EQ_MSG_HEAD_NAME(1_2), H_CHECK_EQ_MSG_BODY_NAME(1_2), + "1", "2", "1 != 2: 1 does not match 2", false }, + { H_CHECK_EQ_MSG_HEAD_NAME(2_1), H_CHECK_EQ_MSG_BODY_NAME(2_1), + "2", "1", "2 != 1: 2 does not match 1", false }, + { H_CHECK_EQ_MSG_HEAD_NAME(2_2), H_CHECK_EQ_MSG_BODY_NAME(2_2), + "2", "2", "2 != 2: 2 does not match 2", true }, + { NULL, NULL, 0, 0, "", false } + }; + do_check_eq_tests(tests); +} + +H_CHECK_STREQ(1_1, "1", "1"); +H_CHECK_STREQ(1_2, "1", "2"); +H_CHECK_STREQ(2_1, "2", "1"); +H_CHECK_STREQ(2_2, "2", "2"); +H_CHECK_STREQ_MSG(1_1, "1", "1", "1 does not match 1"); +H_CHECK_STREQ_MSG(1_2, "1", "2", "1 does not match 2"); +H_CHECK_STREQ_MSG(2_1, "2", "1", "2 does not match 1"); +H_CHECK_STREQ_MSG(2_2, "2", "2", "2 does not match 2"); +#define CHECK_STREQ_VAR1 "5" +#define CHECK_STREQ_VAR2 "9" +const char *check_streq_var1 = CHECK_STREQ_VAR1; +const char *check_streq_var2 = CHECK_STREQ_VAR2; +H_CHECK_STREQ(vars, check_streq_var1, check_streq_var2); + +ATF_TC(check_streq); +ATF_TC_HEAD(check_streq, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the ATF_CHECK_STREQ and " + "ATF_CHECK_STREQ_MSG macros"); +} +ATF_TC_BODY(check_streq, tc) +{ + struct check_eq_test tests[] = { + { H_CHECK_STREQ_HEAD_NAME(1_1), H_CHECK_STREQ_BODY_NAME(1_1), + "1", "1", "\"1\" != \"1\" \\(1 != 1\\)", true }, + { H_CHECK_STREQ_HEAD_NAME(1_2), H_CHECK_STREQ_BODY_NAME(1_2), + "1", "2", "\"1\" != \"2\" \\(1 != 2\\)", false }, + { H_CHECK_STREQ_HEAD_NAME(2_1), H_CHECK_STREQ_BODY_NAME(2_1), + "2", "1", "\"2\" != \"1\" \\(2 != 1\\)", false }, + { H_CHECK_STREQ_HEAD_NAME(2_2), H_CHECK_STREQ_BODY_NAME(2_2), + "2", "2", "\"2\" != \"2\" \\(2 != 2\\)", true }, + { H_CHECK_STREQ_MSG_HEAD_NAME(1_1), + H_CHECK_STREQ_MSG_BODY_NAME(1_1), + "1", "1", "\"1\" != \"1\" \\(1 != 1\\): 1 does not match 1", true }, + { H_CHECK_STREQ_MSG_HEAD_NAME(1_2), + H_CHECK_STREQ_MSG_BODY_NAME(1_2), + "1", "2", "\"1\" != \"2\" \\(1 != 2\\): 1 does not match 2", false }, + { H_CHECK_STREQ_MSG_HEAD_NAME(2_1), + H_CHECK_STREQ_MSG_BODY_NAME(2_1), + "2", "1", "\"2\" != \"1\" \\(2 != 1\\): 2 does not match 1", false }, + { H_CHECK_STREQ_MSG_HEAD_NAME(2_2), + H_CHECK_STREQ_MSG_BODY_NAME(2_2), + "2", "2", "\"2\" != \"2\" \\(2 != 2\\): 2 does not match 2", true }, + { H_CHECK_STREQ_HEAD_NAME(vars), H_CHECK_STREQ_BODY_NAME(vars), + check_streq_var1, check_streq_var2, + "check_streq_var1 != check_streq_var2 \\(" + CHECK_STREQ_VAR1 " != " CHECK_STREQ_VAR2 "\\)", false }, + { NULL, NULL, 0, 0, "", false } + }; + do_check_eq_tests(tests); +} + +/* --------------------------------------------------------------------- + * Test cases for the ATF_CHECK_MATCH and ATF_CHECK_MATCH_MSG macros. + * --------------------------------------------------------------------- */ + +H_CHECK_MATCH(yes, "hello [a-z]+", "abc hello world"); +H_CHECK_MATCH(no, "hello [a-z]+", "abc hello WORLD"); +H_CHECK_MATCH_MSG(yes, "hello [a-z]+", "abc hello world", "lowercase"); +H_CHECK_MATCH_MSG(no, "hello [a-z]+", "abc hello WORLD", "uppercase"); + +ATF_TC(check_match); +ATF_TC_HEAD(check_match, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the ATF_CHECK_MATCH and " + "ATF_CHECK_MATCH_MSG macros"); +} +ATF_TC_BODY(check_match, tc) +{ + struct check_eq_test tests[] = { + { H_CHECK_MATCH_HEAD_NAME(yes), H_CHECK_MATCH_BODY_NAME(yes), + "hello [a-z]+", "abc hello world", "", true }, + { H_CHECK_MATCH_HEAD_NAME(no), H_CHECK_MATCH_BODY_NAME(no), + "hello [a-z]+", "abc hello WORLD", + "'hello \\[a-z\\]\\+' not matched in 'abc hello WORLD'", false }, + { H_CHECK_MATCH_MSG_HEAD_NAME(yes), H_CHECK_MATCH_MSG_BODY_NAME(yes), + "hello [a-z]+", "abc hello world", "", true }, + { H_CHECK_MATCH_MSG_HEAD_NAME(no), H_CHECK_MATCH_MSG_BODY_NAME(no), + "hello [a-z]+", "abc hello WORLD", + "'hello \\[a-z\\]\\+' not matched in 'abc hello WORLD': uppercase", + false }, + { NULL, NULL, 0, 0, "", false } + }; + do_check_eq_tests(tests); +} + +/* --------------------------------------------------------------------- + * Test cases for the ATF_REQUIRE and ATF_REQUIRE_MSG macros. + * --------------------------------------------------------------------- */ + +H_REQUIRE(0, 0); +H_REQUIRE(1, 1); +H_REQUIRE_MSG(0, 0, "expected a false value"); +H_REQUIRE_MSG(1, 1, "expected a true value"); + +ATF_TC(require); +ATF_TC_HEAD(require, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the ATF_REQUIRE and " + "ATF_REQUIRE_MSG macros"); +} +ATF_TC_BODY(require, tc) +{ + struct test { + void (*head)(atf_tc_t *); + void (*body)(const atf_tc_t *); + bool value; + const char *msg; + bool ok; + } *t, tests[] = { + { H_REQUIRE_HEAD_NAME(0), H_REQUIRE_BODY_NAME(0), 0, + "0 not met", false }, + { H_REQUIRE_HEAD_NAME(1), H_REQUIRE_BODY_NAME(1), 1, + "1 not met", true }, + { H_REQUIRE_MSG_HEAD_NAME(0), H_REQUIRE_MSG_BODY_NAME(0), 0, + "expected a false value", false }, + { H_REQUIRE_MSG_HEAD_NAME(1), H_REQUIRE_MSG_BODY_NAME(1), 1, + "expected a true value", true }, + { NULL, NULL, false, NULL, false } + }; + + for (t = &tests[0]; t->head != NULL; t++) { + printf("Checking with a %d value\n", t->value); + + init_and_run_h_tc("h_require", t->head, t->body); + + ATF_REQUIRE(exists("before")); + if (t->ok) { + ATF_REQUIRE(atf_utils_grep_file("^passed", "result")); + ATF_REQUIRE(exists("after")); + } else { + ATF_REQUIRE(atf_utils_grep_file( + "^failed: .*macros_test.c:[0-9]+: %s$", "result", t->msg)); + ATF_REQUIRE(!exists("after")); + } + + ATF_REQUIRE(unlink("before") != -1); + if (t->ok) + ATF_REQUIRE(unlink("after") != -1); + } +} + +/* --------------------------------------------------------------------- + * Test cases for the ATF_REQUIRE_*EQ_ macros. + * --------------------------------------------------------------------- */ + +struct require_eq_test { + void (*head)(atf_tc_t *); + void (*body)(const atf_tc_t *); + const char *v1; + const char *v2; + const char *msg; + bool ok; +}; + +static +void +do_require_eq_tests(const struct require_eq_test *tests) +{ + const struct require_eq_test *t; + + for (t = &tests[0]; t->head != NULL; t++) { + printf("Checking with %s, %s and expecting %s\n", t->v1, t->v2, + t->ok ? "true" : "false"); + + init_and_run_h_tc("h_require", t->head, t->body); + + ATF_REQUIRE(exists("before")); + if (t->ok) { + ATF_REQUIRE(atf_utils_grep_file("^passed", "result")); + ATF_REQUIRE(exists("after")); + } else { + ATF_REQUIRE(atf_utils_grep_file("^failed: .*macros_test.c" + ":[0-9]+: %s$", "result", t->msg)); + ATF_REQUIRE(!exists("after")); + } + + ATF_REQUIRE(unlink("before") != -1); + if (t->ok) + ATF_REQUIRE(unlink("after") != -1); + } +} + +H_REQUIRE_EQ(1_1, 1, 1); +H_REQUIRE_EQ(1_2, 1, 2); +H_REQUIRE_EQ(2_1, 2, 1); +H_REQUIRE_EQ(2_2, 2, 2); +H_REQUIRE_EQ_MSG(1_1, 1, 1, "1 does not match 1"); +H_REQUIRE_EQ_MSG(1_2, 1, 2, "1 does not match 2"); +H_REQUIRE_EQ_MSG(2_1, 2, 1, "2 does not match 1"); +H_REQUIRE_EQ_MSG(2_2, 2, 2, "2 does not match 2"); + +ATF_TC(require_eq); +ATF_TC_HEAD(require_eq, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the ATF_REQUIRE_EQ and " + "ATF_REQUIRE_EQ_MSG macros"); +} +ATF_TC_BODY(require_eq, tc) +{ + struct require_eq_test tests[] = { + { H_REQUIRE_EQ_HEAD_NAME(1_1), H_REQUIRE_EQ_BODY_NAME(1_1), + "1", "1", "1 != 1", true }, + { H_REQUIRE_EQ_HEAD_NAME(1_2), H_REQUIRE_EQ_BODY_NAME(1_2), + "1", "2", "1 != 2", false }, + { H_REQUIRE_EQ_HEAD_NAME(2_1), H_REQUIRE_EQ_BODY_NAME(2_1), + "2", "1", "2 != 1", false }, + { H_REQUIRE_EQ_HEAD_NAME(2_2), H_REQUIRE_EQ_BODY_NAME(2_2), + "2", "2", "2 != 2", true }, + { H_REQUIRE_EQ_MSG_HEAD_NAME(1_1), H_REQUIRE_EQ_MSG_BODY_NAME(1_1), + "1", "1", "1 != 1: 1 does not match 1", true }, + { H_REQUIRE_EQ_MSG_HEAD_NAME(1_2), H_REQUIRE_EQ_MSG_BODY_NAME(1_2), + "1", "2", "1 != 2: 1 does not match 2", false }, + { H_REQUIRE_EQ_MSG_HEAD_NAME(2_1), H_REQUIRE_EQ_MSG_BODY_NAME(2_1), + "2", "1", "2 != 1: 2 does not match 1", false }, + { H_REQUIRE_EQ_MSG_HEAD_NAME(2_2), H_REQUIRE_EQ_MSG_BODY_NAME(2_2), + "2", "2", "2 != 2: 2 does not match 2", true }, + { NULL, NULL, 0, 0, "", false } + }; + do_require_eq_tests(tests); +} + +H_REQUIRE_STREQ(1_1, "1", "1"); +H_REQUIRE_STREQ(1_2, "1", "2"); +H_REQUIRE_STREQ(2_1, "2", "1"); +H_REQUIRE_STREQ(2_2, "2", "2"); +H_REQUIRE_STREQ_MSG(1_1, "1", "1", "1 does not match 1"); +H_REQUIRE_STREQ_MSG(1_2, "1", "2", "1 does not match 2"); +H_REQUIRE_STREQ_MSG(2_1, "2", "1", "2 does not match 1"); +H_REQUIRE_STREQ_MSG(2_2, "2", "2", "2 does not match 2"); +#define REQUIRE_STREQ_VAR1 "5" +#define REQUIRE_STREQ_VAR2 "9" +const char *require_streq_var1 = REQUIRE_STREQ_VAR1; +const char *require_streq_var2 = REQUIRE_STREQ_VAR2; +H_REQUIRE_STREQ(vars, require_streq_var1, require_streq_var2); + +ATF_TC(require_streq); +ATF_TC_HEAD(require_streq, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the ATF_REQUIRE_STREQ and " + "ATF_REQUIRE_STREQ_MSG macros"); +} +ATF_TC_BODY(require_streq, tc) +{ + struct require_eq_test tests[] = { + { H_REQUIRE_STREQ_HEAD_NAME(1_1), H_REQUIRE_STREQ_BODY_NAME(1_1), + "1", "1", "\"1\" != \"1\" \\(1 != 1\\)", true }, + { H_REQUIRE_STREQ_HEAD_NAME(1_2), H_REQUIRE_STREQ_BODY_NAME(1_2), + "1", "2", "\"1\" != \"2\" \\(1 != 2\\)", false }, + { H_REQUIRE_STREQ_HEAD_NAME(2_1), H_REQUIRE_STREQ_BODY_NAME(2_1), + "2", "1", "\"2\" != \"1\" \\(2 != 1\\)", false }, + { H_REQUIRE_STREQ_HEAD_NAME(2_2), H_REQUIRE_STREQ_BODY_NAME(2_2), + "2", "2", "\"2\" != \"2\" \\(2 != 2\\)", true }, + { H_REQUIRE_STREQ_MSG_HEAD_NAME(1_1), + H_REQUIRE_STREQ_MSG_BODY_NAME(1_1), + "1", "1", "\"1\" != \"1\" \\(1 != 1\\): 1 does not match 1", true }, + { H_REQUIRE_STREQ_MSG_HEAD_NAME(1_2), + H_REQUIRE_STREQ_MSG_BODY_NAME(1_2), + "1", "2", "\"1\" != \"2\" \\(1 != 2\\): 1 does not match 2", false }, + { H_REQUIRE_STREQ_MSG_HEAD_NAME(2_1), + H_REQUIRE_STREQ_MSG_BODY_NAME(2_1), + "2", "1", "\"2\" != \"1\" \\(2 != 1\\): 2 does not match 1", false }, + { H_REQUIRE_STREQ_MSG_HEAD_NAME(2_2), + H_REQUIRE_STREQ_MSG_BODY_NAME(2_2), + "2", "2", "\"2\" != \"2\" \\(2 != 2\\): 2 does not match 2", true }, + { H_REQUIRE_STREQ_HEAD_NAME(vars), H_REQUIRE_STREQ_BODY_NAME(vars), + require_streq_var1, require_streq_var2, + "require_streq_var1 != require_streq_var2 \\(" + REQUIRE_STREQ_VAR1 " != " REQUIRE_STREQ_VAR2 "\\)", false }, + { NULL, NULL, 0, 0, "", false } + }; + do_require_eq_tests(tests); +} + +/* --------------------------------------------------------------------- + * Test cases for the ATF_REQUIRE_MATCH and ATF_REQUIRE_MATCH_MSG macros. + * --------------------------------------------------------------------- */ + +H_REQUIRE_MATCH(yes, "hello [a-z]+", "abc hello world"); +H_REQUIRE_MATCH(no, "hello [a-z]+", "abc hello WORLD"); +H_REQUIRE_MATCH_MSG(yes, "hello [a-z]+", "abc hello world", "lowercase"); +H_REQUIRE_MATCH_MSG(no, "hello [a-z]+", "abc hello WORLD", "uppercase"); + +ATF_TC(require_match); +ATF_TC_HEAD(require_match, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the ATF_REQUIRE_MATCH and " + "ATF_REQUIRE_MATCH_MSG macros"); +} +ATF_TC_BODY(require_match, tc) +{ + struct require_eq_test tests[] = { + { H_REQUIRE_MATCH_HEAD_NAME(yes), H_REQUIRE_MATCH_BODY_NAME(yes), + "hello [a-z]+", "abc hello world", "", true }, + { H_REQUIRE_MATCH_HEAD_NAME(no), H_REQUIRE_MATCH_BODY_NAME(no), + "hello [a-z]+", "abc hello WORLD", + "'hello \\[a-z\\]\\+' not matched in 'abc hello WORLD'", false }, + { H_REQUIRE_MATCH_MSG_HEAD_NAME(yes), + H_REQUIRE_MATCH_MSG_BODY_NAME(yes), + "hello [a-z]+", "abc hello world", "", true }, + { H_REQUIRE_MATCH_MSG_HEAD_NAME(no), H_REQUIRE_MATCH_MSG_BODY_NAME(no), + "hello [a-z]+", "abc hello WORLD", + "'hello \\[a-z\\]\\+' not matched in 'abc hello WORLD': uppercase", + false }, + { NULL, NULL, 0, 0, "", false } + }; + do_require_eq_tests(tests); +} + +/* --------------------------------------------------------------------- + * Miscellaneous test cases covering several macros. + * --------------------------------------------------------------------- */ + +static +bool +aux_bool(const char *fmt ATF_DEFS_ATTRIBUTE_UNUSED) +{ + return false; +} + +static +const char * +aux_str(const char *fmt ATF_DEFS_ATTRIBUTE_UNUSED) +{ + return "foo"; +} + +H_CHECK(msg, aux_bool("%d")); +H_REQUIRE(msg, aux_bool("%d")); +H_CHECK_STREQ(msg, aux_str("%d"), ""); +H_REQUIRE_STREQ(msg, aux_str("%d"), ""); + +ATF_TC(msg_embedded_fmt); +ATF_TC_HEAD(msg_embedded_fmt, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests that format strings passed " + "as part of the automatically-generated messages " + "do not get expanded"); +} +ATF_TC_BODY(msg_embedded_fmt, tc) +{ + struct test { + void (*head)(atf_tc_t *); + void (*body)(const atf_tc_t *); + bool fatal; + const char *msg; + } *t, tests[] = { + { H_CHECK_HEAD_NAME(msg), H_CHECK_BODY_NAME(msg), false, + "aux_bool\\(\"%d\"\\) not met" }, + { H_REQUIRE_HEAD_NAME(msg), H_REQUIRE_BODY_NAME(msg), true, + "aux_bool\\(\"%d\"\\) not met" }, + { H_CHECK_STREQ_HEAD_NAME(msg), H_CHECK_STREQ_BODY_NAME(msg), false, + "aux_str\\(\"%d\"\\) != \"\" \\(foo != \\)" }, + { H_REQUIRE_STREQ_HEAD_NAME(msg), H_REQUIRE_STREQ_BODY_NAME(msg), true, + "aux_str\\(\"%d\"\\) != \"\" \\(foo != \\)" }, + { NULL, NULL, false, NULL } + }; + + for (t = &tests[0]; t->head != NULL; t++) { + printf("Checking with an expected '%s' message\n", t->msg); + + init_and_run_h_tc("h_check", t->head, t->body); + + if (t->fatal) { + bool matched = + atf_utils_grep_file( + "^failed: .*macros_test.c:[0-9]+: %s$", "result", t->msg); + ATF_CHECK_MSG(matched, "couldn't find error string in result"); + } else { + bool matched = atf_utils_grep_file("Check failed: .*" + "macros_test.c:[0-9]+: %s$", "error", t->msg); + ATF_CHECK_MSG(matched, "couldn't find error string in output"); + } + } +} + +/* --------------------------------------------------------------------- + * Tests cases for the header file. + * --------------------------------------------------------------------- */ + +BUILD_TC(use, "macros_h_test.c", + "Tests that the macros provided by the atf-c/macros.h file " + "do not cause syntax errors when used", + "Build of macros_h_test.c failed; some macros in atf-c/macros.h " + "are broken"); + +ATF_TC(detect_unused_tests); +ATF_TC_HEAD(detect_unused_tests, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Tests that defining an unused test case raises a " + "warning (and thus an error)"); +} +ATF_TC_BODY(detect_unused_tests, tc) +{ + const char* validate_compiler = + "struct test_struct { int dummy; };\n" + "#define define_unused static struct test_struct unused\n" + "define_unused;\n"; + + atf_utils_create_file("compiler_test.c", "%s", validate_compiler); + if (build_check_c_o("compiler_test.c")) + atf_tc_expect_fail("Compiler does not raise a warning on an unused " + "static global variable declared by a macro"); + + if (build_check_c_o_srcdir(tc, "unused_test.c")) + atf_tc_fail("Build of unused_test.c passed; unused test cases are " + "not properly detected"); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, check); + ATF_TP_ADD_TC(tp, check_eq); + ATF_TP_ADD_TC(tp, check_streq); + ATF_TP_ADD_TC(tp, check_errno); + ATF_TP_ADD_TC(tp, check_match); + + ATF_TP_ADD_TC(tp, require); + ATF_TP_ADD_TC(tp, require_eq); + ATF_TP_ADD_TC(tp, require_streq); + ATF_TP_ADD_TC(tp, require_errno); + ATF_TP_ADD_TC(tp, require_match); + + ATF_TP_ADD_TC(tp, msg_embedded_fmt); + + /* Add the test cases for the header file. */ + ATF_TP_ADD_TC(tp, use); + ATF_TP_ADD_TC(tp, detect_unused_tests); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/pkg_config_test.sh b/unit/atf-src/atf-c/pkg_config_test.sh new file mode 100644 index 0000000..ddffdb0 --- /dev/null +++ b/unit/atf-src/atf-c/pkg_config_test.sh @@ -0,0 +1,143 @@ +# Copyright (c) 2008 The NetBSD Foundation, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND +# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# The following tests assume that the atf-c.pc file is installed in a +# directory that is known by pkg-config. Otherwise they will fail, +# and you will be required to adjust PKG_CONFIG_PATH accordingly. +# +# It would be possible to bypass this requirement by setting the path +# explicitly during the tests, but then this would not do a real check +# to ensure that the installation is working. + +require_pc() +{ + pkg-config ${1} || atf_fail "pkg-config could not locate ${1}.pc;" \ + "maybe need to set PKG_CONFIG_PATH?" +} + +check_version() +{ + ver1=$($(atf_get_srcdir)/detail/version_helper) + echo "Version reported by builtin PACKAGE_VERSION: ${ver1}" + + atf_check -s eq:0 -o save:stdout -e empty pkg-config --modversion "${1}" + ver2=$(cat stdout) + echo "Version reported by pkg-config: ${ver2}" + + atf_check_equal ${ver1} ${ver2} +} + +atf_test_case version +version_head() +{ + atf_set "descr" "Checks that the version in atf-c is correct" + atf_set "require.progs" "pkg-config" +} +version_body() +{ + require_pc "atf-c" + + check_version "atf-c" +} + +atf_test_case build +build_head() +{ + atf_set "descr" "Checks that a test program can be built against" \ + "the C library based on the pkg-config information" + atf_set "require.progs" "pkg-config" +} +build_body() +{ + require_pc "atf-c" + + atf_check -s eq:0 -o save:stdout -e empty pkg-config --variable=cc atf-c + cc=$(cat stdout) + echo "Compiler is: ${cxx}" + atf_require_prog ${cxx} + + cat >tp.c <<EOF +#include <stdio.h> + +#include <atf-c.h> + +ATF_TC(tc); +ATF_TC_HEAD(tc, tc) { + atf_tc_set_md_var(tc, "descr", "A test case"); +} +ATF_TC_BODY(tc, tc) { + printf("Running\n"); +} + +ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, tc); + + return atf_no_error(); +} +EOF + + atf_check -s eq:0 -o save:stdout -e empty pkg-config --cflags atf-c + cflags=$(cat stdout) + echo "CFLAGS are: ${cflags}" + + atf_check -s eq:0 -o save:stdout -e empty \ + pkg-config --libs-only-L --libs-only-other atf-c + ldflags=$(cat stdout) + atf_check -s eq:0 -o save:stdout -e empty pkg-config --libs-only-l atf-c + libs=$(cat stdout) + echo "LDFLAGS are: ${ldflags}" + echo "LIBS are: ${libs}" + + atf_check -s eq:0 -o empty -e empty ${cc} ${cflags} -o tp.o -c tp.c + atf_check -s eq:0 -o empty -e empty ${cc} ${ldflags} -o tp tp.o ${libs} + + libpath= + for f in ${ldflags}; do + case ${f} in + -L*) + dir=$(echo ${f} | sed -e 's,^-L,,') + if [ -z "${libpath}" ]; then + libpath="${dir}" + else + libpath="${libpath}:${dir}" + fi + ;; + *) + ;; + esac + done + + atf_check -s eq:0 -o empty -e empty test -x tp + atf_check -s eq:0 -o match:'Running' -e empty -x \ + "LD_LIBRARY_PATH=${libpath} ./tp tc" +} + +atf_init_test_cases() +{ + atf_add_test_case version + atf_add_test_case build +} + +# vim: syntax=sh:expandtab:shiftwidth=4:softtabstop=4 diff --git a/unit/atf-src/atf-c/tc.c b/unit/atf-src/atf-c/tc.c new file mode 100644 index 0000000..f75d059 --- /dev/null +++ b/unit/atf-src/atf-c/tc.c @@ -0,0 +1,1219 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/tc.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/uio.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "atf-c/defs.h" +#include "atf-c/detail/env.h" +#include "atf-c/detail/fs.h" +#include "atf-c/detail/map.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/detail/text.h" +#include "atf-c/error.h" + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +enum expect_type { + EXPECT_PASS, + EXPECT_FAIL, + EXPECT_EXIT, + EXPECT_SIGNAL, + EXPECT_DEATH, + EXPECT_TIMEOUT, +}; + +struct context { + const atf_tc_t *tc; + const char *resfile; + size_t fail_count; + + enum expect_type expect; + atf_dynstr_t expect_reason; + size_t expect_previous_fail_count; + size_t expect_fail_count; + int expect_exitcode; + int expect_signo; +}; + +static void context_init(struct context *, const atf_tc_t *, const char *); +static void check_fatal_error(atf_error_t); +static void report_fatal_error(const char *, ...) + ATF_DEFS_ATTRIBUTE_NORETURN; +static atf_error_t write_resfile(const int, const char *, const int, + const atf_dynstr_t *); +static void create_resfile(const char *, const char *, const int, + atf_dynstr_t *); +static void error_in_expect(struct context *, const char *, ...) + ATF_DEFS_ATTRIBUTE_NORETURN; +static void validate_expect(struct context *); +static void expected_failure(struct context *, atf_dynstr_t *) + ATF_DEFS_ATTRIBUTE_NORETURN; +static void fail_requirement(struct context *, atf_dynstr_t *) + ATF_DEFS_ATTRIBUTE_NORETURN; +static void fail_check(struct context *, atf_dynstr_t *); +static void pass(struct context *) + ATF_DEFS_ATTRIBUTE_NORETURN; +static void skip(struct context *, atf_dynstr_t *) + ATF_DEFS_ATTRIBUTE_NORETURN; +static void format_reason_ap(atf_dynstr_t *, const char *, const size_t, + const char *, va_list); +static void format_reason_fmt(atf_dynstr_t *, const char *, const size_t, + const char *, ...); +static void errno_test(struct context *, const char *, const size_t, + const int, const char *, const bool, + void (*)(struct context *, atf_dynstr_t *)); +static atf_error_t check_prog_in_dir(const char *, void *); +static atf_error_t check_prog(struct context *, const char *); + +static void +context_init(struct context *ctx, const atf_tc_t *tc, const char *resfile) +{ + ctx->tc = tc; + ctx->resfile = resfile; + ctx->fail_count = 0; + ctx->expect = EXPECT_PASS; + check_fatal_error(atf_dynstr_init(&ctx->expect_reason)); + ctx->expect_previous_fail_count = 0; + ctx->expect_fail_count = 0; + ctx->expect_exitcode = 0; + ctx->expect_signo = 0; +} + +static void +check_fatal_error(atf_error_t err) +{ + if (atf_is_error(err)) { + char buf[1024]; + atf_error_format(err, buf, sizeof(buf)); + fprintf(stderr, "FATAL ERROR: %s\n", buf); + atf_error_free(err); + abort(); + } +} + +static void +report_fatal_error(const char *msg, ...) +{ + va_list ap; + fprintf(stderr, "FATAL ERROR: "); + + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); + + fprintf(stderr, "\n"); + abort(); +} + +/** Writes to a results file. + * + * The results file is supposed to be already open. + * + * This function returns an error code instead of exiting in case of error + * because the caller needs to clean up the reason object before terminating. + */ +static atf_error_t +write_resfile(const int fd, const char *result, const int arg, + const atf_dynstr_t *reason) +{ + static char NL[] = "\n", CS[] = ": "; + char buf[64]; + const char *r; + struct iovec iov[5]; + ssize_t ret; + int count = 0; + + INV(arg == -1 || reason != NULL); + +#define UNCONST(a) ((void *)(unsigned long)(const void *)(a)) + iov[count].iov_base = UNCONST(result); + iov[count++].iov_len = strlen(result); + + if (reason != NULL) { + if (arg != -1) { + iov[count].iov_base = buf; + iov[count++].iov_len = snprintf(buf, sizeof(buf), "(%d)", arg); + } + + iov[count].iov_base = CS; + iov[count++].iov_len = sizeof(CS) - 1; + + r = atf_dynstr_cstring(reason); + iov[count].iov_base = UNCONST(r); + iov[count++].iov_len = strlen(r); + } +#undef UNCONST + + iov[count].iov_base = NL; + iov[count++].iov_len = sizeof(NL) - 1; + + while ((ret = writev(fd, iov, count)) == -1 && errno == EINTR) + continue; /* Retry. */ + if (ret != -1) + return atf_no_error(); + + return atf_libc_error( + errno, "Failed to write results file; result %s, reason %s", result, + reason == NULL ? "null" : atf_dynstr_cstring(reason)); +} + +/** Creates a results file. + * + * The input reason is released in all cases. + * + * An error in this function is considered to be fatal, hence why it does + * not return any error code. + */ +static void +create_resfile(const char *resfile, const char *result, const int arg, + atf_dynstr_t *reason) +{ + atf_error_t err; + + if (strcmp("/dev/stdout", resfile) == 0) { + err = write_resfile(STDOUT_FILENO, result, arg, reason); + } else if (strcmp("/dev/stderr", resfile) == 0) { + err = write_resfile(STDERR_FILENO, result, arg, reason); + } else { + const int fd = open(resfile, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd == -1) { + err = atf_libc_error(errno, "Cannot create results file '%s'", + resfile); + } else { + err = write_resfile(fd, result, arg, reason); + close(fd); + } + } + + if (reason != NULL) + atf_dynstr_fini(reason); + + check_fatal_error(err); +} + +/** Fails a test case if validate_expect fails. */ +static void +error_in_expect(struct context *ctx, const char *fmt, ...) +{ + atf_dynstr_t reason; + va_list ap; + + va_start(ap, fmt); + format_reason_ap(&reason, NULL, 0, fmt, ap); + va_end(ap); + + ctx->expect = EXPECT_PASS; /* Ensure fail_requirement really fails. */ + fail_requirement(ctx, &reason); +} + +/** Ensures that the "expect" state is correct. + * + * Call this function before modifying the current value of expect. + */ +static void +validate_expect(struct context *ctx) +{ + if (ctx->expect == EXPECT_DEATH) { + error_in_expect(ctx, "Test case was expected to terminate abruptly " + "but it continued execution"); + } else if (ctx->expect == EXPECT_EXIT) { + error_in_expect(ctx, "Test case was expected to exit cleanly but it " + "continued execution"); + } else if (ctx->expect == EXPECT_FAIL) { + if (ctx->expect_fail_count == ctx->expect_previous_fail_count) + error_in_expect(ctx, "Test case was expecting a failure but none " + "were raised"); + else + INV(ctx->expect_fail_count > ctx->expect_previous_fail_count); + } else if (ctx->expect == EXPECT_PASS) { + /* Nothing to validate. */ + } else if (ctx->expect == EXPECT_SIGNAL) { + error_in_expect(ctx, "Test case was expected to receive a termination " + "signal but it continued execution"); + } else if (ctx->expect == EXPECT_TIMEOUT) { + error_in_expect(ctx, "Test case was expected to hang but it continued " + "execution"); + } else + UNREACHABLE; +} + +static void +expected_failure(struct context *ctx, atf_dynstr_t *reason) +{ + check_fatal_error(atf_dynstr_prepend_fmt(reason, "%s: ", + atf_dynstr_cstring(&ctx->expect_reason))); + create_resfile(ctx->resfile, "expected_failure", -1, reason); + exit(EXIT_SUCCESS); +} + +static void +fail_requirement(struct context *ctx, atf_dynstr_t *reason) +{ + if (ctx->expect == EXPECT_FAIL) { + expected_failure(ctx, reason); + } else if (ctx->expect == EXPECT_PASS) { + create_resfile(ctx->resfile, "failed", -1, reason); + exit(EXIT_FAILURE); + } else { + error_in_expect(ctx, "Test case raised a failure but was not " + "expecting one; reason was %s", atf_dynstr_cstring(reason)); + } + UNREACHABLE; +} + +static void +fail_check(struct context *ctx, atf_dynstr_t *reason) +{ + if (ctx->expect == EXPECT_FAIL) { + fprintf(stderr, "*** Expected check failure: %s: %s\n", + atf_dynstr_cstring(&ctx->expect_reason), + atf_dynstr_cstring(reason)); + ctx->expect_fail_count++; + } else if (ctx->expect == EXPECT_PASS) { + fprintf(stderr, "*** Check failed: %s\n", atf_dynstr_cstring(reason)); + ctx->fail_count++; + } else { + error_in_expect(ctx, "Test case raised a failure but was not " + "expecting one; reason was %s", atf_dynstr_cstring(reason)); + } + + atf_dynstr_fini(reason); +} + +static void +pass(struct context *ctx) +{ + if (ctx->expect == EXPECT_FAIL) { + error_in_expect(ctx, "Test case was expecting a failure but got " + "a pass instead"); + } else if (ctx->expect == EXPECT_PASS) { + create_resfile(ctx->resfile, "passed", -1, NULL); + exit(EXIT_SUCCESS); + } else { + error_in_expect(ctx, "Test case asked to explicitly pass but was " + "not expecting such condition"); + } + UNREACHABLE; +} + +static void +skip(struct context *ctx, atf_dynstr_t *reason) +{ + if (ctx->expect == EXPECT_PASS) { + create_resfile(ctx->resfile, "skipped", -1, reason); + exit(EXIT_SUCCESS); + } else { + error_in_expect(ctx, "Can only skip a test case when running in " + "expect pass mode"); + } + UNREACHABLE; +} + +/** Formats a failure/skip reason message. + * + * The formatted reason is stored in out_reason. out_reason is initialized + * in this function and is supposed to be released by the caller. In general, + * the reason will eventually be fed to create_resfile, which will release + * it. + * + * Errors in this function are fatal. Rationale being: reasons are used to + * create results files; if we can't format the reason correctly, the result + * of the test program will be bogus. So it's better to just exit with a + * fatal error. + */ +static void +format_reason_ap(atf_dynstr_t *out_reason, + const char *source_file, const size_t source_line, + const char *reason, va_list ap) +{ + atf_error_t err; + + if (source_file != NULL) { + err = atf_dynstr_init_fmt(out_reason, "%s:%zd: ", source_file, + source_line); + } else { + PRE(source_line == 0); + err = atf_dynstr_init(out_reason); + } + + if (!atf_is_error(err)) { + va_list ap2; + va_copy(ap2, ap); + err = atf_dynstr_append_ap(out_reason, reason, ap2); + va_end(ap2); + } + + check_fatal_error(err); +} + +static void +format_reason_fmt(atf_dynstr_t *out_reason, + const char *source_file, const size_t source_line, + const char *reason, ...) +{ + va_list ap; + + va_start(ap, reason); + format_reason_ap(out_reason, source_file, source_line, reason, ap); + va_end(ap); +} + +static void +errno_test(struct context *ctx, const char *file, const size_t line, + const int exp_errno, const char *expr_str, + const bool expr_result, + void (*fail_func)(struct context *, atf_dynstr_t *)) +{ + const int actual_errno = errno; + + if (expr_result) { + if (exp_errno != actual_errno) { + atf_dynstr_t reason; + + format_reason_fmt(&reason, file, line, "Expected errno %d, got %d, " + "in %s", exp_errno, actual_errno, expr_str); + fail_func(ctx, &reason); + } + } else { + atf_dynstr_t reason; + + format_reason_fmt(&reason, file, line, "Expected true value in %s", + expr_str); + fail_func(ctx, &reason); + } +} + +struct prog_found_pair { + const char *prog; + bool found; +}; + +static atf_error_t +check_prog_in_dir(const char *dir, void *data) +{ + struct prog_found_pair *pf = data; + atf_error_t err; + + if (pf->found) + err = atf_no_error(); + else { + atf_fs_path_t p; + + err = atf_fs_path_init_fmt(&p, "%s/%s", dir, pf->prog); + if (atf_is_error(err)) + goto out_p; + + err = atf_fs_eaccess(&p, atf_fs_access_x); + if (!atf_is_error(err)) + pf->found = true; + else { + atf_error_free(err); + INV(!pf->found); + err = atf_no_error(); + } + +out_p: + atf_fs_path_fini(&p); + } + + return err; +} + +static atf_error_t +check_prog(struct context *ctx, const char *prog) +{ + atf_error_t err; + atf_fs_path_t p; + + err = atf_fs_path_init_fmt(&p, "%s", prog); + if (atf_is_error(err)) + goto out; + + if (atf_fs_path_is_absolute(&p)) { + err = atf_fs_eaccess(&p, atf_fs_access_x); + if (atf_is_error(err)) { + atf_dynstr_t reason; + + atf_error_free(err); + atf_fs_path_fini(&p); + format_reason_fmt(&reason, NULL, 0, "The required program %s could " + "not be found", prog); + skip(ctx, &reason); + } + } else { + const char *path = atf_env_get("PATH"); + struct prog_found_pair pf; + atf_fs_path_t bp; + + err = atf_fs_path_branch_path(&p, &bp); + if (atf_is_error(err)) + goto out_p; + + if (strcmp(atf_fs_path_cstring(&bp), ".") != 0) { + atf_fs_path_fini(&bp); + atf_fs_path_fini(&p); + + report_fatal_error("Relative paths are not allowed when searching " + "for a program (%s)", prog); + UNREACHABLE; + } + + pf.prog = prog; + pf.found = false; + err = atf_text_for_each_word(path, ":", check_prog_in_dir, &pf); + if (atf_is_error(err)) + goto out_bp; + + if (!pf.found) { + atf_dynstr_t reason; + + atf_fs_path_fini(&bp); + atf_fs_path_fini(&p); + format_reason_fmt(&reason, NULL, 0, "The required program %s could " + "not be found in the PATH", prog); + fail_requirement(ctx, &reason); + } + +out_bp: + atf_fs_path_fini(&bp); + } + +out_p: + atf_fs_path_fini(&p); +out: + return err; +} + +/* --------------------------------------------------------------------- + * The "atf_tc" type. + * --------------------------------------------------------------------- */ + +struct atf_tc_impl { + const char *m_ident; + + atf_map_t m_vars; + atf_map_t m_config; + + atf_tc_head_t m_head; + atf_tc_body_t m_body; + atf_tc_cleanup_t m_cleanup; +}; + +/* + * Constructors/destructors. + */ + +atf_error_t +atf_tc_init(atf_tc_t *tc, const char *ident, atf_tc_head_t head, + atf_tc_body_t body, atf_tc_cleanup_t cleanup, + const char *const *config) +{ + atf_error_t err; + + tc->pimpl = malloc(sizeof(struct atf_tc_impl)); + if (tc->pimpl == NULL) { + err = atf_no_memory_error(); + goto err; + } + + tc->pimpl->m_ident = ident; + tc->pimpl->m_head = head; + tc->pimpl->m_body = body; + tc->pimpl->m_cleanup = cleanup; + + err = atf_map_init_charpp(&tc->pimpl->m_config, config); + if (atf_is_error(err)) + goto err; + + err = atf_map_init(&tc->pimpl->m_vars); + if (atf_is_error(err)) + goto err_vars; + + err = atf_tc_set_md_var(tc, "ident", ident); + if (atf_is_error(err)) + goto err_map; + + if (cleanup != NULL) { + err = atf_tc_set_md_var(tc, "has.cleanup", "true"); + if (atf_is_error(err)) + goto err_map; + } + + /* XXX Should the head be able to return error codes? */ + if (tc->pimpl->m_head != NULL) + tc->pimpl->m_head(tc); + + if (strcmp(atf_tc_get_md_var(tc, "ident"), ident) != 0) { + report_fatal_error("Test case head modified the read-only 'ident' " + "property"); + UNREACHABLE; + } + + INV(!atf_is_error(err)); + return err; + +err_map: + atf_map_fini(&tc->pimpl->m_vars); +err_vars: + atf_map_fini(&tc->pimpl->m_config); +err: + return err; +} + +atf_error_t +atf_tc_init_pack(atf_tc_t *tc, const atf_tc_pack_t *pack, + const char *const *config) +{ + return atf_tc_init(tc, pack->m_ident, pack->m_head, pack->m_body, + pack->m_cleanup, config); +} + +void +atf_tc_fini(atf_tc_t *tc) +{ + atf_map_fini(&tc->pimpl->m_vars); + atf_map_fini(&tc->pimpl->m_config); + free(tc->pimpl); + tc->pimpl = NULL; +} + +/* + * Getters. + */ + +const char * +atf_tc_get_ident(const atf_tc_t *tc) +{ + return tc->pimpl->m_ident; +} + +const char * +atf_tc_get_config_var(const atf_tc_t *tc, const char *name) +{ + const char *val; + atf_map_citer_t iter; + + PRE(atf_tc_has_config_var(tc, name)); + iter = atf_map_find_c(&tc->pimpl->m_config, name); + val = atf_map_citer_data(iter); + INV(val != NULL); + + return val; +} + +const char * +atf_tc_get_config_var_wd(const atf_tc_t *tc, const char *name, + const char *defval) +{ + const char *val; + + if (!atf_tc_has_config_var(tc, name)) + val = defval; + else + val = atf_tc_get_config_var(tc, name); + + return val; +} + +bool +atf_tc_get_config_var_as_bool(const atf_tc_t *tc, const char *name) +{ + bool val; + const char *strval; + atf_error_t err; + + strval = atf_tc_get_config_var(tc, name); + err = atf_text_to_bool(strval, &val); + if (atf_is_error(err)) { + atf_error_free(err); + atf_tc_fail("Configuration variable %s does not have a valid " + "boolean value; found %s", name, strval); + } + + return val; +} + +bool +atf_tc_get_config_var_as_bool_wd(const atf_tc_t *tc, const char *name, + const bool defval) +{ + bool val; + + if (!atf_tc_has_config_var(tc, name)) + val = defval; + else + val = atf_tc_get_config_var_as_bool(tc, name); + + return val; +} + +long +atf_tc_get_config_var_as_long(const atf_tc_t *tc, const char *name) +{ + long val; + const char *strval; + atf_error_t err; + + strval = atf_tc_get_config_var(tc, name); + err = atf_text_to_long(strval, &val); + if (atf_is_error(err)) { + atf_error_free(err); + atf_tc_fail("Configuration variable %s does not have a valid " + "long value; found %s", name, strval); + } + + return val; +} + +long +atf_tc_get_config_var_as_long_wd(const atf_tc_t *tc, const char *name, + const long defval) +{ + long val; + + if (!atf_tc_has_config_var(tc, name)) + val = defval; + else + val = atf_tc_get_config_var_as_long(tc, name); + + return val; +} + +const char * +atf_tc_get_md_var(const atf_tc_t *tc, const char *name) +{ + const char *val; + atf_map_citer_t iter; + + PRE(atf_tc_has_md_var(tc, name)); + iter = atf_map_find_c(&tc->pimpl->m_vars, name); + val = atf_map_citer_data(iter); + INV(val != NULL); + + return val; +} + +char ** +atf_tc_get_md_vars(const atf_tc_t *tc) +{ + return atf_map_to_charpp(&tc->pimpl->m_vars); +} + +bool +atf_tc_has_config_var(const atf_tc_t *tc, const char *name) +{ + atf_map_citer_t end, iter; + + iter = atf_map_find_c(&tc->pimpl->m_config, name); + end = atf_map_end_c(&tc->pimpl->m_config); + return !atf_equal_map_citer_map_citer(iter, end); +} + +bool +atf_tc_has_md_var(const atf_tc_t *tc, const char *name) +{ + atf_map_citer_t end, iter; + + iter = atf_map_find_c(&tc->pimpl->m_vars, name); + end = atf_map_end_c(&tc->pimpl->m_vars); + return !atf_equal_map_citer_map_citer(iter, end); +} + +/* + * Modifiers. + */ + +atf_error_t +atf_tc_set_md_var(atf_tc_t *tc, const char *name, const char *fmt, ...) +{ + atf_error_t err; + char *value; + va_list ap; + + va_start(ap, fmt); + err = atf_text_format_ap(&value, fmt, ap); + va_end(ap); + + if (!atf_is_error(err)) + err = atf_map_insert(&tc->pimpl->m_vars, name, value, true); + else + free(value); + + return err; +} + +/* --------------------------------------------------------------------- + * Free functions, as they should be publicly but they can't. + * --------------------------------------------------------------------- */ + +static void _atf_tc_fail(struct context *, const char *, va_list) + ATF_DEFS_ATTRIBUTE_NORETURN; +static void _atf_tc_fail_nonfatal(struct context *, const char *, va_list); +static void _atf_tc_fail_check(struct context *, const char *, const size_t, + const char *, va_list); +static void _atf_tc_fail_requirement(struct context *, const char *, + const size_t, const char *, va_list) ATF_DEFS_ATTRIBUTE_NORETURN; +static void _atf_tc_pass(struct context *) ATF_DEFS_ATTRIBUTE_NORETURN; +static void _atf_tc_require_prog(struct context *, const char *); +static void _atf_tc_skip(struct context *, const char *, va_list) + ATF_DEFS_ATTRIBUTE_NORETURN; +static void _atf_tc_check_errno(struct context *, const char *, const size_t, + const int, const char *, const bool); +static void _atf_tc_require_errno(struct context *, const char *, const size_t, + const int, const char *, const bool); +static void _atf_tc_expect_pass(struct context *); +static void _atf_tc_expect_fail(struct context *, const char *, va_list); +static void _atf_tc_expect_exit(struct context *, const int, const char *, + va_list); +static void _atf_tc_expect_signal(struct context *, const int, const char *, + va_list); +static void _atf_tc_expect_death(struct context *, const char *, + va_list); + +static void +_atf_tc_fail(struct context *ctx, const char *fmt, va_list ap) +{ + va_list ap2; + atf_dynstr_t reason; + + va_copy(ap2, ap); + format_reason_ap(&reason, NULL, 0, fmt, ap2); + va_end(ap2); + + fail_requirement(ctx, &reason); + UNREACHABLE; +} + +static void +_atf_tc_fail_nonfatal(struct context *ctx, const char *fmt, va_list ap) +{ + va_list ap2; + atf_dynstr_t reason; + + va_copy(ap2, ap); + format_reason_ap(&reason, NULL, 0, fmt, ap2); + va_end(ap2); + + fail_check(ctx, &reason); +} + +static void +_atf_tc_fail_check(struct context *ctx, const char *file, const size_t line, + const char *fmt, va_list ap) +{ + va_list ap2; + atf_dynstr_t reason; + + va_copy(ap2, ap); + format_reason_ap(&reason, file, line, fmt, ap2); + va_end(ap2); + + fail_check(ctx, &reason); +} + +static void +_atf_tc_fail_requirement(struct context *ctx, const char *file, + const size_t line, const char *fmt, va_list ap) +{ + va_list ap2; + atf_dynstr_t reason; + + va_copy(ap2, ap); + format_reason_ap(&reason, file, line, fmt, ap2); + va_end(ap2); + + fail_requirement(ctx, &reason); + UNREACHABLE; +} + +static void +_atf_tc_pass(struct context *ctx) +{ + pass(ctx); + UNREACHABLE; +} + +static void +_atf_tc_require_prog(struct context *ctx, const char *prog) +{ + check_fatal_error(check_prog(ctx, prog)); +} + +static void +_atf_tc_skip(struct context *ctx, const char *fmt, va_list ap) +{ + atf_dynstr_t reason; + va_list ap2; + + va_copy(ap2, ap); + format_reason_ap(&reason, NULL, 0, fmt, ap2); + va_end(ap2); + + skip(ctx, &reason); +} + +static void +_atf_tc_check_errno(struct context *ctx, const char *file, const size_t line, + const int exp_errno, const char *expr_str, + const bool expr_result) +{ + errno_test(ctx, file, line, exp_errno, expr_str, expr_result, fail_check); +} + +static void +_atf_tc_require_errno(struct context *ctx, const char *file, const size_t line, + const int exp_errno, const char *expr_str, + const bool expr_result) +{ + errno_test(ctx, file, line, exp_errno, expr_str, expr_result, + fail_requirement); +} + +static void +_atf_tc_expect_pass(struct context *ctx) +{ + validate_expect(ctx); + + ctx->expect = EXPECT_PASS; +} + +static void +_atf_tc_expect_fail(struct context *ctx, const char *reason, va_list ap) +{ + va_list ap2; + + validate_expect(ctx); + + ctx->expect = EXPECT_FAIL; + atf_dynstr_fini(&ctx->expect_reason); + va_copy(ap2, ap); + check_fatal_error(atf_dynstr_init_ap(&ctx->expect_reason, reason, ap2)); + va_end(ap2); + ctx->expect_previous_fail_count = ctx->expect_fail_count; +} + +static void +_atf_tc_expect_exit(struct context *ctx, const int exitcode, const char *reason, + va_list ap) +{ + va_list ap2; + atf_dynstr_t formatted; + + validate_expect(ctx); + + ctx->expect = EXPECT_EXIT; + va_copy(ap2, ap); + check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2)); + va_end(ap2); + + create_resfile(ctx->resfile, "expected_exit", exitcode, &formatted); +} + +static void +_atf_tc_expect_signal(struct context *ctx, const int signo, const char *reason, + va_list ap) +{ + va_list ap2; + atf_dynstr_t formatted; + + validate_expect(ctx); + + ctx->expect = EXPECT_SIGNAL; + va_copy(ap2, ap); + check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2)); + va_end(ap2); + + create_resfile(ctx->resfile, "expected_signal", signo, &formatted); +} + +static void +_atf_tc_expect_death(struct context *ctx, const char *reason, va_list ap) +{ + va_list ap2; + atf_dynstr_t formatted; + + validate_expect(ctx); + + ctx->expect = EXPECT_DEATH; + va_copy(ap2, ap); + check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2)); + va_end(ap2); + + create_resfile(ctx->resfile, "expected_death", -1, &formatted); +} + +static void +_atf_tc_expect_timeout(struct context *ctx, const char *reason, va_list ap) +{ + va_list ap2; + atf_dynstr_t formatted; + + validate_expect(ctx); + + ctx->expect = EXPECT_TIMEOUT; + va_copy(ap2, ap); + check_fatal_error(atf_dynstr_init_ap(&formatted, reason, ap2)); + va_end(ap2); + + create_resfile(ctx->resfile, "expected_timeout", -1, &formatted); +} + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +static struct context Current; + +atf_error_t +atf_tc_run(const atf_tc_t *tc, const char *resfile) +{ + context_init(&Current, tc, resfile); + + tc->pimpl->m_body(tc); + + validate_expect(&Current); + + if (Current.fail_count > 0) { + atf_dynstr_t reason; + + format_reason_fmt(&reason, NULL, 0, "%d checks failed; see output for " + "more details", Current.fail_count); + fail_requirement(&Current, &reason); + } else if (Current.expect_fail_count > 0) { + atf_dynstr_t reason; + + format_reason_fmt(&reason, NULL, 0, "%d checks failed as expected; " + "see output for more details", Current.expect_fail_count); + expected_failure(&Current, &reason); + } else { + pass(&Current); + } + UNREACHABLE; + return atf_no_error(); +} + +atf_error_t +atf_tc_cleanup(const atf_tc_t *tc) +{ + if (tc->pimpl->m_cleanup != NULL) + tc->pimpl->m_cleanup(tc); + return atf_no_error(); /* XXX */ +} + +/* --------------------------------------------------------------------- + * Free functions that depend on Current. + * --------------------------------------------------------------------- */ + +/* + * All the functions below provide delegates to other internal functions + * (prefixed by _) that take the current test case as an argument to + * prevent them from accessing global state. This is to keep the side- + * effects of the internal functions clearer and easier to understand. + * + * The public API should never have hid the fact that it needs access to + * the current test case (other than maybe in the macros), but changing it + * is hard. TODO: Revisit in the future. + */ + +void +atf_tc_fail(const char *fmt, ...) +{ + va_list ap; + + PRE(Current.tc != NULL); + + va_start(ap, fmt); + _atf_tc_fail(&Current, fmt, ap); + va_end(ap); +} + +void +atf_tc_fail_nonfatal(const char *fmt, ...) +{ + va_list ap; + + PRE(Current.tc != NULL); + + va_start(ap, fmt); + _atf_tc_fail_nonfatal(&Current, fmt, ap); + va_end(ap); +} + +void +atf_tc_fail_check(const char *file, const size_t line, const char *fmt, ...) +{ + va_list ap; + + PRE(Current.tc != NULL); + + va_start(ap, fmt); + _atf_tc_fail_check(&Current, file, line, fmt, ap); + va_end(ap); +} + +void +atf_tc_fail_requirement(const char *file, const size_t line, + const char *fmt, ...) +{ + va_list ap; + + PRE(Current.tc != NULL); + + va_start(ap, fmt); + _atf_tc_fail_requirement(&Current, file, line, fmt, ap); + va_end(ap); +} + +void +atf_tc_pass(void) +{ + PRE(Current.tc != NULL); + + _atf_tc_pass(&Current); +} + +void +atf_tc_require_prog(const char *prog) +{ + PRE(Current.tc != NULL); + + _atf_tc_require_prog(&Current, prog); +} + +void +atf_tc_skip(const char *fmt, ...) +{ + va_list ap; + + PRE(Current.tc != NULL); + + va_start(ap, fmt); + _atf_tc_skip(&Current, fmt, ap); + va_end(ap); +} + +void +atf_tc_check_errno(const char *file, const size_t line, const int exp_errno, + const char *expr_str, const bool expr_result) +{ + PRE(Current.tc != NULL); + + _atf_tc_check_errno(&Current, file, line, exp_errno, expr_str, + expr_result); +} + +void +atf_tc_require_errno(const char *file, const size_t line, const int exp_errno, + const char *expr_str, const bool expr_result) +{ + PRE(Current.tc != NULL); + + _atf_tc_require_errno(&Current, file, line, exp_errno, expr_str, + expr_result); +} + +void +atf_tc_expect_pass(void) +{ + PRE(Current.tc != NULL); + + _atf_tc_expect_pass(&Current); +} + +void +atf_tc_expect_fail(const char *reason, ...) +{ + va_list ap; + + PRE(Current.tc != NULL); + + va_start(ap, reason); + _atf_tc_expect_fail(&Current, reason, ap); + va_end(ap); +} + +void +atf_tc_expect_exit(const int exitcode, const char *reason, ...) +{ + va_list ap; + + PRE(Current.tc != NULL); + + va_start(ap, reason); + _atf_tc_expect_exit(&Current, exitcode, reason, ap); + va_end(ap); +} + +void +atf_tc_expect_signal(const int signo, const char *reason, ...) +{ + va_list ap; + + PRE(Current.tc != NULL); + + va_start(ap, reason); + _atf_tc_expect_signal(&Current, signo, reason, ap); + va_end(ap); +} + +void +atf_tc_expect_death(const char *reason, ...) +{ + va_list ap; + + PRE(Current.tc != NULL); + + va_start(ap, reason); + _atf_tc_expect_death(&Current, reason, ap); + va_end(ap); +} + +void +atf_tc_expect_timeout(const char *reason, ...) +{ + va_list ap; + + PRE(Current.tc != NULL); + + va_start(ap, reason); + _atf_tc_expect_timeout(&Current, reason, ap); + va_end(ap); +} diff --git a/unit/atf-src/atf-c/tc.h b/unit/atf-src/atf-c/tc.h new file mode 100644 index 0000000..3fb6955 --- /dev/null +++ b/unit/atf-src/atf-c/tc.h @@ -0,0 +1,136 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_TC_H) +#define ATF_C_TC_H + +#include <stdbool.h> +#include <stddef.h> + +#include <atf-c/defs.h> +#include <atf-c/error_fwd.h> + +struct atf_tc; + +typedef void (*atf_tc_head_t)(struct atf_tc *); +typedef void (*atf_tc_body_t)(const struct atf_tc *); +typedef void (*atf_tc_cleanup_t)(const struct atf_tc *); + +/* --------------------------------------------------------------------- + * The "atf_tc_pack" type. + * --------------------------------------------------------------------- */ + +/* For static initialization only. */ +struct atf_tc_pack { + const char *m_ident; + + const char *const *m_config; + + atf_tc_head_t m_head; + atf_tc_body_t m_body; + atf_tc_cleanup_t m_cleanup; +}; +typedef const struct atf_tc_pack atf_tc_pack_t; + +/* --------------------------------------------------------------------- + * The "atf_tc" type. + * --------------------------------------------------------------------- */ + +struct atf_tc_impl; +struct atf_tc { + struct atf_tc_impl *pimpl; +}; +typedef struct atf_tc atf_tc_t; + +/* Constructors/destructors. */ +atf_error_t atf_tc_init(atf_tc_t *, const char *, atf_tc_head_t, + atf_tc_body_t, atf_tc_cleanup_t, + const char *const *); +atf_error_t atf_tc_init_pack(atf_tc_t *, atf_tc_pack_t *, + const char *const *); +void atf_tc_fini(atf_tc_t *); + +/* Getters. */ +const char *atf_tc_get_ident(const atf_tc_t *); +const char *atf_tc_get_config_var(const atf_tc_t *, const char *); +const char *atf_tc_get_config_var_wd(const atf_tc_t *, const char *, + const char *); +bool atf_tc_get_config_var_as_bool(const atf_tc_t *, const char *); +bool atf_tc_get_config_var_as_bool_wd(const atf_tc_t *, const char *, + const bool); +long atf_tc_get_config_var_as_long(const atf_tc_t *, const char *); +long atf_tc_get_config_var_as_long_wd(const atf_tc_t *, const char *, + const long); +const char *atf_tc_get_md_var(const atf_tc_t *, const char *); +char **atf_tc_get_md_vars(const atf_tc_t *); +bool atf_tc_has_config_var(const atf_tc_t *, const char *); +bool atf_tc_has_md_var(const atf_tc_t *, const char *); + +/* Modifiers. */ +atf_error_t atf_tc_set_md_var(atf_tc_t *, const char *, const char *, ...); + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +atf_error_t atf_tc_run(const atf_tc_t *, const char *); +atf_error_t atf_tc_cleanup(const atf_tc_t *); + +/* To be run from test case bodies only. */ +void atf_tc_fail(const char *, ...) + ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 2) + ATF_DEFS_ATTRIBUTE_NORETURN; +void atf_tc_fail_nonfatal(const char *, ...) + ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 2); +void atf_tc_pass(void) + ATF_DEFS_ATTRIBUTE_NORETURN; +void atf_tc_require_prog(const char *); +void atf_tc_skip(const char *, ...) + ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 2) + ATF_DEFS_ATTRIBUTE_NORETURN; +void atf_tc_expect_pass(void); +void atf_tc_expect_fail(const char *, ...) + ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 2); +void atf_tc_expect_exit(const int, const char *, ...) + ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(2, 3); +void atf_tc_expect_signal(const int, const char *, ...) + ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(2, 3); +void atf_tc_expect_death(const char *, ...) + ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 2); +void atf_tc_expect_timeout(const char *, ...) + ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 2); + +/* To be run from test case bodies only; internal to macros.h. */ +void atf_tc_fail_check(const char *, const size_t, const char *, ...) + ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(3, 4); +void atf_tc_fail_requirement(const char *, const size_t, const char *, ...) + ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(3, 4) + ATF_DEFS_ATTRIBUTE_NORETURN; +void atf_tc_check_errno(const char *, const size_t, const int, + const char *, const bool); +void atf_tc_require_errno(const char *, const size_t, const int, + const char *, const bool); + +#endif /* !defined(ATF_C_TC_H) */ diff --git a/unit/atf-src/atf-c/tc_test.c b/unit/atf-src/atf-c/tc_test.c new file mode 100644 index 0000000..616c37e --- /dev/null +++ b/unit/atf-src/atf-c/tc_test.c @@ -0,0 +1,183 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/tc.h" + +#include <stdbool.h> +#include <string.h> + +#include <atf-c.h> + +#include "atf-c/detail/test_helpers.h" + +/* --------------------------------------------------------------------- + * Auxiliary test cases. + * --------------------------------------------------------------------- */ + +ATF_TC_HEAD(empty, tc) +{ + if (tc != NULL) {} +} +ATF_TC_BODY(empty, tc) +{ +} + +ATF_TC_HEAD(test_var, tc) +{ + atf_tc_set_md_var(tc, "test-var", "Test text"); +} + +/* --------------------------------------------------------------------- + * Test cases for the "atf_tc_t" type. + * --------------------------------------------------------------------- */ + +ATF_TC(init); +ATF_TC_HEAD(init, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_tc_init function"); +} +ATF_TC_BODY(init, tcin) +{ + atf_tc_t tc; + + RE(atf_tc_init(&tc, "test1", ATF_TC_HEAD_NAME(empty), + ATF_TC_BODY_NAME(empty), NULL, NULL)); + ATF_REQUIRE(strcmp(atf_tc_get_ident(&tc), "test1") == 0); + ATF_REQUIRE(!atf_tc_has_md_var(&tc, "test-var")); + atf_tc_fini(&tc); + + RE(atf_tc_init(&tc, "test2", ATF_TC_HEAD_NAME(test_var), + ATF_TC_BODY_NAME(empty), NULL, NULL)); + ATF_REQUIRE(strcmp(atf_tc_get_ident(&tc), "test2") == 0); + ATF_REQUIRE(atf_tc_has_md_var(&tc, "test-var")); + atf_tc_fini(&tc); +} + +ATF_TC(init_pack); +ATF_TC_HEAD(init_pack, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_tc_init_pack function"); +} +ATF_TC_BODY(init_pack, tcin) +{ + atf_tc_t tc; + atf_tc_pack_t tcp1 = { + .m_ident = "test1", + .m_head = ATF_TC_HEAD_NAME(empty), + .m_body = ATF_TC_BODY_NAME(empty), + .m_cleanup = NULL, + }; + atf_tc_pack_t tcp2 = { + .m_ident = "test2", + .m_head = ATF_TC_HEAD_NAME(test_var), + .m_body = ATF_TC_BODY_NAME(empty), + .m_cleanup = NULL, + }; + + RE(atf_tc_init_pack(&tc, &tcp1, NULL)); + ATF_REQUIRE(strcmp(atf_tc_get_ident(&tc), "test1") == 0); + ATF_REQUIRE(!atf_tc_has_md_var(&tc, "test-var")); + atf_tc_fini(&tc); + + RE(atf_tc_init_pack(&tc, &tcp2, NULL)); + ATF_REQUIRE(strcmp(atf_tc_get_ident(&tc), "test2") == 0); + ATF_REQUIRE(atf_tc_has_md_var(&tc, "test-var")); + atf_tc_fini(&tc); +} + +ATF_TC(vars); +ATF_TC_HEAD(vars, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_tc_get_md_var, " + "atf_tc_has_md_var and atf_tc_set_md_var functions"); +} +ATF_TC_BODY(vars, tcin) +{ + atf_tc_t tc; + + RE(atf_tc_init(&tc, "test1", ATF_TC_HEAD_NAME(empty), + ATF_TC_BODY_NAME(empty), NULL, NULL)); + ATF_REQUIRE(!atf_tc_has_md_var(&tc, "test-var")); + RE(atf_tc_set_md_var(&tc, "test-var", "Test value")); + ATF_REQUIRE(atf_tc_has_md_var(&tc, "test-var")); + ATF_REQUIRE(strcmp(atf_tc_get_md_var(&tc, "test-var"), "Test value") == 0); + atf_tc_fini(&tc); +} + +ATF_TC(config); +ATF_TC_HEAD(config, tc) +{ + atf_tc_set_md_var(tc, "descr", "Tests the atf_tc_get_config_var, " + "atf_tc_get_config_var_wd and atf_tc_has_config_var " + "functions"); +} +ATF_TC_BODY(config, tcin) +{ + atf_tc_t tc; + const char *const config[] = { "test-var", "test-value", NULL }; + + RE(atf_tc_init(&tc, "test1", ATF_TC_HEAD_NAME(empty), + ATF_TC_BODY_NAME(empty), NULL, NULL)); + ATF_REQUIRE(!atf_tc_has_config_var(&tc, "test-var")); + ATF_REQUIRE(!atf_tc_has_md_var(&tc, "test-var")); + atf_tc_fini(&tc); + + RE(atf_tc_init(&tc, "test1", ATF_TC_HEAD_NAME(empty), + ATF_TC_BODY_NAME(empty), NULL, config)); + ATF_REQUIRE(atf_tc_has_config_var(&tc, "test-var")); + ATF_REQUIRE(strcmp(atf_tc_get_config_var(&tc, "test-var"), + "test-value") == 0); + ATF_REQUIRE(!atf_tc_has_md_var(&tc, "test-var")); + ATF_REQUIRE(!atf_tc_has_config_var(&tc, "test-var2")); + ATF_REQUIRE(strcmp(atf_tc_get_config_var_wd(&tc, "test-var2", "def-value"), + "def-value") == 0); + atf_tc_fini(&tc); +} + +/* --------------------------------------------------------------------- + * Test cases for the free functions. + * --------------------------------------------------------------------- */ + +/* TODO: Add test cases for atf_tc_run. This is going to be very tough, + * but good tests here could allow us to avoid much of the indirect + * testing done later on. */ + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + /* Add the test cases for the "atf_tcr_t" type. */ + ATF_TP_ADD_TC(tp, init); + ATF_TP_ADD_TC(tp, init_pack); + ATF_TP_ADD_TC(tp, vars); + ATF_TP_ADD_TC(tp, config); + + /* Add the test cases for the free functions. */ + /* TODO */ + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/tp.c b/unit/atf-src/atf-c/tp.c new file mode 100644 index 0000000..f4f2257 --- /dev/null +++ b/unit/atf-src/atf-c/tp.c @@ -0,0 +1,213 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/tp.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "atf-c/detail/fs.h" +#include "atf-c/detail/map.h" +#include "atf-c/detail/sanity.h" +#include "atf-c/error.h" +#include "atf-c/tc.h" + +struct atf_tp_impl { + atf_list_t m_tcs; + atf_map_t m_config; +}; + +/* --------------------------------------------------------------------- + * Auxiliary functions. + * --------------------------------------------------------------------- */ + +static +const atf_tc_t * +find_tc(const atf_tp_t *tp, const char *ident) +{ + const atf_tc_t *tc; + atf_list_citer_t iter; + + tc = NULL; + atf_list_for_each_c(iter, &tp->pimpl->m_tcs) { + const atf_tc_t *tc2; + tc2 = atf_list_citer_data(iter); + if (strcmp(atf_tc_get_ident(tc2), ident) == 0) { + tc = tc2; + break; + } + } + return tc; +} + +/* --------------------------------------------------------------------- + * The "atf_tp" type. + * --------------------------------------------------------------------- */ + +/* + * Constructors/destructors. + */ + +atf_error_t +atf_tp_init(atf_tp_t *tp, const char *const *config) +{ + atf_error_t err; + + PRE(config != NULL); + + tp->pimpl = malloc(sizeof(struct atf_tp_impl)); + if (tp->pimpl == NULL) + return atf_no_memory_error(); + + err = atf_list_init(&tp->pimpl->m_tcs); + if (atf_is_error(err)) + goto out; + + err = atf_map_init_charpp(&tp->pimpl->m_config, config); + if (atf_is_error(err)) { + atf_list_fini(&tp->pimpl->m_tcs); + goto out; + } + + INV(!atf_is_error(err)); +out: + return err; +} + +void +atf_tp_fini(atf_tp_t *tp) +{ + atf_list_iter_t iter; + + atf_map_fini(&tp->pimpl->m_config); + + atf_list_for_each(iter, &tp->pimpl->m_tcs) { + atf_tc_t *tc = atf_list_iter_data(iter); + atf_tc_fini(tc); + } + atf_list_fini(&tp->pimpl->m_tcs); + + free(tp->pimpl); +} + +/* + * Getters. + */ + +char ** +atf_tp_get_config(const atf_tp_t *tp) +{ + return atf_map_to_charpp(&tp->pimpl->m_config); +} + +bool +atf_tp_has_tc(const atf_tp_t *tp, const char *id) +{ + const atf_tc_t *tc = find_tc(tp, id); + return tc != NULL; +} + +const atf_tc_t * +atf_tp_get_tc(const atf_tp_t *tp, const char *id) +{ + const atf_tc_t *tc = find_tc(tp, id); + PRE(tc != NULL); + return tc; +} + +const atf_tc_t *const * +atf_tp_get_tcs(const atf_tp_t *tp) +{ + const atf_tc_t **array; + atf_list_citer_t iter; + size_t i; + + array = malloc(sizeof(atf_tc_t *) * + (atf_list_size(&tp->pimpl->m_tcs) + 1)); + if (array == NULL) + goto out; + + i = 0; + atf_list_for_each_c(iter, &tp->pimpl->m_tcs) { + array[i] = atf_list_citer_data(iter); + if (array[i] == NULL) { + free(array); + array = NULL; + goto out; + } + + i++; + } + array[i] = NULL; + +out: + return array; +} + +/* + * Modifiers. + */ + +atf_error_t +atf_tp_add_tc(atf_tp_t *tp, atf_tc_t *tc) +{ + atf_error_t err; + + PRE(find_tc(tp, atf_tc_get_ident(tc)) == NULL); + + err = atf_list_append(&tp->pimpl->m_tcs, tc, false); + + POST(find_tc(tp, atf_tc_get_ident(tc)) != NULL); + + return err; +} + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +atf_error_t +atf_tp_run(const atf_tp_t *tp, const char *tcname, const char *resfile) +{ + const atf_tc_t *tc; + + tc = find_tc(tp, tcname); + PRE(tc != NULL); + + return atf_tc_run(tc, resfile); +} + +atf_error_t +atf_tp_cleanup(const atf_tp_t *tp, const char *tcname) +{ + const atf_tc_t *tc; + + tc = find_tc(tp, tcname); + PRE(tc != NULL); + + return atf_tc_cleanup(tc); +} diff --git a/unit/atf-src/atf-c/tp.h b/unit/atf-src/atf-c/tp.h new file mode 100644 index 0000000..c176f7d --- /dev/null +++ b/unit/atf-src/atf-c/tp.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_TP_H) +#define ATF_C_TP_H + +#include <stdbool.h> + +#include <atf-c/error_fwd.h> + +struct atf_tc; + +/* --------------------------------------------------------------------- + * The "atf_tp" type. + * --------------------------------------------------------------------- */ + +struct atf_tp_impl; +struct atf_tp { + struct atf_tp_impl *pimpl; +}; +typedef struct atf_tp atf_tp_t; + +/* Constructors/destructors. */ +atf_error_t atf_tp_init(atf_tp_t *, const char *const *); +void atf_tp_fini(atf_tp_t *); + +/* Getters. */ +char **atf_tp_get_config(const atf_tp_t *); +bool atf_tp_has_tc(const atf_tp_t *, const char *); +const struct atf_tc *atf_tp_get_tc(const atf_tp_t *, const char *); +const struct atf_tc *const *atf_tp_get_tcs(const atf_tp_t *); + +/* Modifiers. */ +atf_error_t atf_tp_add_tc(atf_tp_t *, struct atf_tc *); + +/* --------------------------------------------------------------------- + * Free functions. + * --------------------------------------------------------------------- */ + +atf_error_t atf_tp_run(const atf_tp_t *, const char *, const char *); +atf_error_t atf_tp_cleanup(const atf_tp_t *, const char *); + +#endif /* !defined(ATF_C_TP_H) */ diff --git a/unit/atf-src/atf-c/tp_test.c b/unit/atf-src/atf-c/tp_test.c new file mode 100644 index 0000000..de334a9 --- /dev/null +++ b/unit/atf-src/atf-c/tp_test.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/tp.h" + +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +ATF_TC(getopt); +ATF_TC_HEAD(getopt, tc) +{ + atf_tc_set_md_var(tc, "descr", "Checks if getopt(3) global state is " + "reset by the test program driver so that test cases can use " + "getopt(3) again"); +} +ATF_TC_BODY(getopt, tc) +{ + /* Provide an option that is unknown to the test program driver and + * one that is, together with an argument that would be swallowed by + * the test program option if it were recognized. */ + int argc = 4; + char arg1[] = "progname"; + char arg2[] = "-Z"; + char arg3[] = "-s"; + char arg4[] = "foo"; + char *const argv[] = { arg1, arg2, arg3, arg4, NULL }; + + int ch; + bool zflag; + + /* Given that this obviously is a test program, and that we used the + * same driver to start, we can test getopt(3) right here without doing + * any fancy stuff. */ + zflag = false; + while ((ch = getopt(argc, argv, ":Z")) != -1) { + switch (ch) { + case 'Z': + zflag = true; + break; + + case '?': + default: + if (optopt != 's') + atf_tc_fail("Unexpected unknown option -%c found", optopt); + } + } + + ATF_REQUIRE(zflag); + ATF_REQUIRE_EQ_MSG(1, argc - optind, "Invalid number of arguments left " + "after the call to getopt(3)"); + ATF_CHECK_STREQ_MSG("foo", argv[optind], "The non-option argument is " + "invalid"); +} + +/* --------------------------------------------------------------------- + * Main. + * --------------------------------------------------------------------- */ + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, getopt); + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/unused_test.c b/unit/atf-src/atf-c/unused_test.c new file mode 100644 index 0000000..1c5fda9 --- /dev/null +++ b/unit/atf-src/atf-c/unused_test.c @@ -0,0 +1,52 @@ +/* Copyright (c) 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include <atf-c.h> + +ATF_TC(this_is_used); +ATF_TC_HEAD(this_is_used, tc) +{ + atf_tc_set_md_var(tc, "descr", "A test case that is not referenced"); +} +ATF_TC_BODY(this_is_used, tc) +{ +} + +ATF_TC(this_is_unused); +ATF_TC_HEAD(this_is_unused, tc) +{ + atf_tc_set_md_var(tc, "descr", "A test case that is referenced"); +} +ATF_TC_BODY(this_is_unused, tc) +{ +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, this_is_used); + /* ATF_TP_ADD_TC(tp, this_is_unused); */ + + return atf_no_error(); +} diff --git a/unit/atf-src/atf-c/utils.c b/unit/atf-src/atf-c/utils.c new file mode 100644 index 0000000..1e2aac1 --- /dev/null +++ b/unit/atf-src/atf-c/utils.c @@ -0,0 +1,456 @@ +/* Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/utils.h" + +#include <sys/stat.h> +#include <sys/wait.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/detail/dynstr.h" + +/** Allocate a filename to be used by atf_utils_{fork,wait}. + * + * In case of a failure, marks the calling test as failed when in_parent is + * true, else terminates execution. + * + * \param [out] name String to contain the generated file. + * \param pid PID of the process that will write to the file. + * \param suffix Either "out" or "err". + * \param in_parent If true, fail with atf_tc_fail; else use err(3). */ +static void +init_out_filename(atf_dynstr_t *name, const pid_t pid, const char *suffix, + const bool in_parent) +{ + atf_error_t error; + + error = atf_dynstr_init_fmt(name, "atf_utils_fork_%d_%s.txt", + (int)pid, suffix); + if (atf_is_error(error)) { + char buffer[1024]; + atf_error_format(error, buffer, sizeof(buffer)); + if (in_parent) { + atf_tc_fail("Failed to create output file: %s", buffer); + } else { + err(EXIT_FAILURE, "Failed to create output file: %s", buffer); + } + } +} + +/** Searches for a regexp in a string. + * + * \param regex The regexp to look for. + * \param str The string in which to look for the expression. + * + * \return True if there is a match; false otherwise. */ +static +bool +grep_string(const char *regex, const char *str) +{ + int res; + regex_t preg; + + printf("Looking for '%s' in '%s'\n", regex, str); + ATF_REQUIRE(regcomp(&preg, regex, REG_EXTENDED) == 0); + + res = regexec(&preg, str, 0, NULL, 0); + ATF_REQUIRE(res == 0 || res == REG_NOMATCH); + + regfree(&preg); + + return res == 0; +} + +/** Prints the contents of a file to stdout. + * + * \param name The name of the file to be printed. + * \param prefix An string to be prepended to every line of the printed + * file. */ +void +atf_utils_cat_file(const char *name, const char *prefix) +{ + const int fd = open(name, O_RDONLY); + ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", name); + + char buffer[1024]; + ssize_t count; + bool continued = false; + while ((count = read(fd, buffer, sizeof(buffer) - 1)) > 0) { + buffer[count] = '\0'; + + if (!continued) + printf("%s", prefix); + + char *iter = buffer; + char *end; + while ((end = strchr(iter, '\n')) != NULL) { + *end = '\0'; + printf("%s\n", iter); + + iter = end + 1; + if (iter != buffer + count) + printf("%s", prefix); + else + continued = false; + } + if (iter < buffer + count) { + printf("%s", iter); + continued = true; + } + } + ATF_REQUIRE(count == 0); +} + +/** Compares a file against the given golden contents. + * + * \param name Name of the file to be compared. + * \param contents Expected contents of the file. + * + * \return True if the file matches the contents; false otherwise. */ +bool +atf_utils_compare_file(const char *name, const char *contents) +{ + const int fd = open(name, O_RDONLY); + ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", name); + + const char *pos = contents; + ssize_t remaining = strlen(contents); + + char buffer[1024]; + ssize_t count; + while ((count = read(fd, buffer, sizeof(buffer))) > 0 && + count <= remaining) { + if (memcmp(pos, buffer, count) != 0) { + close(fd); + return false; + } + remaining -= count; + pos += count; + } + close(fd); + return count == 0 && remaining == 0; +} + +/** Copies a file. + * + * \param source Path to the source file. + * \param destination Path to the destination file. */ +void +atf_utils_copy_file(const char *source, const char *destination) +{ + const int input = open(source, O_RDONLY); + ATF_REQUIRE_MSG(input != -1, "Failed to open source file during " + "copy (%s)", source); + + const int output = open(destination, O_WRONLY | O_CREAT | O_TRUNC, 0777); + ATF_REQUIRE_MSG(output != -1, "Failed to open destination file during " + "copy (%s)", destination); + + char buffer[1024]; + ssize_t length; + while ((length = read(input, buffer, sizeof(buffer))) > 0) + ATF_REQUIRE_MSG(write(output, buffer, length) == length, + "Failed to write to %s during copy", destination); + ATF_REQUIRE_MSG(length != -1, "Failed to read from %s during copy", source); + + struct stat sb; + ATF_REQUIRE_MSG(fstat(input, &sb) != -1, + "Failed to stat source file %s during copy", source); + ATF_REQUIRE_MSG(fchmod(output, sb.st_mode) != -1, + "Failed to chmod destination file %s during copy", + destination); + + close(output); + close(input); +} + +/** Creates a file. + * + * \param name Name of the file to create. + * \param contents Text to write into the created file. + * \param ... Positional parameters to the contents. */ +void +atf_utils_create_file(const char *name, const char *contents, ...) +{ + va_list ap; + atf_dynstr_t formatted; + atf_error_t error; + + va_start(ap, contents); + error = atf_dynstr_init_ap(&formatted, contents, ap); + va_end(ap); + ATF_REQUIRE(!atf_is_error(error)); + + const int fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644); + ATF_REQUIRE_MSG(fd != -1, "Cannot create file %s", name); + ATF_REQUIRE(write(fd, atf_dynstr_cstring(&formatted), + atf_dynstr_length(&formatted)) != -1); + close(fd); + + atf_dynstr_fini(&formatted); +} + +/** Checks if a file exists. + * + * \param path Location of the file to check for. + * + * \return True if the file exists, false otherwise. */ +bool +atf_utils_file_exists(const char *path) +{ + const int ret = access(path, F_OK); + if (ret == -1) { + if (errno != ENOENT) + atf_tc_fail("Failed to check the existence of %s: %s", path, + strerror(errno)); + else + return false; + } else + return true; +} + +/** Spawns a subprocess and redirects its output to files. + * + * Use the atf_utils_wait() function to wait for the completion of the spawned + * subprocess and validate its exit conditions. + * + * \return 0 in the new child; the PID of the new child in the parent. Does + * not return in error conditions. */ +pid_t +atf_utils_fork(void) +{ + const pid_t pid = fork(); + if (pid == -1) + atf_tc_fail("fork failed"); + + if (pid == 0) { + atf_dynstr_t out_name; + init_out_filename(&out_name, getpid(), "out", false); + + atf_dynstr_t err_name; + init_out_filename(&err_name, getpid(), "err", false); + + atf_utils_redirect(STDOUT_FILENO, atf_dynstr_cstring(&out_name)); + atf_utils_redirect(STDERR_FILENO, atf_dynstr_cstring(&err_name)); + + atf_dynstr_fini(&err_name); + atf_dynstr_fini(&out_name); + } + return pid; +} + +/** Frees an dynamically-allocated "argv" array. + * + * \param argv A dynamically-allocated array of dynamically-allocated + * strings. */ +void +atf_utils_free_charpp(char **argv) +{ + char **ptr; + + for (ptr = argv; *ptr != NULL; ptr++) + free(*ptr); + + free(argv); +} + +/** Searches for a regexp in a file. + * + * \param regex The regexp to look for. + * \param file The file in which to look for the expression. + * \param ... Positional parameters to the regex. + * + * \return True if there is a match; false otherwise. */ +bool +atf_utils_grep_file(const char *regex, const char *file, ...) +{ + int fd; + va_list ap; + atf_dynstr_t formatted; + atf_error_t error; + + va_start(ap, file); + error = atf_dynstr_init_ap(&formatted, regex, ap); + va_end(ap); + ATF_REQUIRE(!atf_is_error(error)); + + ATF_REQUIRE((fd = open(file, O_RDONLY)) != -1); + bool found = false; + char *line = NULL; + while (!found && (line = atf_utils_readline(fd)) != NULL) { + found = grep_string(atf_dynstr_cstring(&formatted), line); + free(line); + } + close(fd); + + atf_dynstr_fini(&formatted); + + return found; +} + +/** Searches for a regexp in a string. + * + * \param regex The regexp to look for. + * \param str The string in which to look for the expression. + * \param ... Positional parameters to the regex. + * + * \return True if there is a match; false otherwise. */ +bool +atf_utils_grep_string(const char *regex, const char *str, ...) +{ + bool res; + va_list ap; + atf_dynstr_t formatted; + atf_error_t error; + + va_start(ap, str); + error = atf_dynstr_init_ap(&formatted, regex, ap); + va_end(ap); + ATF_REQUIRE(!atf_is_error(error)); + + res = grep_string(atf_dynstr_cstring(&formatted), str); + + atf_dynstr_fini(&formatted); + + return res; +} + +/** Reads a line of arbitrary length. + * + * \param fd The descriptor from which to read the line. + * + * \return A pointer to the read line, which must be released with free(), or + * NULL if there was nothing to read from the file. */ +char * +atf_utils_readline(const int fd) +{ + char ch; + ssize_t cnt; + atf_dynstr_t temp; + atf_error_t error; + + error = atf_dynstr_init(&temp); + ATF_REQUIRE(!atf_is_error(error)); + + while ((cnt = read(fd, &ch, sizeof(ch))) == sizeof(ch) && + ch != '\n') { + error = atf_dynstr_append_fmt(&temp, "%c", ch); + ATF_REQUIRE(!atf_is_error(error)); + } + ATF_REQUIRE(cnt != -1); + + if (cnt == 0 && atf_dynstr_length(&temp) == 0) { + atf_dynstr_fini(&temp); + return NULL; + } else + return atf_dynstr_fini_disown(&temp); +} + +/** Redirects a file descriptor to a file. + * + * \param target_fd The file descriptor to be replaced. + * \param name The name of the file to direct the descriptor to. + * + * \pre Should only be called from the process spawned by fork_for_testing + * because this exits uncontrolledly. + * \post Terminates execution if the redirection fails. */ +void +atf_utils_redirect(const int target_fd, const char *name) +{ + if (target_fd == STDOUT_FILENO) + fflush(stdout); + else if (target_fd == STDERR_FILENO) + fflush(stderr); + + const int new_fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (new_fd == -1) + err(EXIT_FAILURE, "Cannot create %s", name); + if (new_fd != target_fd) { + if (dup2(new_fd, target_fd) == -1) + err(EXIT_FAILURE, "Cannot redirect to fd %d", target_fd); + } + close(new_fd); +} + +/** Waits for a subprocess and validates its exit condition. + * + * \param pid The process to be waited for. Must have been started by + * testutils_fork(). + * \param exitstatus Expected exit status. + * \param expout Expected contents of stdout. + * \param experr Expected contents of stderr. */ +void +atf_utils_wait(const pid_t pid, const int exitstatus, const char *expout, + const char *experr) +{ + int status; + ATF_REQUIRE(waitpid(pid, &status, 0) != -1); + + atf_dynstr_t out_name; + init_out_filename(&out_name, pid, "out", true); + + atf_dynstr_t err_name; + init_out_filename(&err_name, pid, "err", true); + + atf_utils_cat_file(atf_dynstr_cstring(&out_name), "subprocess stdout: "); + atf_utils_cat_file(atf_dynstr_cstring(&err_name), "subprocess stderr: "); + + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE_EQ(exitstatus, WEXITSTATUS(status)); + + const char *save_prefix = "save:"; + const size_t save_prefix_length = strlen(save_prefix); + + if (strlen(expout) > save_prefix_length && + strncmp(expout, save_prefix, save_prefix_length) == 0) { + atf_utils_copy_file(atf_dynstr_cstring(&out_name), + expout + save_prefix_length); + } else { + ATF_REQUIRE(atf_utils_compare_file(atf_dynstr_cstring(&out_name), + expout)); + } + + if (strlen(experr) > save_prefix_length && + strncmp(experr, save_prefix, save_prefix_length) == 0) { + atf_utils_copy_file(atf_dynstr_cstring(&err_name), + experr + save_prefix_length); + } else { + ATF_REQUIRE(atf_utils_compare_file(atf_dynstr_cstring(&err_name), + experr)); + } + + ATF_REQUIRE(unlink(atf_dynstr_cstring(&out_name)) != -1); + ATF_REQUIRE(unlink(atf_dynstr_cstring(&err_name)) != -1); +} diff --git a/unit/atf-src/atf-c/utils.h b/unit/atf-src/atf-c/utils.h new file mode 100644 index 0000000..e4162b2 --- /dev/null +++ b/unit/atf-src/atf-c/utils.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#if !defined(ATF_C_UTILS_H) +#define ATF_C_UTILS_H + +#include <stdbool.h> +#include <unistd.h> + +#include <atf-c/defs.h> + +void atf_utils_cat_file(const char *, const char *); +bool atf_utils_compare_file(const char *, const char *); +void atf_utils_copy_file(const char *, const char *); +void atf_utils_create_file(const char *, const char *, ...) + ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(2, 3); +bool atf_utils_file_exists(const char *); +pid_t atf_utils_fork(void); +void atf_utils_free_charpp(char **); +bool atf_utils_grep_file(const char *, const char *, ...) + ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 3); +bool atf_utils_grep_string(const char *, const char *, ...) + ATF_DEFS_ATTRIBUTE_FORMAT_PRINTF(1, 3); +char *atf_utils_readline(int); +void atf_utils_redirect(const int, const char *); +void atf_utils_wait(const pid_t, const int, const char *, const char *); + +#endif /* !defined(ATF_C_UTILS_H) */ diff --git a/unit/atf-src/atf-c/utils_test.c b/unit/atf-src/atf-c/utils_test.c new file mode 100644 index 0000000..fb81cd3 --- /dev/null +++ b/unit/atf-src/atf-c/utils_test.c @@ -0,0 +1,570 @@ +/* Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND + * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +#include "atf-c/utils.h" + +#include <sys/stat.h> +#include <sys/wait.h> + +#include <fcntl.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <atf-c.h> + +#include "atf-c/detail/dynstr.h" +#include "atf-c/detail/test_helpers.h" + +/** Reads the contents of a file into a buffer. + * + * Up to buflen-1 characters are read into buffer. If this function returns, + * the contents read into the buffer are guaranteed to be nul-terminated. + * Note, however, that if the file contains any nul characters itself, + * comparing it "as a string" will not work. + * + * \param path The file to be read, which must exist. + * \param buffer Buffer into which to store the file contents. + * \param buflen Size of the target buffer. + * + * \return The count of bytes read. */ +static ssize_t +read_file(const char *path, void *const buffer, const size_t buflen) +{ + const int fd = open(path, O_RDONLY); + ATF_REQUIRE_MSG(fd != -1, "Cannot open %s", path); + const ssize_t length = read(fd, buffer, buflen - 1); + close(fd); + ATF_REQUIRE(length != -1); + ((char *)buffer)[length] = '\0'; + return length; +} + +ATF_TC_WITHOUT_HEAD(cat_file__empty); +ATF_TC_BODY(cat_file__empty, tc) +{ + atf_utils_create_file("file.txt", "%s", ""); + atf_utils_redirect(STDOUT_FILENO, "captured.txt"); + atf_utils_cat_file("file.txt", "PREFIX"); + fflush(stdout); + close(STDOUT_FILENO); + + char buffer[1024]; + read_file("captured.txt", buffer, sizeof(buffer)); + ATF_REQUIRE_STREQ("", buffer); +} + +ATF_TC_WITHOUT_HEAD(cat_file__one_line); +ATF_TC_BODY(cat_file__one_line, tc) +{ + atf_utils_create_file("file.txt", "This is a single line\n"); + atf_utils_redirect(STDOUT_FILENO, "captured.txt"); + atf_utils_cat_file("file.txt", "PREFIX"); + fflush(stdout); + close(STDOUT_FILENO); + + char buffer[1024]; + read_file("captured.txt", buffer, sizeof(buffer)); + ATF_REQUIRE_STREQ("PREFIXThis is a single line\n", buffer); +} + +ATF_TC_WITHOUT_HEAD(cat_file__several_lines); +ATF_TC_BODY(cat_file__several_lines, tc) +{ + atf_utils_create_file("file.txt", "First\nSecond line\nAnd third\n"); + atf_utils_redirect(STDOUT_FILENO, "captured.txt"); + atf_utils_cat_file("file.txt", ">"); + fflush(stdout); + close(STDOUT_FILENO); + + char buffer[1024]; + read_file("captured.txt", buffer, sizeof(buffer)); + ATF_REQUIRE_STREQ(">First\n>Second line\n>And third\n", buffer); +} + +ATF_TC_WITHOUT_HEAD(cat_file__no_newline_eof); +ATF_TC_BODY(cat_file__no_newline_eof, tc) +{ + atf_utils_create_file("file.txt", "Foo\n bar baz"); + atf_utils_redirect(STDOUT_FILENO, "captured.txt"); + atf_utils_cat_file("file.txt", "PREFIX"); + fflush(stdout); + close(STDOUT_FILENO); + + char buffer[1024]; + read_file("captured.txt", buffer, sizeof(buffer)); + ATF_REQUIRE_STREQ("PREFIXFoo\nPREFIX bar baz", buffer); +} + +ATF_TC_WITHOUT_HEAD(compare_file__empty__match); +ATF_TC_BODY(compare_file__empty__match, tc) +{ + atf_utils_create_file("test.txt", "%s", ""); + ATF_REQUIRE(atf_utils_compare_file("test.txt", "")); +} + +ATF_TC_WITHOUT_HEAD(compare_file__empty__not_match); +ATF_TC_BODY(compare_file__empty__not_match, tc) +{ + atf_utils_create_file("test.txt", "%s", ""); + ATF_REQUIRE(!atf_utils_compare_file("test.txt", "\n")); + ATF_REQUIRE(!atf_utils_compare_file("test.txt", "foo")); + ATF_REQUIRE(!atf_utils_compare_file("test.txt", " ")); +} + +ATF_TC_WITHOUT_HEAD(compare_file__short__match); +ATF_TC_BODY(compare_file__short__match, tc) +{ + atf_utils_create_file("test.txt", "this is a short file"); + ATF_REQUIRE(atf_utils_compare_file("test.txt", "this is a short file")); +} + +ATF_TC_WITHOUT_HEAD(compare_file__short__not_match); +ATF_TC_BODY(compare_file__short__not_match, tc) +{ + atf_utils_create_file("test.txt", "this is a short file"); + ATF_REQUIRE(!atf_utils_compare_file("test.txt", "")); + ATF_REQUIRE(!atf_utils_compare_file("test.txt", "\n")); + ATF_REQUIRE(!atf_utils_compare_file("test.txt", "this is a Short file")); + ATF_REQUIRE(!atf_utils_compare_file("test.txt", "this is a short fil")); + ATF_REQUIRE(!atf_utils_compare_file("test.txt", "this is a short file ")); +} + +ATF_TC_WITHOUT_HEAD(compare_file__long__match); +ATF_TC_BODY(compare_file__long__match, tc) +{ + char long_contents[3456]; + size_t i = 0; + for (; i < sizeof(long_contents) - 1; i++) + long_contents[i] = '0' + (i % 10); + long_contents[i] = '\0'; + atf_utils_create_file("test.txt", "%s", long_contents); + + ATF_REQUIRE(atf_utils_compare_file("test.txt", long_contents)); +} + +ATF_TC_WITHOUT_HEAD(compare_file__long__not_match); +ATF_TC_BODY(compare_file__long__not_match, tc) +{ + char long_contents[3456]; + size_t i = 0; + for (; i < sizeof(long_contents) - 1; i++) + long_contents[i] = '0' + (i % 10); + long_contents[i] = '\0'; + atf_utils_create_file("test.txt", "%s", long_contents); + + ATF_REQUIRE(!atf_utils_compare_file("test.txt", "")); + ATF_REQUIRE(!atf_utils_compare_file("test.txt", "\n")); + ATF_REQUIRE(!atf_utils_compare_file("test.txt", "0123456789")); + long_contents[i - 1] = 'Z'; + ATF_REQUIRE(!atf_utils_compare_file("test.txt", long_contents)); +} + +ATF_TC_WITHOUT_HEAD(copy_file__empty); +ATF_TC_BODY(copy_file__empty, tc) +{ + atf_utils_create_file("src.txt", "%s", ""); + ATF_REQUIRE(chmod("src.txt", 0520) != -1); + + atf_utils_copy_file("src.txt", "dest.txt"); + ATF_REQUIRE(atf_utils_compare_file("dest.txt", "")); + struct stat sb; + ATF_REQUIRE(stat("dest.txt", &sb) != -1); + ATF_REQUIRE_EQ(0520, sb.st_mode & 0xfff); +} + +ATF_TC_WITHOUT_HEAD(copy_file__some_contents); +ATF_TC_BODY(copy_file__some_contents, tc) +{ + atf_utils_create_file("src.txt", "This is a\ntest file\n"); + atf_utils_copy_file("src.txt", "dest.txt"); + ATF_REQUIRE(atf_utils_compare_file("dest.txt", "This is a\ntest file\n")); +} + +ATF_TC_WITHOUT_HEAD(create_file); +ATF_TC_BODY(create_file, tc) +{ + atf_utils_create_file("test.txt", "This is a test with %d", 12345); + + char buffer[128]; + read_file("test.txt", buffer, sizeof(buffer)); + ATF_REQUIRE_STREQ("This is a test with 12345", buffer); +} + +ATF_TC_WITHOUT_HEAD(file_exists); +ATF_TC_BODY(file_exists, tc) +{ + atf_utils_create_file("test.txt", "foo"); + + ATF_REQUIRE( atf_utils_file_exists("test.txt")); + ATF_REQUIRE( atf_utils_file_exists("./test.txt")); + ATF_REQUIRE(!atf_utils_file_exists("./test.tx")); + ATF_REQUIRE(!atf_utils_file_exists("test.txt2")); +} + +ATF_TC_WITHOUT_HEAD(fork); +ATF_TC_BODY(fork, tc) +{ + fprintf(stdout, "Should not get into child\n"); + fprintf(stderr, "Should not get into child\n"); + pid_t pid = atf_utils_fork(); + if (pid == 0) { + fprintf(stdout, "Child stdout\n"); + fprintf(stderr, "Child stderr\n"); + exit(EXIT_SUCCESS); + } + + int status; + ATF_REQUIRE(waitpid(pid, &status, 0) != -1); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); + + atf_dynstr_t out_name; + RE(atf_dynstr_init_fmt(&out_name, "atf_utils_fork_%d_out.txt", (int)pid)); + atf_dynstr_t err_name; + RE(atf_dynstr_init_fmt(&err_name, "atf_utils_fork_%d_err.txt", (int)pid)); + + char buffer[1024]; + read_file(atf_dynstr_cstring(&out_name), buffer, sizeof(buffer)); + ATF_REQUIRE_STREQ("Child stdout\n", buffer); + read_file(atf_dynstr_cstring(&err_name), buffer, sizeof(buffer)); + ATF_REQUIRE_STREQ("Child stderr\n", buffer); + + atf_dynstr_fini(&err_name); + atf_dynstr_fini(&out_name); +} + +ATF_TC_WITHOUT_HEAD(free_charpp__empty); +ATF_TC_BODY(free_charpp__empty, tc) +{ + char **array = malloc(sizeof(char *) * 1); + array[0] = NULL; + + atf_utils_free_charpp(array); +} + +ATF_TC_WITHOUT_HEAD(free_charpp__some); +ATF_TC_BODY(free_charpp__some, tc) +{ + char **array = malloc(sizeof(char *) * 4); + array[0] = strdup("first"); + array[1] = strdup("second"); + array[2] = strdup("third"); + array[3] = NULL; + + atf_utils_free_charpp(array); +} + +ATF_TC_WITHOUT_HEAD(grep_file); +ATF_TC_BODY(grep_file, tc) +{ + atf_utils_create_file("test.txt", "line1\nthe second line\naaaabbbb\n"); + + ATF_CHECK(atf_utils_grep_file("line1", "test.txt")); + ATF_CHECK(atf_utils_grep_file("line%d", "test.txt", 1)); + ATF_CHECK(atf_utils_grep_file("second line", "test.txt")); + ATF_CHECK(atf_utils_grep_file("aa.*bb", "test.txt")); + ATF_CHECK(!atf_utils_grep_file("foo", "test.txt")); + ATF_CHECK(!atf_utils_grep_file("bar", "test.txt")); + ATF_CHECK(!atf_utils_grep_file("aaaaa", "test.txt")); +} + +ATF_TC_WITHOUT_HEAD(grep_string); +ATF_TC_BODY(grep_string, tc) +{ + const char *str = "a string - aaaabbbb"; + ATF_CHECK(atf_utils_grep_string("a string", str)); + ATF_CHECK(atf_utils_grep_string("^a string", str)); + ATF_CHECK(atf_utils_grep_string("aaaabbbb$", str)); + ATF_CHECK(atf_utils_grep_string("a%s*bb", str, "a.")); + ATF_CHECK(!atf_utils_grep_string("foo", str)); + ATF_CHECK(!atf_utils_grep_string("bar", str)); + ATF_CHECK(!atf_utils_grep_string("aaaaa", str)); +} + +ATF_TC_WITHOUT_HEAD(readline__none); +ATF_TC_BODY(readline__none, tc) +{ + atf_utils_create_file("empty.txt", "%s", ""); + + const int fd = open("empty.txt", O_RDONLY); + ATF_REQUIRE(fd != -1); + ATF_REQUIRE(atf_utils_readline(fd) == NULL); + close(fd); +} + +ATF_TC_WITHOUT_HEAD(readline__some); +ATF_TC_BODY(readline__some, tc) +{ + const char *l1 = "First line with % formatting % characters %"; + const char *l2 = "Second line; much longer than the first one"; + const char *l3 = "Last line, without terminator"; + + atf_utils_create_file("test.txt", "%s\n%s\n%s", l1, l2, l3); + + const int fd = open("test.txt", O_RDONLY); + ATF_REQUIRE(fd != -1); + + char *line; + + line = atf_utils_readline(fd); + ATF_REQUIRE_STREQ(l1, line); + free(line); + + line = atf_utils_readline(fd); + ATF_REQUIRE_STREQ(l2, line); + free(line); + + line = atf_utils_readline(fd); + ATF_REQUIRE_STREQ(l3, line); + free(line); + + close(fd); +} + +ATF_TC_WITHOUT_HEAD(redirect__stdout); +ATF_TC_BODY(redirect__stdout, tc) +{ + printf("Buffer this"); + atf_utils_redirect(STDOUT_FILENO, "captured.txt"); + printf("The printed message"); + fflush(stdout); + + char buffer[1024]; + read_file("captured.txt", buffer, sizeof(buffer)); + ATF_REQUIRE_STREQ("The printed message", buffer); +} + +ATF_TC_WITHOUT_HEAD(redirect__stderr); +ATF_TC_BODY(redirect__stderr, tc) +{ + fprintf(stderr, "Buffer this"); + atf_utils_redirect(STDERR_FILENO, "captured.txt"); + fprintf(stderr, "The printed message"); + fflush(stderr); + + char buffer[1024]; + read_file("captured.txt", buffer, sizeof(buffer)); + ATF_REQUIRE_STREQ("The printed message", buffer); +} + +ATF_TC_WITHOUT_HEAD(redirect__other); +ATF_TC_BODY(redirect__other, tc) +{ + const char *message = "Foo bar\nbaz\n"; + atf_utils_redirect(15, "captured.txt"); + ATF_REQUIRE(write(15, message, strlen(message)) != -1); + close(15); + + char buffer[1024]; + read_file("captured.txt", buffer, sizeof(buffer)); + ATF_REQUIRE_STREQ(message, buffer); +} + +static void +fork_and_wait(const int exitstatus, const char* expout, const char* experr) +{ + const pid_t pid = atf_utils_fork(); + ATF_REQUIRE(pid != -1); + if (pid == 0) { + fprintf(stdout, "Some output\n"); + fprintf(stderr, "Some error\n"); + exit(123); + } + atf_utils_wait(pid, exitstatus, expout, experr); + exit(EXIT_SUCCESS); +} + +ATF_TC_WITHOUT_HEAD(wait__ok); +ATF_TC_BODY(wait__ok, tc) +{ + const pid_t control = fork(); + ATF_REQUIRE(control != -1); + if (control == 0) + fork_and_wait(123, "Some output\n", "Some error\n"); + else { + int status; + ATF_REQUIRE(waitpid(control, &status, 0) != -1); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); + } +} + +ATF_TC_WITHOUT_HEAD(wait__ok_nested); +ATF_TC_BODY(wait__ok_nested, tc) +{ + const pid_t parent = atf_utils_fork(); + ATF_REQUIRE(parent != -1); + if (parent == 0) { + const pid_t child = atf_utils_fork(); + ATF_REQUIRE(child != -1); + if (child == 0) { + fflush(stderr); + fprintf(stdout, "Child output\n"); + fflush(stdout); + fprintf(stderr, "Child error\n"); + exit(50); + } else { + fprintf(stdout, "Parent output\n"); + fprintf(stderr, "Parent error\n"); + atf_utils_wait(child, 50, "Child output\n", "Child error\n"); + exit(40); + } + } else { + atf_utils_wait(parent, 40, + "Parent output\n" + "subprocess stdout: Child output\n" + "subprocess stderr: Child error\n", + "Parent error\n"); + } +} + +ATF_TC_WITHOUT_HEAD(wait__invalid_exitstatus); +ATF_TC_BODY(wait__invalid_exitstatus, tc) +{ + const pid_t control = fork(); + ATF_REQUIRE(control != -1); + if (control == 0) + fork_and_wait(120, "Some output\n", "Some error\n"); + else { + int status; + ATF_REQUIRE(waitpid(control, &status, 0) != -1); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE_EQ(EXIT_FAILURE, WEXITSTATUS(status)); + } +} + +ATF_TC_WITHOUT_HEAD(wait__invalid_stdout); +ATF_TC_BODY(wait__invalid_stdout, tc) +{ + const pid_t control = fork(); + ATF_REQUIRE(control != -1); + if (control == 0) + fork_and_wait(123, "Some output foo\n", "Some error\n"); + else { + int status; + ATF_REQUIRE(waitpid(control, &status, 0) != -1); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE_EQ(EXIT_FAILURE, WEXITSTATUS(status)); + } +} + +ATF_TC_WITHOUT_HEAD(wait__invalid_stderr); +ATF_TC_BODY(wait__invalid_stderr, tc) +{ + const pid_t control = fork(); + ATF_REQUIRE(control != -1); + if (control == 0) + fork_and_wait(123, "Some output\n", "Some error foo\n"); + else { + int status; + ATF_REQUIRE(waitpid(control, &status, 0) != -1); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE_EQ(EXIT_FAILURE, WEXITSTATUS(status)); + } +} + +ATF_TC_WITHOUT_HEAD(wait__save_stdout); +ATF_TC_BODY(wait__save_stdout, tc) +{ + const pid_t control = fork(); + ATF_REQUIRE(control != -1); + if (control == 0) + fork_and_wait(123, "save:my-output.txt", "Some error\n"); + else { + int status; + ATF_REQUIRE(waitpid(control, &status, 0) != -1); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); + + ATF_REQUIRE(atf_utils_compare_file("my-output.txt", "Some output\n")); + } +} + +ATF_TC_WITHOUT_HEAD(wait__save_stderr); +ATF_TC_BODY(wait__save_stderr, tc) +{ + const pid_t control = fork(); + ATF_REQUIRE(control != -1); + if (control == 0) + fork_and_wait(123, "Some output\n", "save:my-output.txt"); + else { + int status; + ATF_REQUIRE(waitpid(control, &status, 0) != -1); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE_EQ(EXIT_SUCCESS, WEXITSTATUS(status)); + + ATF_REQUIRE(atf_utils_compare_file("my-output.txt", "Some error\n")); + } +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, cat_file__empty); + ATF_TP_ADD_TC(tp, cat_file__one_line); + ATF_TP_ADD_TC(tp, cat_file__several_lines); + ATF_TP_ADD_TC(tp, cat_file__no_newline_eof); + + ATF_TP_ADD_TC(tp, compare_file__empty__match); + ATF_TP_ADD_TC(tp, compare_file__empty__not_match); + ATF_TP_ADD_TC(tp, compare_file__short__match); + ATF_TP_ADD_TC(tp, compare_file__short__not_match); + ATF_TP_ADD_TC(tp, compare_file__long__match); + ATF_TP_ADD_TC(tp, compare_file__long__not_match); + + ATF_TP_ADD_TC(tp, copy_file__empty); + ATF_TP_ADD_TC(tp, copy_file__some_contents); + + ATF_TP_ADD_TC(tp, create_file); + + ATF_TP_ADD_TC(tp, file_exists); + + ATF_TP_ADD_TC(tp, fork); + + ATF_TP_ADD_TC(tp, free_charpp__empty); + ATF_TP_ADD_TC(tp, free_charpp__some); + + ATF_TP_ADD_TC(tp, grep_file); + ATF_TP_ADD_TC(tp, grep_string); + + ATF_TP_ADD_TC(tp, readline__none); + ATF_TP_ADD_TC(tp, readline__some); + + ATF_TP_ADD_TC(tp, redirect__stdout); + ATF_TP_ADD_TC(tp, redirect__stderr); + ATF_TP_ADD_TC(tp, redirect__other); + + ATF_TP_ADD_TC(tp, wait__ok); + ATF_TP_ADD_TC(tp, wait__ok_nested); + ATF_TP_ADD_TC(tp, wait__save_stdout); + ATF_TP_ADD_TC(tp, wait__save_stderr); + ATF_TP_ADD_TC(tp, wait__invalid_exitstatus); + ATF_TP_ADD_TC(tp, wait__invalid_stdout); + ATF_TP_ADD_TC(tp, wait__invalid_stderr); + + return atf_no_error(); +} |