summaryrefslogtreecommitdiff
path: root/atf-c
diff options
context:
space:
mode:
Diffstat (limited to 'atf-c')
-rw-r--r--atf-c/atf-c-api.3272
-rw-r--r--atf-c/atf-c.m448
-rw-r--r--atf-c/atf-c.pc.in11
-rw-r--r--atf-c/atf-common.m492
-rw-r--r--atf-c/check_test.c50
-rw-r--r--atf-c/detail/Atffile1
-rw-r--r--atf-c/detail/Kyuafile1
-rw-r--r--atf-c/detail/Makefile.am.inc5
-rw-r--r--atf-c/detail/process_test.c40
-rw-r--r--atf-c/detail/sanity_test.c40
-rw-r--r--atf-c/detail/test_helpers.c67
-rw-r--r--atf-c/detail/test_helpers.h2
-rw-r--r--atf-c/detail/test_helpers_test.c185
-rw-r--r--atf-c/macros.h22
-rw-r--r--atf-c/macros_test.c154
-rw-r--r--atf-c/utils.c374
-rw-r--r--atf-c/utils.h19
-rw-r--r--atf-c/utils_test.c480
18 files changed, 1482 insertions, 381 deletions
diff --git a/atf-c/atf-c-api.3 b/atf-c/atf-c-api.3
index 548db4f9f712c..0ef47a49ccdc6 100644
--- a/atf-c/atf-c-api.3
+++ b/atf-c/atf-c-api.3
@@ -26,14 +26,17 @@
.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
.\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.Dd December 26, 2010
+.Dd November 30, 2012
.Dt ATF-C-API 3
.Os
.Sh NAME
+.Nm atf-c-api ,
.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 ,
@@ -41,6 +44,8 @@
.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 ,
@@ -72,7 +77,19 @@
.Nm atf_tc_fail ,
.Nm atf_tc_fail_nonfatal ,
.Nm atf_tc_pass ,
-.Nm atf_tc_skip
+.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
@@ -80,6 +97,8 @@
.Fn ATF_CHECK_MSG "expression" "fail_msg_fmt" ...
.Fn ATF_CHECK_EQ "expression_1" "expression_2"
.Fn ATF_CHECK_EQ_MSG "expression_1" "expression_2" "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 "exp_errno" "bool_expression"
@@ -87,6 +106,8 @@
.Fn ATF_REQUIRE_MSG "expression" "fail_msg_fmt" ...
.Fn ATF_REQUIRE_EQ "expression_1" "expression_2"
.Fn ATF_REQUIRE_EQ_MSG "expression_1" "expression_2" "fail_msg_fmt" ...
+.Fn ATF_REQUIRE_MATCH "regexp" "string"
+.Fn ATF_REQUIRE_MATCH_MSG "regexp" "string" "fail_msg_fmt" ...
.Fn ATF_REQUIRE_STREQ "string_1" "string_2"
.Fn ATF_REQUIRE_STREQ_MSG "string_1" "string_2" "fail_msg_fmt" ...
.Fn ATF_REQUIRE_ERRNO "exp_errno" "bool_expression"
@@ -119,6 +140,67 @@
.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
The ATF
.Pp
@@ -382,7 +464,7 @@ 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 now sense to continue the execution of a
+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
@@ -417,6 +499,16 @@ and
.Fn ATF_REQUIRE_EQ_MSG
take two expressions and fail if the two evaluated values are not equal.
.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
@@ -433,6 +525,180 @@ 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 -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 -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 -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 -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 -offset indent
+Checks if
+.Fa file
+exists.
+.Ed
+.Pp
+.Ft pid_t
+.Fo atf_utils_fork
+.Fa "void"
+.Fc
+.Bd -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 -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 -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 -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 -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 -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 -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 EXAMPLES
The following shows a complete test program with a single test case that
validates the addition operator:
diff --git a/atf-c/atf-c.m4 b/atf-c/atf-c.m4
new file mode 100644
index 0000000000000..d0237b8482063
--- /dev/null
+++ b/atf-c/atf-c.m4
@@ -0,0 +1,48 @@
+dnl
+dnl Automated Testing Framework (atf)
+dnl
+dnl Copyright 2011 Google Inc.
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted provided that the following conditions are
+dnl met:
+dnl
+dnl * Redistributions of source code must retain the above copyright
+dnl notice, this list of conditions and the following disclaimer.
+dnl * Redistributions in binary form must reproduce the above copyright
+dnl notice, this list of conditions and the following disclaimer in the
+dnl documentation and/or other materials provided with the distribution.
+dnl * Neither the name of Google Inc. nor the names of its contributors
+dnl may be used to endorse or promote products derived from this software
+dnl without specific prior written permission.
+dnl
+dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+dnl
+
+dnl ATF_CHECK_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/atf-c/atf-c.pc.in b/atf-c/atf-c.pc.in
new file mode 100644
index 0000000000000..6fd52740b7910
--- /dev/null
+++ b/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/atf-c/atf-common.m4 b/atf-c/atf-common.m4
new file mode 100644
index 0000000000000..464a5a7fa0076
--- /dev/null
+++ b/atf-c/atf-common.m4
@@ -0,0 +1,92 @@
+dnl
+dnl Automated Testing Framework (atf)
+dnl
+dnl Copyright 2011 Google Inc.
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted provided that the following conditions are
+dnl met:
+dnl
+dnl * Redistributions of source code must retain the above copyright
+dnl notice, this list of conditions and the following disclaimer.
+dnl * Redistributions in binary form must reproduce the above copyright
+dnl notice, this list of conditions and the following disclaimer in the
+dnl documentation and/or other materials provided with the distribution.
+dnl * Neither the name of Google Inc. nor the names of its contributors
+dnl may be used to endorse or promote products derived from this software
+dnl without specific prior written permission.
+dnl
+dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+dnl "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+dnl LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+dnl A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+dnl OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+dnl SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+dnl LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+dnl DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+dnl THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+dnl (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+dnl OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+dnl
+
+dnl ATF_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/atf-c/check_test.c b/atf-c/check_test.c
index b36dd73afc092..a26e032e11c51 100644
--- a/atf-c/check_test.c
+++ b/atf-c/check_test.c
@@ -90,14 +90,10 @@ static
void
check_line(int fd, const char *exp)
{
- atf_dynstr_t line;
-
- atf_dynstr_init(&line);
- ATF_CHECK(!read_line(fd, &line));
- ATF_CHECK_MSG(atf_equal_dynstr_cstring(&line, exp),
- "read: '%s', expected: '%s'",
- atf_dynstr_cstring(&line), exp);
- atf_dynstr_fini(&line);
+ char *line = atf_utils_readline(fd);
+ ATF_CHECK(line != NULL);
+ ATF_CHECK_STREQ_MSG(exp, line, "read: '%s', expected: '%s'", line, exp);
+ free(line);
}
/* ---------------------------------------------------------------------
@@ -246,15 +242,15 @@ 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(grep_file("stdout", "-o test.o"));
- ATF_CHECK(grep_file("stdout", "-c test.c"));
+ 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(grep_file("stdout", "-o test.o"));
- ATF_CHECK(grep_file("stdout", "-c test.c"));
- ATF_CHECK(grep_file("stderr", "test.c"));
- ATF_CHECK(grep_file("stderr", "UNDEFINED_SYMBOL"));
+ 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);
@@ -267,16 +263,16 @@ 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(grep_file("stdout", "-o.*test.p"));
- ATF_CHECK(grep_file("stdout", "test.c"));
- ATF_CHECK(grep_file("test.p", "foo bar"));
+ 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(grep_file("stdout", "-o test.p"));
- ATF_CHECK(grep_file("stdout", "test.c"));
- ATF_CHECK(grep_file("stderr", "test.c"));
- ATF_CHECK(grep_file("stderr", "non-existent.h"));
+ 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);
@@ -289,15 +285,15 @@ 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(grep_file("stdout", "-o test.o"));
- ATF_CHECK(grep_file("stdout", "-c test.cpp"));
+ 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(grep_file("stdout", "-o test.o"));
- ATF_CHECK(grep_file("stdout", "-c test.cpp"));
- ATF_CHECK(grep_file("stderr", "test.cpp"));
- ATF_CHECK(grep_file("stderr", "UNDEFINED_SYMBOL"));
+ 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);
diff --git a/atf-c/detail/Atffile b/atf-c/detail/Atffile
index f715c98cfb742..5fd859382c56f 100644
--- a/atf-c/detail/Atffile
+++ b/atf-c/detail/Atffile
@@ -9,6 +9,5 @@ tp: list_test
tp: map_test
tp: process_test
tp: sanity_test
-tp: test_helpers_test
tp: text_test
tp: user_test
diff --git a/atf-c/detail/Kyuafile b/atf-c/detail/Kyuafile
index 3f4901def1b4c..bb741da4dd5da 100644
--- a/atf-c/detail/Kyuafile
+++ b/atf-c/detail/Kyuafile
@@ -9,6 +9,5 @@ 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="test_helpers_test"}
atf_test_program{name="text_test"}
atf_test_program{name="user_test"}
diff --git a/atf-c/detail/Makefile.am.inc b/atf-c/detail/Makefile.am.inc
index d3c325bd131c2..20a8dc57c205d 100644
--- a/atf-c/detail/Makefile.am.inc
+++ b/atf-c/detail/Makefile.am.inc
@@ -69,11 +69,6 @@ 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/test_helpers_test
-atf_c_detail_test_helpers_test_SOURCES = atf-c/detail/test_helpers_test.c
-atf_c_detail_test_helpers_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
diff --git a/atf-c/detail/process_test.c b/atf-c/detail/process_test.c
index 229593bf83255..9e55f70e32149 100644
--- a/atf-c/detail/process_test.c
+++ b/atf-c/detail/process_test.c
@@ -95,12 +95,12 @@ check_file(const enum out_type type)
{
switch (type) {
case stdout_type:
- ATF_CHECK(grep_file("stdout", "stdout: msg"));
- ATF_CHECK(!grep_file("stdout", "stderr: msg"));
+ ATF_CHECK(atf_utils_grep_file("stdout: msg", "stdout"));
+ ATF_CHECK(!atf_utils_grep_file("stderr: msg", "stdout"));
break;
case stderr_type:
- ATF_CHECK(grep_file("stderr", "stderr: msg"));
- ATF_CHECK(!grep_file("stderr", "stdout: msg"));
+ ATF_CHECK(atf_utils_grep_file("stderr: msg", "stderr"));
+ ATF_CHECK(!atf_utils_grep_file("stdout: msg", "stderr"));
break;
default:
UNREACHABLE;
@@ -110,7 +110,7 @@ check_file(const enum out_type type)
struct capture_stream {
struct base_stream m_base;
- atf_dynstr_t m_msg;
+ char *m_msg;
};
#define CAPTURE_STREAM(type) \
{ .m_base = BASE_STREAM(capture_stream_init, \
@@ -126,7 +126,7 @@ capture_stream_init(void *v)
s->m_base.m_sb_ptr = &s->m_base.m_sb;
RE(atf_process_stream_init_capture(&s->m_base.m_sb));
- RE(atf_dynstr_init(&s->m_msg));
+ s->m_msg = NULL;
}
static
@@ -137,10 +137,10 @@ capture_stream_process(void *v, atf_process_child_t *c)
switch (s->m_base.m_type) {
case stdout_type:
- (void)read_line(atf_process_child_stdout(c), &s->m_msg);
+ s->m_msg = atf_utils_readline(atf_process_child_stdout(c));
break;
case stderr_type:
- (void)read_line(atf_process_child_stderr(c), &s->m_msg);
+ s->m_msg = atf_utils_readline(atf_process_child_stderr(c));
break;
default:
UNREACHABLE;
@@ -155,18 +155,18 @@ capture_stream_fini(void *v)
switch (s->m_base.m_type) {
case stdout_type:
- ATF_CHECK(grep_string(&s->m_msg, "stdout: msg"));
- ATF_CHECK(!grep_string(&s->m_msg, "stderr: msg"));
+ 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(!grep_string(&s->m_msg, "stdout: msg"));
- ATF_CHECK(grep_string(&s->m_msg, "stderr: msg"));
+ 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;
}
- atf_dynstr_fini(&s->m_msg);
+ free(s->m_msg);
atf_process_stream_fini(&s->m_base.m_sb);
}
@@ -881,16 +881,10 @@ static
void
check_line(int fd, const char *exp)
{
- atf_dynstr_t line;
- bool eof;
-
- atf_dynstr_init(&line);
- eof = read_line(fd, &line);
- ATF_CHECK(!eof);
- ATF_CHECK_MSG(atf_equal_dynstr_cstring(&line, exp),
- "read: '%s', expected: '%s'",
- atf_dynstr_cstring(&line), exp);
- atf_dynstr_fini(&line);
+ 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);
diff --git a/atf-c/detail/sanity_test.c b/atf-c/detail/sanity_test.c
index af2bbc06f671d..7c8285b5193c1 100644
--- a/atf-c/detail/sanity_test.c
+++ b/atf-c/detail/sanity_test.c
@@ -53,21 +53,6 @@
enum type { inv, pre, post, unreachable };
-static
-bool
-grep(const atf_dynstr_t *line, const char *text)
-{
- const char *l = atf_dynstr_cstring(line);
- bool found;
-
- found = false;
-
- if (strstr(l, text) != NULL)
- found = true;
-
- return found;
-}
-
struct test_data {
enum type m_type;
bool m_cond;
@@ -109,9 +94,8 @@ do_test(enum type t, bool cond)
{
atf_process_child_t child;
atf_process_status_t status;
- bool eof;
int nlines;
- atf_dynstr_t lines[3];
+ char *lines[3];
{
atf_process_stream_t outsb, errsb;
@@ -125,13 +109,9 @@ do_test(enum type t, bool cond)
}
nlines = 0;
- eof = false;
- do {
- RE(atf_dynstr_init(&lines[nlines]));
- if (!eof)
- eof = read_line(atf_process_child_stderr(&child), &lines[nlines]);
+ while (nlines < 3 && (lines[nlines] =
+ atf_utils_readline(atf_process_child_stderr(&child))) != NULL)
nlines++;
- } while (nlines < 3);
ATF_REQUIRE(nlines == 0 || nlines == 3);
RE(atf_process_child_wait(&child, &status));
@@ -147,29 +127,29 @@ do_test(enum type t, bool cond)
if (!cond) {
switch (t) {
case inv:
- ATF_REQUIRE(grep(&lines[0], "Invariant"));
+ ATF_REQUIRE(atf_utils_grep_string("Invariant", lines[0]));
break;
case pre:
- ATF_REQUIRE(grep(&lines[0], "Precondition"));
+ ATF_REQUIRE(atf_utils_grep_string("Precondition", lines[0]));
break;
case post:
- ATF_REQUIRE(grep(&lines[0], "Postcondition"));
+ ATF_REQUIRE(atf_utils_grep_string("Postcondition", lines[0]));
break;
case unreachable:
- ATF_REQUIRE(grep(&lines[0], "Invariant"));
+ ATF_REQUIRE(atf_utils_grep_string("Invariant", lines[0]));
break;
}
- ATF_REQUIRE(grep(&lines[0], __FILE__));
- ATF_REQUIRE(grep(&lines[2], PACKAGE_BUGREPORT));
+ ATF_REQUIRE(atf_utils_grep_string(__FILE__, lines[0]));
+ ATF_REQUIRE(atf_utils_grep_string(PACKAGE_BUGREPORT, lines[2]));
}
while (nlines > 0) {
nlines--;
- atf_dynstr_fini(&lines[nlines]);
+ free(lines[nlines]);
}
}
diff --git a/atf-c/detail/test_helpers.c b/atf-c/detail/test_helpers.c
index b20a8498331c6..aa64c12de4054 100644
--- a/atf-c/detail/test_helpers.c
+++ b/atf-c/detail/test_helpers.c
@@ -30,7 +30,6 @@
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
-#include <regex.h>
#include <unistd.h>
#include "atf-c/build.h"
@@ -106,72 +105,6 @@ get_process_helpers_path(const atf_tc_t *tc, const bool is_detail,
is_detail ? "" : "detail/"));
}
-bool
-grep_string(const atf_dynstr_t *str, const char *regex)
-{
- int res;
- regex_t preg;
-
- printf("Looking for '%s' in '%s'\n", regex, atf_dynstr_cstring(str));
- ATF_REQUIRE(regcomp(&preg, regex, REG_EXTENDED) == 0);
-
- res = regexec(&preg, atf_dynstr_cstring(str), 0, NULL, 0);
- ATF_REQUIRE(res == 0 || res == REG_NOMATCH);
-
- regfree(&preg);
-
- return res == 0;
-}
-
-bool
-grep_file(const char *file, const char *regex, ...)
-{
- bool done, found;
- int fd;
- va_list ap;
- atf_dynstr_t formatted;
-
- va_start(ap, regex);
- RE(atf_dynstr_init_ap(&formatted, regex, ap));
- va_end(ap);
-
- done = false;
- found = false;
- ATF_REQUIRE((fd = open(file, O_RDONLY)) != -1);
- do {
- atf_dynstr_t line;
-
- RE(atf_dynstr_init(&line));
-
- done = read_line(fd, &line);
- if (!done)
- found = grep_string(&line, atf_dynstr_cstring(&formatted));
-
- atf_dynstr_fini(&line);
- } while (!found && !done);
- close(fd);
-
- atf_dynstr_fini(&formatted);
-
- return found;
-}
-
-bool
-read_line(int fd, atf_dynstr_t *dest)
-{
- char ch;
- ssize_t cnt;
-
- while ((cnt = read(fd, &ch, sizeof(ch))) == sizeof(ch) &&
- ch != '\n') {
- const atf_error_t err = atf_dynstr_append_fmt(dest, "%c", ch);
- ATF_REQUIRE(!atf_is_error(err));
- }
- ATF_REQUIRE(cnt != -1);
-
- return cnt == 0;
-}
-
struct run_h_tc_data {
atf_tc_t *m_tc;
const char *m_resname;
diff --git a/atf-c/detail/test_helpers.h b/atf-c/detail/test_helpers.h
index b0ce6ccca46c6..5df034fb42ad9 100644
--- a/atf-c/detail/test_helpers.h
+++ b/atf-c/detail/test_helpers.h
@@ -81,7 +81,5 @@ void build_check_c_o(const atf_tc_t *, const char *, const char *, const bool);
void header_check(const char *);
void get_process_helpers_path(const atf_tc_t *, const bool,
struct atf_fs_path *);
-bool grep_string(const struct atf_dynstr *, const char *);
-bool grep_file(const char *, const char *, ...);
bool read_line(int, struct atf_dynstr *);
void run_h_tc(atf_tc_t *, const char *, const char *, const char *);
diff --git a/atf-c/detail/test_helpers_test.c b/atf-c/detail/test_helpers_test.c
deleted file mode 100644
index 52d8608b701c0..0000000000000
--- a/atf-c/detail/test_helpers_test.c
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Automated Testing Framework (atf)
- *
- * Copyright (c) 2009 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
- * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
- * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
- * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
- * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include <fcntl.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include <atf-c.h>
-
-#include "dynstr.h"
-#include "test_helpers.h"
-
-/* ---------------------------------------------------------------------
- * Test cases for the free functions.
- * --------------------------------------------------------------------- */
-
-/* TODO: Add checks for build_check_c_o and the macros defined in the
- * header file. */
-
-ATF_TC(grep_string);
-ATF_TC_HEAD(grep_string, tc)
-{
- atf_tc_set_md_var(tc, "descr", "Tests the grep_string helper "
- "function");
-}
-ATF_TC_BODY(grep_string, tc)
-{
- atf_dynstr_t str;
-
- atf_dynstr_init_fmt(&str, "a string - aaaabbbb");
- ATF_CHECK(grep_string(&str, "a string"));
- ATF_CHECK(grep_string(&str, "^a string"));
- ATF_CHECK(grep_string(&str, "aaaabbbb$"));
- ATF_CHECK(grep_string(&str, "aa.*bb"));
- ATF_CHECK(!grep_string(&str, "foo"));
- ATF_CHECK(!grep_string(&str, "bar"));
- ATF_CHECK(!grep_string(&str, "aaaaa"));
-
- atf_dynstr_fini(&str);
-}
-
-
-ATF_TC(grep_file);
-ATF_TC_HEAD(grep_file, tc)
-{
- atf_tc_set_md_var(tc, "descr", "Tests the grep_file helper function");
-}
-ATF_TC_BODY(grep_file, tc)
-{
- FILE *f;
-
- f = fopen("test.txt", "w");
- ATF_CHECK(f != NULL);
- fprintf(f, "line1\n");
- fprintf(f, "the second line\n");
- fprintf(f, "aaaabbbb\n");
- fclose(f);
-
- ATF_CHECK(grep_file("test.txt", "line1"));
- ATF_CHECK(grep_file("test.txt", "line%d", 1));
- ATF_CHECK(grep_file("test.txt", "second line"));
- ATF_CHECK(grep_file("test.txt", "aa.*bb"));
- ATF_CHECK(!grep_file("test.txt", "foo"));
- ATF_CHECK(!grep_file("test.txt", "bar"));
- ATF_CHECK(!grep_file("test.txt", "aaaaa"));
-}
-
-ATF_TC(read_line);
-ATF_TC_HEAD(read_line, tc)
-{
- atf_tc_set_md_var(tc, "descr", "Tests the read_line function");
-}
-ATF_TC_BODY(read_line, 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";
-
- {
- FILE *f;
-
- f = fopen("test", "w");
- ATF_REQUIRE(f != NULL);
- fclose(f);
- }
-
- {
- int fd;
- atf_dynstr_t dest;
- bool eof;
-
- fd = open("test", O_RDONLY);
- ATF_REQUIRE(fd != -1);
-
- RE(atf_dynstr_init(&dest));
- eof = read_line(fd, &dest);
- ATF_REQUIRE(eof);
- atf_dynstr_fini(&dest);
- }
-
- {
- FILE *f;
-
- f = fopen("test", "w");
- ATF_REQUIRE(f != NULL);
-
- fprintf(f, "%s\n", l1);
- fprintf(f, "%s\n", l2);
- fprintf(f, "%s", l3);
-
- fclose(f);
- }
-
- {
- int fd;
- atf_dynstr_t dest;
- bool eof;
-
- fd = open("test", O_RDONLY);
- ATF_REQUIRE(fd != -1);
-
- RE(atf_dynstr_init(&dest));
- eof = read_line(fd, &dest);
- ATF_REQUIRE(!eof);
- printf("1st line: >%s<\n", atf_dynstr_cstring(&dest));
- ATF_REQUIRE(atf_equal_dynstr_cstring(&dest, l1));
- atf_dynstr_fini(&dest);
-
- RE(atf_dynstr_init(&dest));
- eof = read_line(fd, &dest);
- ATF_REQUIRE(!eof);
- printf("2nd line: >%s<\n", atf_dynstr_cstring(&dest));
- ATF_REQUIRE(atf_equal_dynstr_cstring(&dest, l2));
- atf_dynstr_fini(&dest);
-
- RE(atf_dynstr_init(&dest));
- eof = read_line(fd, &dest);
- ATF_REQUIRE(eof);
- printf("3rd line: >%s<\n", atf_dynstr_cstring(&dest));
- ATF_REQUIRE(atf_equal_dynstr_cstring(&dest, l3));
- atf_dynstr_fini(&dest);
-
- close(fd);
- }
-}
-
-/* ---------------------------------------------------------------------
- * Main.
- * --------------------------------------------------------------------- */
-
-ATF_TP_ADD_TCS(tp)
-{
- /* Add the tests for the free functions. */
- ATF_TP_ADD_TC(tp, grep_string);
- ATF_TP_ADD_TC(tp, grep_file);
- ATF_TP_ADD_TC(tp, read_line);
-
- return atf_no_error();
-}
diff --git a/atf-c/macros.h b/atf-c/macros.h
index 12f7488b6b9c4..7c33ccbc1e100 100644
--- a/atf-c/macros.h
+++ b/atf-c/macros.h
@@ -30,6 +30,8 @@
#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>
@@ -78,7 +80,7 @@
#define ATF_TC_HEAD(tc, tcptr) \
static \
void \
- atfu_ ## tc ## _head(atf_tc_t *tcptr)
+ atfu_ ## tc ## _head(atf_tc_t *tcptr ATF_DEFS_ATTRIBUTE_UNUSED)
#define ATF_TC_HEAD_NAME(tc) \
(atfu_ ## tc ## _head)
@@ -179,6 +181,24 @@
ATF_CHECK_MSG(strcmp(x, y) == 0, "%s != %s (%s != %s): " fmt, \
#x, #y, x, y, ##__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)
diff --git a/atf-c/macros_test.c b/atf-c/macros_test.c
index 9cf0525bf1df1..f077a273d73dd 100644
--- a/atf-c/macros_test.c
+++ b/atf-c/macros_test.c
@@ -125,6 +125,11 @@ init_and_run_h_tc(const char *name, void (*head)(atf_tc_t *),
#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) \
@@ -139,6 +144,13 @@ init_and_run_h_tc(const char *name, void (*head)(atf_tc_t *),
#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) \
@@ -164,6 +176,11 @@ init_and_run_h_tc(const char *name, void (*head)(atf_tc_t *),
#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) \
@@ -178,6 +195,13 @@ init_and_run_h_tc(const char *name, void (*head)(atf_tc_t *),
#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) \
@@ -240,11 +264,11 @@ ATF_TC_BODY(check_errno, tc)
ATF_REQUIRE(exists("after"));
if (t->ok) {
- ATF_REQUIRE(grep_file("result", "^passed"));
+ ATF_REQUIRE(atf_utils_grep_file("^passed", "result"));
} else {
- ATF_REQUIRE(grep_file("result", "^failed"));
- ATF_REQUIRE(grep_file("error", "macros_test.c:[0-9]+: %s$",
- t->exp_regex));
+ 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);
@@ -282,11 +306,12 @@ ATF_TC_BODY(require_errno, tc)
ATF_REQUIRE(exists("before"));
if (t->ok) {
- ATF_REQUIRE(grep_file("result", "^passed"));
+ ATF_REQUIRE(atf_utils_grep_file("^passed", "result"));
ATF_REQUIRE(exists("after"));
} else {
- ATF_REQUIRE(grep_file("result", "^failed: .*macros_test.c:[0-9]+: "
- "%s$", t->exp_regex));
+ ATF_REQUIRE(atf_utils_grep_file(
+ "^failed: .*macros_test.c:[0-9]+: %s$", "result",
+ t->exp_regex));
ATF_REQUIRE(!exists("after"));
}
@@ -340,11 +365,11 @@ ATF_TC_BODY(check, tc)
ATF_REQUIRE(exists("after"));
if (t->ok) {
- ATF_REQUIRE(grep_file("result", "^passed"));
+ ATF_REQUIRE(atf_utils_grep_file("^passed", "result"));
} else {
- ATF_REQUIRE(grep_file("result", "^failed"));
- ATF_REQUIRE(grep_file("error", "Check failed: .*"
- "macros_test.c:[0-9]+: %s$", t->msg));
+ 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);
@@ -381,11 +406,11 @@ do_check_eq_tests(const struct check_eq_test *tests)
ATF_CHECK(exists("after"));
if (t->ok) {
- ATF_REQUIRE(grep_file("result", "^passed"));
+ ATF_REQUIRE(atf_utils_grep_file("^passed", "result"));
} else {
- ATF_REQUIRE(grep_file("result", "^failed"));
- ATF_CHECK(grep_file("error", "Check failed: .*"
- "macros_test.c:[0-9]+: %s$", t->msg));
+ 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);
@@ -442,8 +467,8 @@ 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 const char *check_streq_var1 = CHECK_STREQ_VAR1;
-const const char *check_streq_var2 = CHECK_STREQ_VAR2;
+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);
@@ -485,6 +510,40 @@ ATF_TC_BODY(check_streq, tc)
}
/* ---------------------------------------------------------------------
+ * 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.
* --------------------------------------------------------------------- */
@@ -526,11 +585,11 @@ ATF_TC_BODY(require, tc)
ATF_REQUIRE(exists("before"));
if (t->ok) {
- ATF_REQUIRE(grep_file("result", "^passed"));
+ ATF_REQUIRE(atf_utils_grep_file("^passed", "result"));
ATF_REQUIRE(exists("after"));
} else {
- ATF_REQUIRE(grep_file("result", "^failed: .*macros_test.c:[0-9]+: "
- "%s$", t->msg));
+ ATF_REQUIRE(atf_utils_grep_file(
+ "^failed: .*macros_test.c:[0-9]+: %s$", "result", t->msg));
ATF_REQUIRE(!exists("after"));
}
@@ -567,11 +626,11 @@ do_require_eq_tests(const struct require_eq_test *tests)
ATF_REQUIRE(exists("before"));
if (t->ok) {
- ATF_REQUIRE(grep_file("result", "^passed"));
+ ATF_REQUIRE(atf_utils_grep_file("^passed", "result"));
ATF_REQUIRE(exists("after"));
} else {
- ATF_REQUIRE(grep_file("result", "^failed: .*macros_test.c"
- ":[0-9]+: %s$", t->msg));
+ ATF_REQUIRE(atf_utils_grep_file("^failed: .*macros_test.c"
+ ":[0-9]+: %s$", "result", t->msg));
ATF_REQUIRE(!exists("after"));
}
@@ -630,8 +689,8 @@ 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 const char *require_streq_var1 = REQUIRE_STREQ_VAR1;
-const const char *require_streq_var2 = REQUIRE_STREQ_VAR2;
+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);
@@ -673,6 +732,41 @@ ATF_TC_BODY(require_streq, tc)
}
/* ---------------------------------------------------------------------
+ * 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.
* --------------------------------------------------------------------- */
@@ -728,12 +822,12 @@ ATF_TC_BODY(msg_embedded_fmt, tc)
if (t->fatal) {
bool matched =
- grep_file("result", "^failed: .*macros_test.c:[0-9]+: "
- "%s$", t->msg);
+ 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 = grep_file("error", "Check failed: .*"
- "macros_test.c:[0-9]+: %s$", t->msg);
+ 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");
}
}
@@ -765,11 +859,13 @@ ATF_TP_ADD_TCS(tp)
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);
diff --git a/atf-c/utils.c b/atf-c/utils.c
index c33270390ace6..4409f7a4fb9ef 100644
--- a/atf-c/utils.c
+++ b/atf-c/utils.c
@@ -27,10 +27,221 @@
* 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/utils.h"
+#include <atf-c.h>
+
+#include "detail/dynstr.h"
+
+/** 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_utils_redirect(STDOUT_FILENO, "atf_utils_fork_out.txt");
+ atf_utils_redirect(STDERR_FILENO, "atf_utils_fork_err.txt");
+ }
+ 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)
{
@@ -41,3 +252,164 @@ atf_utils_free_charpp(char **argv)
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_utils_cat_file("atf_utils_fork_out.txt", "subprocess stdout: ");
+ atf_utils_cat_file("atf_utils_fork_err.txt", "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_utils_fork_out.txt",
+ expout + save_prefix_length);
+ } else {
+ ATF_REQUIRE(atf_utils_compare_file("atf_utils_fork_out.txt", expout));
+ }
+
+ if (strlen(experr) > save_prefix_length &&
+ strncmp(experr, save_prefix, save_prefix_length) == 0) {
+ atf_utils_copy_file("atf_utils_fork_err.txt",
+ experr + save_prefix_length);
+ } else {
+ ATF_REQUIRE(atf_utils_compare_file("atf_utils_fork_err.txt", experr));
+ }
+
+ ATF_REQUIRE(unlink("atf_utils_fork_out.txt") != -1);
+ ATF_REQUIRE(unlink("atf_utils_fork_err.txt") != -1);
+}
diff --git a/atf-c/utils.h b/atf-c/utils.h
index dc4c5ae6f00b7..666804d3a2c1f 100644
--- a/atf-c/utils.h
+++ b/atf-c/utils.h
@@ -30,6 +30,25 @@
#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 /* ATF_C_UTILS_H */
diff --git a/atf-c/utils_test.c b/atf-c/utils_test.c
index 146272a713e6f..57d6af8d2a251 100644
--- a/atf-c/utils_test.c
+++ b/atf-c/utils_test.c
@@ -27,8 +27,15 @@
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#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>
@@ -36,8 +43,218 @@
#include "detail/test_helpers.h"
-ATF_TC_WITHOUT_HEAD(free_charpp_empty);
-ATF_TC_BODY(free_charpp_empty, tc)
+/** 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));
+
+ char buffer[1024];
+ read_file("atf_utils_fork_out.txt", buffer, sizeof(buffer));
+ ATF_REQUIRE_STREQ("Child stdout\n", buffer);
+ read_file("atf_utils_fork_err.txt", buffer, sizeof(buffer));
+ ATF_REQUIRE_STREQ("Child stderr\n", buffer);
+}
+
+ATF_TC_WITHOUT_HEAD(free_charpp__empty);
+ATF_TC_BODY(free_charpp__empty, tc)
{
char **array = malloc(sizeof(char *) * 1);
array[0] = NULL;
@@ -45,8 +262,8 @@ ATF_TC_BODY(free_charpp_empty, tc)
atf_utils_free_charpp(array);
}
-ATF_TC_WITHOUT_HEAD(free_charpp_some);
-ATF_TC_BODY(free_charpp_some, tc)
+ATF_TC_WITHOUT_HEAD(free_charpp__some);
+ATF_TC_BODY(free_charpp__some, tc)
{
char **array = malloc(sizeof(char *) * 4);
array[0] = strdup("first");
@@ -57,12 +274,263 @@ ATF_TC_BODY(free_charpp_some, tc)
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();
+ 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__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"));
+ }
+}
+
HEADER_TC(include, "atf-c/utils.h");
ATF_TP_ADD_TCS(tp)
{
- ATF_TP_ADD_TC(tp, free_charpp_empty);
- ATF_TP_ADD_TC(tp, free_charpp_some);
+ 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__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);
ATF_TP_ADD_TC(tp, include);