From 3f377aa117273eba5c77ad652c0b086446b3f874 Mon Sep 17 00:00:00 2001 From: Russ Allbery Date: Mon, 3 Aug 2020 20:59:25 -0400 Subject: [PATCH] Import of code from c-tap-harness MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit This commit updates the code imported from c-tap-harness to abdb66561ffd4d2f238fdb06f448ccf09d80c059 (release/4.7) Upstream changes are: Daniel Collins (1): Add is_blob() test function. Daniel Kahn Gillmor (1): LICENSE: use https for all URLs Daria Brashear (1): Add verbose mode environment variable to runtests Julien ÉLIE (2): Document -v in usage and comments of runtests Avoid realloc of zero length in tests/runtests.c Marc Dionne (1): Add test_cleanup_register_with_data Russ Allbery (115): clang --analyze cleanups for runtests Modernize POD tests Update README to my current layout Explicitly note that test programs must be executable Fix comment typo in tests/runtests.c Switch to a copyright-format 1.0 LICENSE file Flush harness output after each line Show the test count as ? when the plan is deferred More correctly backspace over test counts when aborting Refactor test list handling Allow passing tests on the runtests command line Don't allow command-line arguments if a list was given Search for tests under the name given as well Release 2.0 Fix backward incompatibility when searching for tests Document decision to ignore TAP version directives Release 2.1 Document different runtests behavior in bail handling Change exit status of bail to 255 Release 2.2 Add a new test_cleanup_register C API Add warn_unused_result attributes Add portability for warn_unsed_result attributes to tap/macros.h Minor coding style fix (spacing) in runtests.c Split the runtests usage string for ISO C90 string limits Include stddef.h Diagnose failure to register the exit handler Use diag internally in the basic C TAP library Some additional comments about cleanup functions Move repetitive printing code in the C TAP library to a macro Set a flag when bailing for more correct cleanup Change my email address to eagle@eyrie.org Release 2.3 Add diag_file_add and diag_file_remove functions Don't die for unknown files passed to diag_file_remove Release 2.4 Update comment about AIX and WCOREDUMP Don't test for NULL before calling free Be more careful about file descriptors in child processes Run cleanup functions in non-primary processes as well Release 3.0 Update collective package copyright notices at start of LICENSE Check integer overflows on memory allocation, fix string creation Switch POD spelling test to use Lancaster consensus variable Add new bnrealloc API for brealloc with checked multiplication Rename nrealloc to reallocarray Return the test status from test functions Fix the overflow check for breallocarray Fix the overflow check for xreallocarray in runtests Restructure test result reallocation in runtests Change diag and sysdiag to always return true Release 3.1 Fix typos in basic.c and basic.h Fix usage message when running runtests with no arguments Update introductory runtests comments for current syntax Add the -l flag to suggested runtests invocation in README Support comments and blank lines in test lists Release 3.2 Update licensing information Various improvements to verbose support Compile warning-free with Clang, check Autoconf macros Release 3.3 Remove unnecessary assert.h include in tap/basic.c Fix some additional -v documentation issues Rebalance usage to avoid too-long strings Fix segfault in runtests with empty test list Release 3.4 Document running autogen if starting from Git Rename autogen to bootstrap Support and prefer C_TAP_SOURCE and C_TAP_BUILD Fix comment typo in tests/runtests.c Add missing va_end to is_double Release 4.0 Fix all non-https www.eyrie.org URLs Add is_bool C test function Add DocKnot metadata and a Markdown README file Update documentation for new DocKnot standards Release 4.1 Use more defaults from DocKnot templates Fix new fall-through warning in GCC 7 Use compiler warnings from rra-c-util, fix issues Merge pull request #4 from solemnwarning/master Coding style fixes and NEWS for is_blob Re-enable -Wunknown-pragmas for GCC Avoid zero-length realloc allocations in breallocarray Update copyright date on tests/runtests.c Release 4.2 Add SPDX-License-Identifier headers to source files Add and run new check-cppcheck target Fix instructions for running one test Identify values as left and right Fix is_string comparisons with NULL pointers Add support for running tests under valgrind Replace putc with fprintf Update shared files from rra-c-util Release 4.3 Update NEWS date for 4.3 release Collapse some copyright dates NEWS and coding style for test_cleanup_register_with_data Remove unused variables caught by Clang scan-build Update to rra-c-util 8.0 Fix error checking in bstrndup Release 4.4 Add support for C++ Document that C TAP Harness can be built as C++ Release 4.5 Regenerate README files Reformat using clang-format 10 Update to rra-c-util 8.1 Release 4.6 Fix spelling errors caught by codespell Protect the test suite against C_TAP_VERBOSE Switch to GitHub Actions for CI Add NEWS entry for GCC 10 warning fixes Release 4.7 Change-Id: I5a78215bf99b53bd848f0fa6bb9092deab38f24e Reviewed-on: https://gerrit.openafs.org/14294 Reviewed-by: Andrew Deason Tested-by: Andrew Deason Reviewed-by: Benjamin Kaduk --- src/external/c-tap-harness-last | 2 +- src/external/c-tap-harness/LICENSE | 290 +++--- src/external/c-tap-harness/NEWS | 272 +++++- src/external/c-tap-harness/README | 214 +++-- src/external/c-tap-harness/tests/runtests.c | 1220 +++++++++++++++++------- src/external/c-tap-harness/tests/tap/basic.c | 752 +++++++++++---- src/external/c-tap-harness/tests/tap/basic.h | 124 ++- src/external/c-tap-harness/tests/tap/float.c | 57 +- src/external/c-tap-harness/tests/tap/float.h | 11 +- src/external/c-tap-harness/tests/tap/libtap.sh | 34 +- src/external/c-tap-harness/tests/tap/macros.h | 37 +- 11 files changed, 2228 insertions(+), 785 deletions(-) diff --git a/src/external/c-tap-harness-last b/src/external/c-tap-harness-last index 3420957..3f9f4cb 100644 --- a/src/external/c-tap-harness-last +++ b/src/external/c-tap-harness-last @@ -1 +1 @@ -d3fc03606efc8e76ff34f04470e6133db25a3982 +abdb66561ffd4d2f238fdb06f448ccf09d80c059 diff --git a/src/external/c-tap-harness/LICENSE b/src/external/c-tap-harness/LICENSE index 5f95816..783d176 100644 --- a/src/external/c-tap-harness/LICENSE +++ b/src/external/c-tap-harness/LICENSE @@ -1,138 +1,154 @@ -The C TAP Harness package as a whole is: +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Comment: This file documents the copyright statements and licenses for + every file in this package in a machine-readable format. For a less + detailed, higher-level overview, see README. + . + For any copyright year range specified as YYYY-ZZZZ in this file, the + range specifies every single year in that closed interval. + +Files: * +Copyright: 2000-2001, 2004, 2006-2020 Russ Allbery + 2001-2002, 2004-2014 + The Board of Trustees of the Leland Stanford Junior University +License: Expat + +Files: .clang-format docs/api/bail.3 docs/api/bail.pod + docs/api/bcalloc_type.3 docs/api/bcalloc_type.pod docs/api/bmalloc.3 + docs/api/bmalloc.pod docs/api/breallocarray.3 docs/api/breallocarray.pod + docs/api/diag.3 docs/api/diag.pod docs/api/diag_file_add.3 + docs/api/diag_file_add.pod docs/api/is_int.3 docs/api/is_int.pod + docs/api/ok.3 docs/api/ok.pod docs/api/plan.3 docs/api/plan.pod + docs/api/skip.3 docs/api/skip.pod docs/api/skip_all.3 + docs/api/skip_all.pod docs/api/test_cleanup_register.3 + docs/api/test_cleanup_register.pod docs/api/test_file_path.3 + docs/api/test_file_path.pod docs/api/test_tmpdir.3 + docs/api/test_tmpdir.pod docs/runtests.1 docs/runtests.pod + docs/writing-tests tests/data/cppcheck.supp +Copyright: 2009-2020 Russ Allbery +License: all-permissive + Copying and distribution of this file, with or without modification, are + permitted in any medium without royalty provided the copyright notice and + this notice are preserved. This file is offered as-is, without any + warranty. + +Files: Makefile.in +Copyright: 1994-2020 Free Software Foundation, Inc. + 2008-2020 Russ Allbery +License: FSF-unlimited and Expat + +Files: aclocal.m4 +Copyright: 1996-2020 Free Software Foundation, Inc. +License: FSF-unlimited + +Files: build-aux/ar-lib build-aux/compile build-aux/depcomp + build-aux/missing +Copyright: 1996-2020 Free Software Foundation, Inc. +License: GPL-2+ with Autoconf exception or Expat + +Files: build-aux/install-sh +Copyright: 1994 X Consortium +License: X11 + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + . + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + . + Except as contained in this notice, the name of the X Consortium shall + not be used in advertising or otherwise to promote the sale, use or other + dealings in this Software without prior written authorization from the X + Consortium. + +Files: configure +Copyright: 1992-1996, 1998-2012 Free Software Foundation, Inc. +License: FSF-configure + This script is free software; the Free Software Foundation gives unlimited + permission to copy, distribute and modify it. + +Files: m4/cc-flags.m4 +Copyright: 2006, 2009, 2016 Internet Systems Consortium, Inc. + 2016-2020 Russ Allbery +License: ISC + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + . + THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY + SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Files: m4/clang.m4 +Copyright: 2015 Russ Allbery +License: unlimited + This file is free software; the authors give unlimited permission to copy + and/or distribute it, with or without modifications, as long as this + notice is preserved. + +License: Expat + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + . + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT + OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +License: FSF-unlimited + This file is free software; the Free Software Foundation gives unlimited + permission to copy and/or distribute it, with or without modifications, as + long as this notice is preserved. + . + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY, to the extent permitted by law; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +License: GPL-2+ with Autoconf exception + This file is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + . + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + Public License for more details. + . + You should have received a copy of the GNU General Public License along + with this program. If not, see . + . + As a special exception to the GNU General Public License, if you + distribute this file as part of a program that contains a configuration + script generated by Autoconf, you may include it under the same + distribution terms that you use for the rest of that program. +Comment: The option described in the license has been accepted and these + files are distributed under the same terms as the package as a whole, as + described at the top of this file. - Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011, 2012 - Russ Allbery - Copyright 2006, 2007, 2008, 2009, 2011, 2012 - The Board of Trustees of the Leland Stanford Junior University - -and released under the following license: - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -All individual files without an explicit exception below are released -under this license. Some files may have additional copyright holders as -noted in those files. - -Some files in this distribution are individually released under different -licenses, all of which are compatible with the above general package -license but which may require preservation of additional notices. All -required notices are preserved in this file. Of the files intended to be -copied into other packages, only docs/writing-tests has a different -license notice, and its requirements are met by preserving the license -section of that document in any derivative works. - -Collected copyright notices for the entire package: - - Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011, 2012 - Russ Allbery - Copyright 2006, 2007, 2008, 2009, 2011, 2012 - The Board of Trustees of the Leland Stanford Junior University - Copyright 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, - 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 - Free Software Foundation, Inc. - Copyright 1994 X Consortium - -The file docs/writing-tests is released under the following license: - - Copying and distribution of this file, with or without modification, are - permitted in any medium without royalty provided the copyright notice - and this notice are preserved. This file is offered as-is, without any - warranty. - -The files Makefile.in and aclocal.m4 are generated by GNU Automake and -released under the following copyright and license: - - Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, - 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software - Foundation, Inc. This file is free software; the Free Software - Foundation gives unlimited permission to copy and/or distribute it, with - or without modifications, as long as this notice is preserved. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY, to the extent permitted by law; without - even the implied warranty of MERCHANTABILITY or FITNESS FOR A - PARTICULAR PURPOSE. - -The file configure is generated by GNU Autoconf and is released under the -following copyright and license: - - Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, - 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software - Foundation, Inc. This configure script is free software; the Free - Software Foundation gives unlimited permission to copy, distribute and - modify it. - -The files build-aux/compile, build-aux/depcomp, and build-aux/missing are -taken from GNU Automake and are released under the following copyright and -license: - - Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006, - 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. - - This program is free software; you can redistribute it and/or modify it - under the terms of the GNU General Public License as published by the - Free Software Foundation; either version 2, or (at your option) any - later version. - - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - As a special exception to the GNU General Public License, if you - distribute this file as part of a program that contains a configuration - script generated by Autoconf, you may include it under the same - distribution terms that you use for the rest of that program. - -For the C TAP Harness distribution, the option described in the last -paragraph has been accepted and these files are distributed under the same -terms as the C TAP Harness package as a whole, as described at the top of -this file. - -The file build-aux/install-sh is released under the following copyright -and license: - - Copyright (C) 1994 X Consortium - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be included - in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of the X Consortium shall - not be used in advertising or otherwise to promote the sale, use or - other dealings in this Software without prior written authorization - from the X Consortium. - - FSF changes to this file are in the public domain. diff --git a/src/external/c-tap-harness/NEWS b/src/external/c-tap-harness/NEWS index be4aba8..7e4a617 100644 --- a/src/external/c-tap-harness/NEWS +++ b/src/external/c-tap-harness/NEWS @@ -1,5 +1,275 @@ User-Visible C TAP Harness Changes +C TAP Harness 4.7 (2020-05-16) + + Allow the package test suite to be run with C_TAP_VERBOSE without + breaking test results via inheritance of that setting. + + Fix warnings with GCC 10. + +C TAP Harness 4.6 (2020-01-07) + + Reformat all C source using clang-format 10 and the formatting rules + specified in .clang-format. + + Update to rra-c-util 8.1: + + * Drop support for Perl versions prior to Perl 5.8. + +C TAP Harness 4.5 (2019-08-31) + + Add new bcalloc_type and breallocarray_type macros that take a type + instead of a size as their third argument and cast the return value to + a pointer to that type. These are more friendly to C++ code than + C-style allocation functions returning void *. + + The test harness and libtap C library can now alternatively be + compiled with a C++ compiler, making it easier to incorporate them + into a C++ project. Thanks to Peter Paris for the initial report. + +C TAP Harness 4.4 (2018-12-25) + + Add test_cleanup_register_with_data to the C TAP library. This is the + same as test_cleanup_register except takes a generic pointer, which is + then passed to the cleanup function as a third argument. This should + have been the only API from the beginning, but test_cleanup_register + is preserved for backward compatibility. Patch from Marc Dionne. + + Fix error checking for malloc failure in bstrndup function (caught by + cppcheck). + + Update to rra-c-util 8.0: + + * Skip tests requiring Test::More if it isn't available. + * Check for pre-SPDX license grant strings. + * Improved test for obsolete strings in package source files. + +C TAP Harness 4.3 (2018-05-06) + + Add support for valgrind and libtool test options in test lists. Test + lists now take a space-separated set of options after the test name. + If the valgrind option is present and C_TAP_VALGRIND is set in the + environment, the test will be run by passing it as an option to the + command given in C_TAP_VALGRIND. If the libtool option is also set, + valgrind will be run via the libtool script set in C_TAP_LIBTOOL, + using --mode=execute, so that valgrind will run on the underlying + binary and not the libtool shell wrapper. + + On test failures, report the values as left and right instead of + wanted and seen. This idea is stolen from the Rust assert framework. + It avoids having to care about the order in which values are passed + into the test functions. + + Fix is_string comparisons involving NULL pointers so that the string + "(null)" will no longer compare equal to NULL (although the diagnostic + output on test failure is still mildly confusing). + + Add new check-cppcheck target that runs cppcheck on all source code, + and fix one unnecessary check for NULL that it uncovered. + + Add SPDX-License-Identifier headers to all substantial source files, + and add a test to check for them. This imports more supporting test + machinery whose canonical home is in rra-c-util. If you want to use + files in tests/tap/perl, copy them from rra-c-util instead of this + package. + + C TAP Harness now imports the Perl test modules from rra-c-util to + support some checks, so tests/docs/pod-spelling-t and tests/docs/pod-t + have been updated to the versions from rra-c-util. Projects that were + previously copying those tests should be able to continue to use them, + but will now need the modules in tests/tap/perl (the canonical version + of which are maintained in rra-c-util). + +C TAP Harness 4.2 (2017-12-30) + + Add is_blob function to the C test library. This tests whether two + regions of memory are identical, similar to ok(memcmp(...)) but + reporting where the regions differ. Patch from Daniel Collins. + + Avoid zero-length realloc allocations in breallocarray. + + Fix new fall-through warning in GCC 7. + + Switch to the compiler warning Autoconf macros and warning set from + rra-c-util with the addition of -ansi -pedantic for GCC and + -pedantic-errors for Clang. Add some casts to fix warnings from + -Wconversion, and suppress some spurious warnings from Clang about + tests/tap/float.c. + +C TAP Harness 4.1 (2016-12-23) + + Add is_bool function to the C test library. This compares its two + arguments only for their truthfulness. is_bool(true, arg) is the same + as ok(arg), but there are times (such as when testing for a false + value) where this allows for clearer code or clearer output. + +C TAP Harness 4.0 (2016-05-07) + + When building runtests, one must now set the C_TAP_SOURCE and + C_TAP_BUILD C preprocessor symbols to the source and build + directories, instead of SOURCE and BUILD. An updated Makefile.am + recipe is documented in README. + + runtests now sets C_TAP_SOURCE and C_TAP_BUILD in the environment in + addition to SOURCE and BUILD. All test programs using this harness + should switch to the new C_TAP_SOURCE and C_TAP_BUILD environment + variables. SOURCE and BUILD are very generic and may conflict with + other programs and uses, and setting them will be removed in a later + version. + + The TAP test libraries (C and shell) now use C_TAP_SOURCE and + C_TAP_BUILD environment variables instead of SOURCE and BUILD for the + test_file_path() and test_tmpdir() functions. If you were using these + libraries with another test harness, you will need to set the new + environment variables. + + Fix missing va_end() call in is_double(), which would have caused + compilation failures or other problems on some platforms. Thanks, + Julien ÉLIE. + + Rename the script to bootstrap from a Git checkout to bootstrap, + matching the emerging consensus in the Autoconf world. + +C TAP Harness 3.4 (2015-08-18) + + Fix segfault in runtests when given a test list containing only + comments and blank lines. Thanks, aherbert. + +C TAP Harness 3.3 (2015-04-26) + + If runtests is given the -v option, or if the environment variable + C_TAP_VERBOSE is set, the complete output of each test program will be + shown instead of the summary of total and failing tests. Based on + work by D. Brashear. + + C TAP Harness now compiles cleanly with Clang with -Weverything + -Wno-padded -pedantic-errors, and automatically detects Clang and + switches warning flags for make warnings. + +C TAP Harness 3.2 (2014-12-25) + + The runtests harness now supports ignoring comments and blank lines in + the test list specified with -l. Leading whitespace before the test + name is also ignored. + +C TAP Harness 3.1 (2014-07-02) + + ok, okv, and all is_* functions now return true if the test succeeds + and false if it fails, matching the return status of the corresponding + Perl Test::More functions. This allows more succinct code when the + actions of a test program should vary based on the success or failure + of previous tests. Based on a patch by Peter Pöschl. + + diag and sysdiag now always return 1, making it easier to insert calls + into compound statements when debugging tests. Based on a patch by + Peter Pöschl. + + Add new breallocarray API that does the same as realloc but takes + calloc-style arguments to specify the size and checks internally for + integer overflow. Inspired by the OpenBSD reallocarray function. + + Check for integer overflows on memory allocation. All the possible + issues for this code are rather theoretical, but one may as well + strive for correctness. + + Replace all uses of sprintf with a simpler string concatenation + function that checks for allocation overflow. (The standards + assumptions for this package don't permit assuming asprintf or a + sufficiently non-broken snprintf to simulate asprintf.) + +C TAP Harness 3.0 (2014-01-28) + + The test_cleanup_register API has changed in this release. Cleanup + functions must now take two parameters, not one, and are called from + all test processes, not just the primary one. The new second argument + indicates whether the cleanup function was called in the primary + process (the one in which plan or plan_lazy was called). External + resources, such as files, should generally only be freed when the + cleanup function is called in the primary process, but tests may want + to free internal resources, like memory, in all processes to ease + analysis with tools like valgrind. + + When running test programs from a list, reopen standard input for each + program to /dev/null, and be more careful about closing all duplicates + of file descriptors left behind after setting up standard output and + standard error so that extraneous file descriptors aren't leaked to + the child process. + +C TAP Harness 2.4 (2013-12-25) + + Add new diag_file_add and diag_file_remove APIs to the basic C TAP + library. These functions manage a list of registered file that + contains supplemental diagnostic information. Each registered file is + checked before each output function for any new lines, and any lines + are displayed as if they'd been passed to diag(). This can be useful + if, for example, the test involves a background daemon whose output + can be logged to a disk file. + +C TAP Harness 2.3 (2013-11-13) + + Add new test_cleanup_register API to the basic C TAP library. This + registers a C callback function that's called during exit from the + test and passed in a boolean argument indicating whether the test + succeeded or failed. + + Suppress lazy plans and the summary of tests at the end of a test + program if the program aborted with bail or sysbail. + + Add warn_unused_result gcc attributes to the C TAP library functions + where ignoring the return value is almost certainly a bug (such as all + the malloc functions). + + Add portability for warn_unsed_result attributes to tap/macros.h. + +C TAP Harness 2.2 (2013-08-14) + + bail and sysbail now exit with status 255 to match the behavior of + BAIL_OUT in Perl's Test::More. + + Document that runtests handling of test cases that bail out differs + from the documented behavior of BAIL_OUT in Perl's Test::More and the + behavior of prove, and document why. + +C TAP Harness 2.1 (2013-03-15) + + When locating test programs, try a suffix (-t, .t, or no suffix) with + all bases before moving on to the next suffix. The behavior in the + previous release was not backward-compatible: it would find the + unsuffixed helper program in the build directory instead of the actual + test in the source directory for some rra-c-util tests when the build + directory and the source directory weren't the same. + + Document that TAP version directives in the TAP output are ignored. + +C TAP Harness 2.0 (2013-03-14) + + The default behavior of tests/runtests has changed to make it act more + like other test drivers. Now, to provide a file containing a list of + tests, use the -l option. Existing users should add -l to the command + line in front of the test list. Otherwise, the command-line arguments + are taken as tests to run, as if they were all listed in a test list + file. + + runtests now tries the test name without any extension if the test + name with either -t or .t was not found. It also does not consider a + directory to be a valid test when searching for the executable, even + if the directory is executable. + + Flush the harness output after each test. This effectively implements + line buffering even when standard output is not a terminal and + therefore output isn't flushed after each line of test output. + + When displaying partial status of tests with a deferred plan, show the + total number of tests as ? rather than the number of tests seen so + far. This matches the behavior of Perl's prove utility. + + More correctly handle backspacing over the test count in several abort + cases when reporting status to a terminal. + + Add GCC annotations to some internal functions in runtests to help + clang --analyze better understand code correctness. Remove a dead + store caught by clang --analyze. + C TAP Harness 1.12 (2012-05-11) Fix additional uses of local in the shell TAP library for portability @@ -54,7 +324,7 @@ C TAP Harness 1.11 (2012-04-25) Move the is_double C TAP test function into a separate source file. Including this function may require linking with libm on some - platforms, which is undesireable if the package otherwise doesn't use + platforms, which is undesirable if the package otherwise doesn't use math functions. The new tests/tap/float.c and tests/tap/float.h files need only be included in a package that wants to do floating point tests. Users of is_double will now need to include tests/tap/float.h diff --git a/src/external/c-tap-harness/README b/src/external/c-tap-harness/README index ddcfc91..3a6bb55 100644 --- a/src/external/c-tap-harness/README +++ b/src/external/c-tap-harness/README @@ -1,12 +1,12 @@ - C TAP Harness 1.12 + C TAP Harness 4.7 (C harness for running TAP-compliant tests) + Maintained by Russ Allbery - Written by Russ Allbery - - Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011, 2012 - Russ Allbery . This software is distributed under a - BSD-style license. Please see the file LICENSE in the distribution for - more information. + Copyright 2000-2001, 2004, 2006-2020 Russ Allbery . + Copyright 2006-2009, 2011-2013 The Board of Trustees of the Leland + Stanford Junior University. This software is distributed under a + BSD-style license. Please see the section LICENSE below for more + information. BLURB @@ -28,7 +28,7 @@ DESCRIPTION merges all the various versions into a single code base that all my packages can pull from. - C TAP Harness provides a full TAP specification (apart from a few + C TAP Harness provides a full TAP specification driver (apart from a few possible edge cases) and has additional special features for supporting builds outside the source directory. It's mostly useful for packages using Autoconf and Automake and because it doesn't assume or require @@ -44,7 +44,11 @@ DESCRIPTION Also included in this package are C and shell libraries that provide utility functions for writing test scripts that use TAP to report - exists. + results. The C library also provides a variety of utility functions + useful for test programs running as part of an Automake-built package: + finding test data files, creating temporary files, reporting output from + external programs running in the background, and similar common + problems. REQUIREMENTS @@ -55,50 +59,68 @@ REQUIREMENTS Bourne-compatible shell. Outside of the test suite, C TAP Harness has no other prerequisites or requirements. - To run the test suite, you will need Perl plus the Perl modules - Test::More and Test::Pod. Test::More comes with Perl 5.8 and later. - Test::Pod is available from CPAN and currently must be installed - separately, but the POD tests will be skipped without interfering with - the rest of the tests if it's not installed. - - To check spelling in the POD documentation, Pod::Spell (available from - CPAN) and either aspell or ispell with the american dictionary are also - required. The user's path is searched for aspell or ispell and aspell - is preferred. Spelling tests are disabled by default since spelling - dictionaries differ too much between systems. To enable those tests, - set RRA_MAINTAINER_TESTS to a true value. + C TAP Harness can also be built with a C++ compiler and should be + similarly portable to any recent C++ compiler, although it is only + tested with g++. To bootstrap from a Git checkout, or if you change the Automake files and need to regenerate Makefile.in, you will need Automake 1.11 or later. For bootstrap or if you change configure.ac or any of the m4 files it includes and need to regenerate configure or config.h.in, you - will need Autoconf 2.64 or later. Perl is also required to generate the - manual page from a fresh Git checkout. + will need Autoconf 2.64 or later. Perl is also required to generate + manual pages from a fresh Git checkout. -BUILDING AND TESTING +BUILDING - You can build C TAP Harness and run its internal test suite with: + You can build C TAP Harness with the standard commands: ./configure make - make check - While there is a configure script, it exists just to drive the build - system and do some path substitution and isn't doing portability - probes. Pass --enable-silent-rules to configure for a quieter build - (similar to the Linux kernel). + If you are building from a Git clone, first run ./bootstrap in the + source directory to generate the build files. Building outside of the + source directory is also supported, if you wish, by creating an empty + directory and then running configure with the correct relative path. + + Pass --enable-silent-rules to configure for a quieter build (similar to + the Linux kernel). Use make warnings instead of make to build with full + compiler warnings (requires either GCC or Clang and may require a + relatively current version of the compiler). + + Installing C TAP Harness is not normally done. Instead, see the section + on using the harness below. + +TESTING - Use make warnings instead of make to build with full GCC compiler - warnings (requires a relatively current version of GCC). + C TAP Harness comes with a comprehensive test suite, which you can run + after building with: + + make check If a test fails, you can run a single test with verbose output via: - ./runtests -b `pwd`/tests -s `pwd`/tests -o + tests/runtests -b $(pwd)/tests -s $(pwd)/tests -o Do this instead of running the test program directly since it will ensure that necessary environment variables are set up. You may need to - change the -s option if you build with a separate build directory from - the source directory. + change the -s option argument if you build with a separate build + directory from the source directory. + + To run the test suite, you will need Perl 5.8 or later. The following + additional Perl modules will be used by the test suite if present: + + * Test::Pod + * Test::Spelling + + All are available on CPAN. Those tests will be skipped if the modules + are not available. + + To enable tests that don't detect functionality problems but are used to + sanity-check the release, set the environment variable RELEASE_TESTING + to a true value. To enable tests that may be sensitive to the local + environment or that produce a lot of false positives without uncovering + many problems, set the environment variable AUTHOR_TESTING to a true + value. USING THE HARNESS @@ -123,8 +145,8 @@ USING THE HARNESS library: check_PROGRAMS = tests/runtests - tests_runtests_CPPFLAGS = -DSOURCE='"$(abs_top_srcdir)/tests"' \ - -DBUILD='"$(abs_top_builddir)/tests"' + tests_runtests_CPPFLAGS = -DC_TAP_SOURCE='"$(abs_top_srcdir)/tests"' \ + -DC_TAP_BUILD='"$(abs_top_builddir)/tests"' check_LIBRARIES = tests/tap/libtap.a tests_tap_libtap_a_CPPFLAGS = -I$(abs_top_srcdir)/tests tests_tap_libtap_a_SOURCES = tests/tap/basic.c tests/tap/basic.h \ @@ -132,8 +154,8 @@ USING THE HARNESS Omit float.c and float.h from the last line if your package doesn't need the is_double function. Building the build and source - directories into runtests will let tests/runtests -o to work - for users without requiring that they set any other variables, even if + directories into runtests will let tests/runtests -o work for + users without requiring that they set any other variables, even if they're doing an out-of-source build. Add additional source files and headers that should go into the TAP @@ -142,16 +164,18 @@ USING THE HARNESS * Add code to Makefile.am to run the test suite: check-local: $(check_PROGRAMS) - cd tests && ./runtests $(abs_top_srcdir)/tests/TESTS + cd tests && ./runtests -l $(abs_top_srcdir)/tests/TESTS + + See the Makefile.am in this package for an example. + + * List the test programs in the tests/TESTS file. This should have the + name of the test executable with the trailing "-t" or ".t" (you can + use either extension as you prefer) omitted. - See the Makefile.am in this package for an example (although note that - it keeps runtests in an unusual location). + Test programs must be executable. - * List the test programs in the TESTS file. This should have the name - of the test executable with the trailing "-t" or ".t" (you can use - either extension as you prefer) omitted. For any test programs that - need to be compiled, add build rules for them in Makefile.am, simliar - to: + For any test programs that need to be compiled, add build rules for + them in Makefile.am, similar to: tests_libtap_c_basic_LDADD = tests/tap/libtap.a @@ -173,7 +197,7 @@ USING THE HARNESS the tap subdirectory of your tests directory and add it to EXTRA_DIST. Shell programs should start with: - . "${SOURCE}/tap/libtap.sh" + . "${C_TAP_SOURCE}/tap/libtap.sh" and can then use the functions defined in the library. @@ -186,43 +210,93 @@ USING THE HARNESS If you have data files that your test cases use, conventionally they go into tests/data. You can then find the data directory relative to the - SOURCE environment variable (set by runtests) in your test program. If - you have data that's compiled or generated by Autoconf, it will be - relative to the BUILD environment variable. Don't forget to add test - data to EXTRA_DIST as necessary. + C_TAP_SOURCE environment variable (set by runtests) in your test + program. If you have data that's compiled or generated by Autoconf, it + will be relative to the BUILD environment variable. Don't forget to add + test data to EXTRA_DIST as necessary. For more TAP library add-ons, generally ones that rely on additional portability code not shipped in this package or with narrower uses, see - the rra-c-util package: + the rra-c-util package [1]. There are several additional TAP library + add-ons in the tests/tap directory in that package. It's also an + example of how to use this test harness in another package. - http://www.eyrie.org/~eagle/software/rra-c-util/ + [1] https://www.eyrie.org/~eagle/software/rra-c-util/ - There are several additional TAP library add-ons in the tests/tap - directory in that package. It's also an example of how to use this test - harness in another package. - -HOMEPAGE AND SOURCE REPOSITORY +SUPPORT The C TAP Harness web page at: - http://www.eyrie.org/~eagle/software/c-tap-harness/ + https://www.eyrie.org/~eagle/software/c-tap-harness/ will always have the current version of this package, the current documentation, and pointers to any additional resources. - C TAP Harness is maintained using Git. You can access the current - source by cloning the repository at: + For bug tracking, use the issue tracker on GitHub: - git://git.eyrie.org/devel/c-tap-harness.git + https://github.com/rra/c-tap-harness/issues - or view the repository via the web at: + However, please be aware that I tend to be extremely busy and work + projects often take priority. I'll save your report and get to it as + soon as I can, but it may take me a couple of months. - http://git.eyrie.org/?p=devel/c-tap-harness.git +SOURCE REPOSITORY - C TAP Harness is also available via github at: + C TAP Harness is maintained using Git. You can access the current + source on GitHub at: + + https://github.com/rra/c-tap-harness + + or by cloning the repository at: - http://github.com/rra/c-tap-harness + https://git.eyrie.org/git/devel/c-tap-harness.git + + or view the repository via the web at: - and the github wiki and issue tracker are available on an experimental - basis. If you like using the github facilities, try filing issues or - adding supplemental documentation there. + https://git.eyrie.org/?p=devel/c-tap-harness.git + + The eyrie.org repository is the canonical one, maintained by the author, + but using GitHub is probably more convenient for most purposes. Pull + requests are gratefully reviewed and normally accepted. + +LICENSE + + The C TAP Harness package as a whole is covered by the following + copyright statement and license: + + Copyright 2000-2001, 2004, 2006-2020 Russ Allbery + Copyright 2006-2009, 2011-2013 + The Board of Trustees of the Leland Stanford Junior University + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Some files in this distribution are individually released under + different licenses, all of which are compatible with the above general + package license but which may require preservation of additional + notices. All required notices, and detailed information about the + licensing of each file, are recorded in the LICENSE file. + + Files covered by a license with an assigned SPDX License Identifier + include SPDX-License-Identifier tags to enable automated processing of + license information. See https://spdx.org/licenses/ for more + information. + + For any copyright range specified by files in this package as YYYY-ZZZZ, + the range specifies every single year in that closed interval. diff --git a/src/external/c-tap-harness/tests/runtests.c b/src/external/c-tap-harness/tests/runtests.c index 4249875..1050120 100644 --- a/src/external/c-tap-harness/tests/runtests.c +++ b/src/external/c-tap-harness/tests/runtests.c @@ -1,17 +1,53 @@ /* * Run a set of tests, reporting results. * + * Test suite driver that runs a set of tests implementing a subset of the + * Test Anything Protocol (TAP) and reports the results. + * + * Any bug reports, bug fixes, and improvements are very much welcome and + * should be sent to the e-mail address below. This program is part of C TAP + * Harness . + * + * Copyright 2000-2001, 2004, 2006-2019 Russ Allbery + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT + */ + +/* * Usage: * - * runtests [-b ] [-s ] - * runtests -o [-b ] [-s ] + * runtests [-hv] [-b ] [-s ] -l + * runtests [-hv] [-b ] [-s ] [ ...] + * runtests -o [-h] [-b ] [-s ] * * In the first case, expects a list of executables located in the given file, - * one line per executable. For each one, runs it as part of a test suite, - * reporting results. Test output should start with a line containing the - * number of tests (numbered from 1 to this number), optionally preceded by - * "1..", although that line may be given anywhere in the output. Each - * additional line should be in the following format: + * one line per executable, possibly followed by a space-separated list of + * options. For each one, runs it as part of a test suite, reporting results. + * In the second case, use the same infrastructure, but run only the tests + * listed on the command line. + * + * Test output should start with a line containing the number of tests + * (numbered from 1 to this number), optionally preceded by "1..", although + * that line may be given anywhere in the output. Each additional line should + * be in the following format: * * ok * not ok @@ -44,49 +80,30 @@ * output. This is intended for use with failing tests so that the person * running the test suite can get more details about what failed. * - * If built with the C preprocessor symbols SOURCE and BUILD defined, C TAP - * Harness will export those values in the environment so that tests can find - * the source and build directory and will look for tests under both - * directories. These paths can also be set with the -b and -s command-line - * options, which will override anything set at build time. + * If built with the C preprocessor symbols C_TAP_SOURCE and C_TAP_BUILD + * defined, C TAP Harness will export those values in the environment so that + * tests can find the source and build directory and will look for tests under + * both directories. These paths can also be set with the -b and -s + * command-line options, which will override anything set at build time. * - * Any bug reports, bug fixes, and improvements are very much welcome and - * should be sent to the e-mail address below. This program is part of C TAP - * Harness . - * - * Copyright 2000, 2001, 2004, 2006, 2007, 2008, 2009, 2010, 2011 - * Russ Allbery - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. -*/ + * If the -v option is given, or the C_TAP_VERBOSE environment variable is set, + * display the full output of each test as it runs rather than showing a + * summary of the results of each test. + */ /* Required for fdopen(), getopt(), and putenv(). */ #if defined(__STRICT_ANSI__) || defined(PEDANTIC) -# ifndef _XOPEN_SOURCE -# define _XOPEN_SOURCE 500 -# endif +# ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 500 +# endif #endif #include #include #include +#include #include +#include #include #include #include @@ -101,63 +118,81 @@ /* sys/time.h must be included before sys/resource.h on some platforms. */ #include -/* AIX doesn't have WCOREDUMP. */ +/* AIX 6.1 (and possibly later) doesn't have WCOREDUMP. */ #ifndef WCOREDUMP -# define WCOREDUMP(status) ((unsigned)(status) & 0x80) +# define WCOREDUMP(status) ((unsigned) (status) &0x80) +#endif + +/* + * POSIX requires that these be defined in , but they're not always + * available. If one of them has been defined, all the rest almost certainly + * have. + */ +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +# define STDOUT_FILENO 1 +# define STDERR_FILENO 2 #endif /* + * Used for iterating through arrays. Returns the number of elements in the + * array (useful for a < upper bound in a for loop). + */ +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) + +/* * The source and build versions of the tests directory. This is used to set - * the SOURCE and BUILD environment variables and find test programs, if set. - * Normally, this should be set as part of the build process to the test - * subdirectories of $(abs_top_srcdir) and $(abs_top_builddir) respectively. + * the C_TAP_SOURCE and C_TAP_BUILD environment variables (and the SOURCE and + * BUILD environment variables set for backward compatibility) and find test + * programs, if set. Normally, this should be set as part of the build + * process to the test subdirectories of $(abs_top_srcdir) and + * $(abs_top_builddir) respectively. */ -#ifndef SOURCE -# define SOURCE NULL +#ifndef C_TAP_SOURCE +# define C_TAP_SOURCE NULL #endif -#ifndef BUILD -# define BUILD NULL +#ifndef C_TAP_BUILD +# define C_TAP_BUILD NULL #endif /* Test status codes. */ -enum test_status { - TEST_FAIL, - TEST_PASS, - TEST_SKIP, - TEST_INVALID -}; +enum test_status { TEST_FAIL, TEST_PASS, TEST_SKIP, TEST_INVALID }; + +/* Really, just a boolean, but this is more self-documenting. */ +enum test_verbose { CONCISE = 0, VERBOSE = 1 }; /* Indicates the state of our plan. */ enum plan_status { - PLAN_INIT, /* Nothing seen yet. */ - PLAN_FIRST, /* Plan seen before any tests. */ - PLAN_PENDING, /* Test seen and no plan yet. */ - PLAN_FINAL /* Plan seen after some tests. */ + PLAN_INIT, /* Nothing seen yet. */ + PLAN_FIRST, /* Plan seen before any tests. */ + PLAN_PENDING, /* Test seen and no plan yet. */ + PLAN_FINAL /* Plan seen after some tests. */ }; /* Error exit statuses for test processes. */ -#define CHILDERR_DUP 100 /* Couldn't redirect stderr or stdout. */ -#define CHILDERR_EXEC 101 /* Couldn't exec child process. */ -#define CHILDERR_STDERR 102 /* Couldn't open stderr file. */ +#define CHILDERR_DUP 100 /* Couldn't redirect stderr or stdout. */ +#define CHILDERR_EXEC 101 /* Couldn't exec child process. */ +#define CHILDERR_STDIN 102 /* Couldn't open stdin file. */ +#define CHILDERR_STDERR 103 /* Couldn't open stderr file. */ /* Structure to hold data for a set of tests. */ struct testset { - char *file; /* The file name of the test. */ - char *path; /* The path to the test program. */ - enum plan_status plan; /* The status of our plan. */ - unsigned long count; /* Expected count of tests. */ - unsigned long current; /* The last seen test number. */ - unsigned int length; /* The length of the last status message. */ - unsigned long passed; /* Count of passing tests. */ - unsigned long failed; /* Count of failing lists. */ - unsigned long skipped; /* Count of skipped tests (passed). */ - unsigned long allocated; /* The size of the results table. */ - enum test_status *results; /* Table of results by test number. */ - unsigned int aborted; /* Whether the set as aborted. */ - int reported; /* Whether the results were reported. */ - int status; /* The exit status of the test. */ - unsigned int all_skipped; /* Whether all tests were skipped. */ - char *reason; /* Why all tests were skipped. */ + char *file; /* The file name of the test. */ + char **command; /* The argv vector to run the command. */ + enum plan_status plan; /* The status of our plan. */ + unsigned long count; /* Expected count of tests. */ + unsigned long current; /* The last seen test number. */ + unsigned int length; /* The length of the last status message. */ + unsigned long passed; /* Count of passing tests. */ + unsigned long failed; /* Count of failing lists. */ + unsigned long skipped; /* Count of skipped tests (passed). */ + unsigned long allocated; /* The size of the results table. */ + enum test_status *results; /* Table of results by test number. */ + unsigned int aborted; /* Whether the set was aborted. */ + unsigned int reported; /* Whether the results were reported. */ + int status; /* The exit status of the test. */ + unsigned int all_skipped; /* Whether all tests were skipped. */ + char *reason; /* Why all tests were skipped. */ }; /* Structure to hold a linked list of test sets. */ @@ -167,21 +202,27 @@ struct testlist { }; /* - * Usage message. Should be used as a printf format with two arguments: the - * path to runtests, given twice. + * Usage message. Should be used as a printf format with four arguments: the + * path to runtests, given three times, and the usage_description. This is + * split into variables to satisfy the pedantic ISO C90 limit on strings. */ static const char usage_message[] = "\ -Usage: %s [-b ] [-s ] \n\ - %s -o [-b ] [-s ] \n\ +Usage: %s [-hv] [-b ] [-s ] ...\n\ + %s [-hv] [-b ] [-s ] -l \n\ + %s -o [-h] [-b ] [-s ] \n\ \n\ Options:\n\ -b Set the build directory to \n\ +%s"; +static const char usage_extra[] = "\ + -l Take the list of tests to run from \n\ -o Run a single test rather than a list of tests\n\ -s Set the source directory to \n\ + -v Show the full output of each test\n\ \n\ -runtests normally runs each test listed in a file whose path is given as\n\ -its command-line argument. With the -o option, it instead runs a single\n\ -test and shows its complete output.\n"; +runtests normally runs each test listed on the command line. With the -l\n\ +option, it instead runs every test listed in a file. With the -o option,\n\ +it instead runs a single test and shows its complete output.\n"; /* * Header used for test output. %s is replaced by the file name of the list @@ -197,9 +238,83 @@ Failed Set Fail/Total (%) Skip Stat Failing Tests\n\ -------------------------- -------------- ---- ---- ------------------------"; /* Include the file name and line number in malloc failures. */ -#define xmalloc(size) x_malloc((size), __FILE__, __LINE__) -#define xrealloc(p, size) x_realloc((p), (size), __FILE__, __LINE__) +#define xcalloc(n, type) \ + ((type *) x_calloc((n), sizeof(type), __FILE__, __LINE__)) +#define xmalloc(size) ((char *) x_malloc((size), __FILE__, __LINE__)) #define xstrdup(p) x_strdup((p), __FILE__, __LINE__) +#define xstrndup(p, size) x_strndup((p), (size), __FILE__, __LINE__) +#define xreallocarray(p, n, type) \ + ((type *) x_reallocarray((p), (n), sizeof(type), __FILE__, __LINE__)) + +/* + * __attribute__ is available in gcc 2.5 and later, but only with gcc 2.7 + * could you use the __format__ form of the attributes, which is what we use + * (to avoid confusion with other macros). + */ +#ifndef __attribute__ +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 7) +# define __attribute__(spec) /* empty */ +# endif +#endif + +/* + * We use __alloc_size__, but it was only available in fairly recent versions + * of GCC. Suppress warnings about the unknown attribute if GCC is too old. + * We know that we're GCC at this point, so we can use the GCC variadic macro + * extension, which will still work with versions of GCC too old to have C99 + * variadic macro support. + */ +#if !defined(__attribute__) && !defined(__alloc_size__) +# if defined(__GNUC__) && !defined(__clang__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) +# define __alloc_size__(spec, args...) /* empty */ +# endif +# endif +#endif + +/* + * LLVM and Clang pretend to be GCC but don't support all of the __attribute__ + * settings that GCC does. For them, suppress warnings about unknown + * attributes on declarations. This unfortunately will affect the entire + * compilation context, but there's no push and pop available. + */ +#if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__)) +# pragma GCC diagnostic ignored "-Wattributes" +#endif + +/* Declare internal functions that benefit from compiler attributes. */ +static void die(const char *, ...) + __attribute__((__nonnull__, __noreturn__, __format__(printf, 1, 2))); +static void sysdie(const char *, ...) + __attribute__((__nonnull__, __noreturn__, __format__(printf, 1, 2))); +static void *x_calloc(size_t, size_t, const char *, int) + __attribute__((__alloc_size__(1, 2), __malloc__, __nonnull__)); +static void *x_malloc(size_t, const char *, int) + __attribute__((__alloc_size__(1), __malloc__, __nonnull__)); +static void *x_reallocarray(void *, size_t, size_t, const char *, int) + __attribute__((__alloc_size__(2, 3), __malloc__, __nonnull__(4))); +static char *x_strdup(const char *, const char *, int) + __attribute__((__malloc__, __nonnull__)); +static char *x_strndup(const char *, size_t, const char *, int) + __attribute__((__malloc__, __nonnull__)); + + +/* + * Report a fatal error and exit. + */ +static void +die(const char *format, ...) +{ + va_list args; + + fflush(stdout); + fprintf(stderr, "runtests: "); + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + fprintf(stderr, "\n"); + exit(1); +} /* @@ -223,6 +338,24 @@ sysdie(const char *format, ...) /* + * Allocate zeroed memory, reporting a fatal error and exiting on failure. + */ +static void * +x_calloc(size_t n, size_t size, const char *file, int line) +{ + void *p; + + n = (n > 0) ? n : 1; + size = (size > 0) ? size : 1; + p = calloc(n, size); + if (p == NULL) + sysdie("failed to calloc %lu bytes at %s line %d", + (unsigned long) size, file, line); + return p; +} + + +/* * Allocate memory, reporting a fatal error and exiting on failure. */ static void * @@ -240,14 +373,29 @@ x_malloc(size_t size, const char *file, int line) /* * Reallocate memory, reporting a fatal error and exiting on failure. + * + * We should technically use SIZE_MAX here for the overflow check, but + * SIZE_MAX is C99 and we're only assuming C89 + SUSv3, which does not + * guarantee that it exists. They do guarantee that UINT_MAX exists, and we + * can assume that UINT_MAX <= SIZE_MAX. And we should not be allocating + * anything anywhere near that large. + * + * (In theory, C89 and C99 permit size_t to be smaller than unsigned int, but + * I disbelieve in the existence of such systems and they will have to cope + * without overflow checks.) */ static void * -x_realloc(void *p, size_t size, const char *file, int line) +x_reallocarray(void *p, size_t n, size_t size, const char *file, int line) { - p = realloc(p, size); + n = (n > 0) ? n : 1; + size = (size > 0) ? size : 1; + + if (n > 0 && UINT_MAX / n <= size) + sysdie("realloc too large at %s line %d", file, line); + p = realloc(p, n * size); if (p == NULL) sysdie("failed to realloc %lu bytes at %s line %d", - (unsigned long) size, file, line); + (unsigned long) (n * size), file, line); return p; } @@ -262,23 +410,101 @@ x_strdup(const char *s, const char *file, int line) size_t len; len = strlen(s) + 1; - p = malloc(len); + p = (char *) malloc(len); if (p == NULL) - sysdie("failed to strdup %lu bytes at %s line %d", - (unsigned long) len, file, line); + sysdie("failed to strdup %lu bytes at %s line %d", (unsigned long) len, + file, line); memcpy(p, s, len); return p; } /* + * Copy the first n characters of a string, reporting a fatal error and + * existing on failure. + * + * Avoid using the system strndup function since it may not exist (on Mac OS + * X, for example), and there's no need to introduce another portability + * requirement. + */ +char * +x_strndup(const char *s, size_t size, const char *file, int line) +{ + const char *p; + size_t len; + char *copy; + + /* Don't assume that the source string is nul-terminated. */ + for (p = s; (size_t)(p - s) < size && *p != '\0'; p++) + ; + len = (size_t)(p - s); + copy = (char *) malloc(len + 1); + if (copy == NULL) + sysdie("failed to strndup %lu bytes at %s line %d", + (unsigned long) len, file, line); + memcpy(copy, s, len); + copy[len] = '\0'; + return copy; +} + + +/* + * Form a new string by concatenating multiple strings. The arguments must be + * terminated by (const char *) 0. + * + * This function only exists because we can't assume asprintf. We can't + * simulate asprintf with snprintf because we're only assuming SUSv3, which + * does not require that snprintf with a NULL buffer return the required + * length. When those constraints are relaxed, this should be ripped out and + * replaced with asprintf or a more trivial replacement with snprintf. + */ +static char * +concat(const char *first, ...) +{ + va_list args; + char *result; + const char *string; + size_t offset; + size_t length = 0; + + /* + * Find the total memory required. Ensure we don't overflow length. We + * aren't guaranteed to have SIZE_MAX, so use UINT_MAX as an acceptable + * substitute (see the x_nrealloc comments). + */ + va_start(args, first); + for (string = first; string != NULL; string = va_arg(args, const char *)) { + if (length >= UINT_MAX - strlen(string)) { + errno = EINVAL; + sysdie("strings too long in concat"); + } + length += strlen(string); + } + va_end(args); + length++; + + /* Create the string. */ + result = xmalloc(length); + va_start(args, first); + offset = 0; + for (string = first; string != NULL; string = va_arg(args, const char *)) { + memcpy(result + offset, string, strlen(string)); + offset += strlen(string); + } + va_end(args); + result[offset] = '\0'; + return result; +} + + +/* * Given a struct timeval, return the number of seconds it represents as a * double. Use difftime() to convert a time_t to a double. */ static double tv_seconds(const struct timeval *tv) { - return difftime(tv->tv_sec, 0) + tv->tv_usec * 1e-6; + return difftime(tv->tv_sec, 0) + (double) tv->tv_usec * 1e-6; } @@ -309,7 +535,20 @@ tv_sum(const struct timeval *tv1, const struct timeval *tv2) static const char * skip_whitespace(const char *p) { - while (isspace((unsigned char)(*p))) + while (isspace((unsigned char) (*p))) + p++; + return p; +} + + +/* + * Given a pointer to a string, skip any non-whitespace characters and return + * a pointer to the first whitespace character, or to the end of the string. + */ +static const char * +skip_non_whitespace(const char *p) +{ + while (*p != '\0' && !isspace((unsigned char) (*p))) p++; return p; } @@ -321,38 +560,65 @@ skip_whitespace(const char *p) * argument. Returns the PID of the new process. Errors are fatal. */ static pid_t -test_start(const char *path, int *fd) +test_start(char *const *command, int *fd) { - int fds[2], errfd; + int fds[2], infd, errfd; pid_t child; + /* Create a pipe used to capture the output from the test program. */ if (pipe(fds) == -1) { puts("ABORTED"); fflush(stdout); sysdie("can't create pipe"); } + + /* Fork a child process, massage the file descriptors, and exec. */ child = fork(); - if (child == (pid_t) -1) { + switch (child) { + case -1: puts("ABORTED"); fflush(stdout); sysdie("can't fork"); - } else if (child == 0) { - /* In child. Set up our stdout and stderr. */ + + /* In the child. Set up our standard output. */ + case 0: + close(fds[0]); + close(STDOUT_FILENO); + if (dup2(fds[1], STDOUT_FILENO) < 0) + _exit(CHILDERR_DUP); + close(fds[1]); + + /* Point standard input at /dev/null. */ + close(STDIN_FILENO); + infd = open("/dev/null", O_RDONLY); + if (infd < 0) + _exit(CHILDERR_STDIN); + if (infd != STDIN_FILENO) { + if (dup2(infd, STDIN_FILENO) < 0) + _exit(CHILDERR_DUP); + close(infd); + } + + /* Point standard error at /dev/null. */ + close(STDERR_FILENO); errfd = open("/dev/null", O_WRONLY); if (errfd < 0) _exit(CHILDERR_STDERR); - if (dup2(errfd, 2) == -1) - _exit(CHILDERR_DUP); - close(fds[0]); - if (dup2(fds[1], 1) == -1) - _exit(CHILDERR_DUP); + if (errfd != STDERR_FILENO) { + if (dup2(errfd, STDERR_FILENO) < 0) + _exit(CHILDERR_DUP); + close(errfd); + } /* Now, exec our process. */ - if (execl(path, path, (char *) 0) == -1) + if (execv(command[0], command) == -1) _exit(CHILDERR_EXEC); - } else { - /* In parent. Close the extra file descriptor. */ + break; + + /* In parent. Close the extra file descriptor. */ + default: close(fds[1]); + break; } *fd = fds[0]; return child; @@ -380,15 +646,63 @@ test_backspace(struct testset *ts) /* + * Allocate or resize the array of test results to be large enough to contain + * the test number in. + */ +static void +resize_results(struct testset *ts, unsigned long n) +{ + unsigned long i; + size_t s; + + /* If there's already enough space, return quickly. */ + if (n <= ts->allocated) + return; + + /* + * If no space has been allocated, do the initial allocation. Otherwise, + * resize. Start with 32 test cases and then add 1024 with each resize to + * try to reduce the number of reallocations. + */ + if (ts->allocated == 0) { + s = (n > 32) ? n : 32; + ts->results = xcalloc(s, enum test_status); + } else { + s = (n > ts->allocated + 1024) ? n : ts->allocated + 1024; + ts->results = xreallocarray(ts->results, s, enum test_status); + } + + /* Set the results for the newly-allocated test array. */ + for (i = ts->allocated; i < s; i++) + ts->results[i] = TEST_INVALID; + ts->allocated = s; +} + + +/* + * Report an invalid test number and set the appropriate flags. Pulled into a + * separate function since we do this in several places. + */ +static void +invalid_test_number(struct testset *ts, long n, enum test_verbose verbose) +{ + if (!verbose) + test_backspace(ts); + printf("ABORTED (invalid test number %ld)\n", n); + ts->aborted = 1; + ts->reported = 1; +} + + +/* * Read the plan line of test output, which should contain the range of test * numbers. We may initialize the testset structure here if we haven't yet * seen a test. Return true if initialization succeeded and the test should * continue, false otherwise. */ static int -test_plan(const char *line, struct testset *ts) +test_plan(const char *line, struct testset *ts, enum test_verbose verbose) { - unsigned long i; long n; /* @@ -401,12 +715,14 @@ test_plan(const char *line, struct testset *ts) line += 3; /* - * Get the count, check it for validity, and initialize the struct. If we - * have something of the form "1..0 # skip foo", the whole file was + * Get the count and check it for validity. + * + * If we have something of the form "1..0 # skip foo", the whole file was * skipped; record that. If we do skip the whole file, zero out all of - * our statistics, since they're no longer relevant. strtol is called - * with a second argument to advance the line pointer past the count to - * make it simpler to detect the # skip case. + * our statistics, since they're no longer relevant. + * + * strtol is called with a second argument to advance the line pointer + * past the count to make it simpler to detect the # skip case. */ n = strtol(line, (char **) &line, 10); if (n == 0) { @@ -435,29 +751,27 @@ test_plan(const char *line, struct testset *ts) ts->reported = 1; return 0; } - if (ts->plan == PLAN_INIT && ts->allocated == 0) { - ts->count = n; - ts->allocated = n; + + /* + * If we are doing lazy planning, check the plan against the largest test + * number that we saw and fail now if we saw a check outside the plan + * range. + */ + if (ts->plan == PLAN_PENDING && (unsigned long) n < ts->count) { + invalid_test_number(ts, (long) ts->count, verbose); + return 0; + } + + /* + * Otherwise, allocated or resize the results if needed and update count, + * and then record that we've seen a plan. + */ + resize_results(ts, (unsigned long) n); + ts->count = (unsigned long) n; + if (ts->plan == PLAN_INIT) ts->plan = PLAN_FIRST; - ts->results = xmalloc(ts->count * sizeof(enum test_status)); - for (i = 0; i < ts->count; i++) - ts->results[i] = TEST_INVALID; - } else if (ts->plan == PLAN_PENDING) { - if ((unsigned long) n < ts->count) { - printf("ABORTED (invalid test number %lu)\n", ts->count); - ts->aborted = 1; - ts->reported = 1; - return 0; - } - ts->count = n; - if ((unsigned long) n > ts->allocated) { - ts->results = xrealloc(ts->results, n * sizeof(enum test_status)); - for (i = ts->allocated; i < ts->count; i++) - ts->results[i] = TEST_INVALID; - ts->allocated = n; - } + else if (ts->plan == PLAN_PENDING) ts->plan = PLAN_FINAL; - } return 1; } @@ -469,13 +783,13 @@ test_plan(const char *line, struct testset *ts) * reported status. */ static void -test_checkline(const char *line, struct testset *ts) +test_checkline(const char *line, struct testset *ts, enum test_verbose verbose) { enum test_status status = TEST_PASS; const char *bail; char *end; long number; - unsigned long i, current; + unsigned long current; int outlen; /* Before anything, check for a test abort. */ @@ -488,7 +802,8 @@ test_checkline(const char *line, struct testset *ts) length = strlen(bail); if (bail[length - 1] == '\n') length--; - test_backspace(ts); + if (!verbose) + test_backspace(ts); printf("ABORTED (%.*s)\n", (int) length, bail); ts->reported = 1; } @@ -508,14 +823,16 @@ test_checkline(const char *line, struct testset *ts) return; /* If we haven't yet seen a plan, look for one. */ - if (ts->plan == PLAN_INIT && isdigit((unsigned char)(*line))) { - if (!test_plan(line, ts)) + if (ts->plan == PLAN_INIT && isdigit((unsigned char) (*line))) { + if (!test_plan(line, ts, verbose)) return; } else if (strncmp(line, "1..", 3) == 0) { if (ts->plan == PLAN_PENDING) { - if (!test_plan(line, ts)) + if (!test_plan(line, ts, verbose)) return; } else { + if (!verbose) + test_backspace(ts); puts("ABORTED (multiple plans)"); ts->aborted = 1; ts->reported = 1; @@ -534,39 +851,30 @@ test_checkline(const char *line, struct testset *ts) errno = 0; number = strtol(line, &end, 10); if (errno != 0 || end == line) - number = ts->current + 1; - current = number; - if (number <= 0 || (current > ts->count && ts->plan == PLAN_FIRST)) { - test_backspace(ts); - printf("ABORTED (invalid test number %lu)\n", current); - ts->aborted = 1; - ts->reported = 1; + current = ts->current + 1; + else if (number <= 0) { + invalid_test_number(ts, number, verbose); + return; + } else + current = (unsigned long) number; + if (current > ts->count && ts->plan == PLAN_FIRST) { + invalid_test_number(ts, (long) current, verbose); return; } /* We have a valid test result. Tweak the results array if needed. */ if (ts->plan == PLAN_INIT || ts->plan == PLAN_PENDING) { ts->plan = PLAN_PENDING; + resize_results(ts, current); if (current > ts->count) ts->count = current; - if (current > ts->allocated) { - unsigned long n; - - n = (ts->allocated == 0) ? 32 : ts->allocated * 2; - if (n < current) - n = current; - ts->results = xrealloc(ts->results, n * sizeof(enum test_status)); - for (i = ts->allocated; i < n; i++) - ts->results[i] = TEST_INVALID; - ts->allocated = n; - } } /* * Handle directives. We should probably do something more interesting * with unexpected passes of todo tests. */ - while (isdigit((unsigned char)(*line))) + while (isdigit((unsigned char) (*line))) line++; line = skip_whitespace(line); if (*line == '#') { @@ -579,7 +887,8 @@ test_checkline(const char *line, struct testset *ts) /* Make sure that the test number is in range and not a duplicate. */ if (ts->results[current - 1] != TEST_INVALID) { - test_backspace(ts); + if (!verbose) + test_backspace(ts); printf("ABORTED (duplicate test number %lu)\n", current); ts->aborted = 1; ts->reported = 1; @@ -588,17 +897,27 @@ test_checkline(const char *line, struct testset *ts) /* Good results. Increment our various counters. */ switch (status) { - case TEST_PASS: ts->passed++; break; - case TEST_FAIL: ts->failed++; break; - case TEST_SKIP: ts->skipped++; break; - case TEST_INVALID: break; + case TEST_PASS: + ts->passed++; + break; + case TEST_FAIL: + ts->failed++; + break; + case TEST_SKIP: + ts->skipped++; + break; + case TEST_INVALID: + break; } ts->current = current; ts->results[current - 1] = status; - test_backspace(ts); - if (isatty(STDOUT_FILENO)) { - outlen = printf("%lu/%lu", current, ts->count); - ts->length = (outlen >= 0) ? outlen : 0; + if (!verbose && isatty(STDOUT_FILENO)) { + test_backspace(ts); + if (ts->plan == PLAN_PENDING) + outlen = printf("%lu/?", current); + else + outlen = printf("%lu/%lu", current, ts->count); + ts->length = (outlen >= 0) ? (unsigned int) outlen : 0; fflush(stdout); } } @@ -614,7 +933,7 @@ test_checkline(const char *line, struct testset *ts) * disable this). */ static unsigned int -test_print_range(unsigned long first, unsigned long last, unsigned int chars, +test_print_range(unsigned long first, unsigned long last, unsigned long chars, unsigned int limit) { unsigned int needed = 0; @@ -754,6 +1073,7 @@ test_analyze(struct testset *ts) if (!ts->reported) puts("ABORTED (execution failed -- not found?)"); break; + case CHILDERR_STDIN: case CHILDERR_STDERR: if (!ts->reported) puts("ABORTED (can't open /dev/null)"); @@ -783,7 +1103,7 @@ test_analyze(struct testset *ts) * false otherwise. */ static int -test_run(struct testset *ts) +test_run(struct testset *ts, enum test_verbose verbose) { pid_t testpid, child; int outfd, status; @@ -792,7 +1112,7 @@ test_run(struct testset *ts) char buffer[BUFSIZ]; /* Run the test program. */ - testpid = test_start(ts->path, &outfd); + testpid = test_start(ts->command, &outfd); output = fdopen(outfd, "r"); if (!output) { puts("ABORTED"); @@ -800,12 +1120,19 @@ test_run(struct testset *ts) sysdie("fdopen failed"); } - /* Pass each line of output to test_checkline(). */ - while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) - test_checkline(buffer, ts); + /* + * Pass each line of output to test_checkline(), and print the line if + * verbosity is requested. + */ + while (!ts->aborted && fgets(buffer, sizeof(buffer), output)) { + if (verbose) + printf("%s", buffer); + test_checkline(buffer, ts, verbose); + } if (ferror(output) || ts->plan == PLAN_INIT) ts->aborted = 1; - test_backspace(ts); + if (!verbose) + test_backspace(ts); /* * Consume the rest of the test output, close the output descriptor, @@ -813,7 +1140,8 @@ test_run(struct testset *ts) * for eventual output. */ while (fgets(buffer, sizeof(buffer), output)) - ; + if (verbose) + printf("%s", buffer); fclose(output); child = waitpid(testpid, &ts->status, 0); if (child == (pid_t) -1) { @@ -846,6 +1174,7 @@ test_fail_summary(const struct testlist *fails) struct testset *ts; unsigned int chars; unsigned long i, first, last, total; + double failed; puts(header); @@ -854,8 +1183,9 @@ test_fail_summary(const struct testlist *fails) for (; fails; fails = fails->next) { ts = fails->ts; total = ts->count - ts->skipped; + failed = (double) ts->failed; printf("%-26.26s %4lu/%-4lu %3.0f%% %4lu ", ts->file, ts->failed, - total, total ? (ts->failed * 100.0) / total : 0, + total, total ? (failed * 100.0) / (double) total : 0, ts->skipped); if (WIFEXITED(ts->status)) printf("%4d ", WEXITSTATUS(ts->status)); @@ -883,109 +1213,325 @@ test_fail_summary(const struct testlist *fails) if (first != 0) test_print_range(first, last, chars, 19); putchar('\n'); - free(ts->file); - free(ts->path); - free(ts->results); - if (ts->reason != NULL) - free(ts->reason); - free(ts); } } /* + * Check whether a given file path is a valid test. Currently, this checks + * whether it is executable and is a regular file. Returns true or false. + */ +static int +is_valid_test(const char *path) +{ + struct stat st; + + if (access(path, X_OK) < 0) + return 0; + if (stat(path, &st) < 0) + return 0; + if (!S_ISREG(st.st_mode)) + return 0; + return 1; +} + + +/* * Given the name of a test, a pointer to the testset struct, and the source * and build directories, find the test. We try first relative to the current * directory, then in the build directory (if not NULL), then in the source * directory. In each of those directories, we first try a "-t" extension and - * then a ".t" extension. When we find an executable program, we fill in the - * path member of the testset struct. If none of those paths are executable, - * just fill in the name of the test with "-t" appended. + * then a ".t" extension. When we find an executable program, we return the + * path to that program. If none of those paths are executable, just fill in + * the name of the test as is. * * The caller is responsible for freeing the path member of the testset * struct. */ -static void -find_test(const char *name, struct testset *ts, const char *source, - const char *build) +static char * +find_test(const char *name, const char *source, const char *build) { - char *path; - const char *bases[4]; - unsigned int i; + char *path = NULL; + const char *bases[3], *suffix, *base; + unsigned int i, j; + const char *suffixes[3] = {"-t", ".t", ""}; + /* Possible base directories. */ bases[0] = "."; bases[1] = build; bases[2] = source; - bases[3] = NULL; - for (i = 0; i < 3; i++) { - if (bases[i] == NULL) + /* Try each suffix with each base. */ + for (i = 0; i < ARRAY_SIZE(suffixes); i++) { + suffix = suffixes[i]; + for (j = 0; j < ARRAY_SIZE(bases); j++) { + base = bases[j]; + if (base == NULL) + continue; + path = concat(base, "/", name, suffix, (const char *) 0); + if (is_valid_test(path)) + return path; + free(path); + path = NULL; + } + } + if (path == NULL) + path = xstrdup(name); + return path; +} + + +/* + * Parse a single line of a test list and store the test name and command to + * execute it in the given testset struct. + * + * Normally, each line is just the name of the test, which is located in the + * test directory and turned into a command to run. However, each line may + * have whitespace-separated options, which change the command that's run. + * Current supported options are: + * + * valgrind + * Run the test under valgrind if C_TAP_VALGRIND is set. The contents + * of that environment variable are taken as the valgrind command (with + * options) to run. The command is parsed with a simple split on + * whitespace and no quoting is supported. + * + * libtool + * If running under valgrind, use libtool to invoke valgrind. This avoids + * running valgrind on the wrapper shell script generated by libtool. If + * set, C_TAP_LIBTOOL must be set to the full path to the libtool program + * to use to run valgrind and thus the test. Ignored if the test isn't + * being run under valgrind. + */ +static void +parse_test_list_line(const char *line, struct testset *ts, const char *source, + const char *build) +{ + const char *p, *end, *option, *libtool; + const char *valgrind = NULL; + unsigned int use_libtool = 0; + unsigned int use_valgrind = 0; + size_t len, i; + + /* Determine the name of the test. */ + p = skip_non_whitespace(line); + ts->file = xstrndup(line, p - line); + + /* Check if any test options are set. */ + p = skip_whitespace(p); + while (*p != '\0') { + end = skip_non_whitespace(p); + if (strncmp(p, "libtool", end - p) == 0) { + use_libtool = 1; + } else if (strncmp(p, "valgrind", end - p) == 0) { + valgrind = getenv("C_TAP_VALGRIND"); + use_valgrind = (valgrind != NULL); + } else { + option = xstrndup(p, end - p); + die("unknown test list option %s", option); + } + p = skip_whitespace(end); + } + + /* Construct the argv to run the test. First, find the length. */ + len = 1; + if (use_valgrind && valgrind != NULL) { + p = skip_whitespace(valgrind); + while (*p != '\0') { + len++; + p = skip_whitespace(skip_non_whitespace(p)); + } + if (use_libtool) + len += 2; + } + + /* Now, build the command. */ + ts->command = xcalloc(len + 1, char *); + i = 0; + if (use_valgrind && valgrind != NULL) { + if (use_libtool) { + libtool = getenv("C_TAP_LIBTOOL"); + if (libtool == NULL) + die("valgrind with libtool requested, but C_TAP_LIBTOOL is not" + " set"); + ts->command[i++] = xstrdup(libtool); + ts->command[i++] = xstrdup("--mode=execute"); + } + p = skip_whitespace(valgrind); + while (*p != '\0') { + end = skip_non_whitespace(p); + ts->command[i++] = xstrndup(p, end - p); + p = skip_whitespace(end); + } + } + if (i != len - 1) + die("internal error while constructing command line"); + ts->command[i++] = find_test(ts->file, source, build); + ts->command[i] = NULL; +} + + +/* + * Read a list of tests from a file, returning the list of tests as a struct + * testlist, or NULL if there were no tests (such as a file containing only + * comments). Reports an error to standard error and exits if the list of + * tests cannot be read. + */ +static struct testlist * +read_test_list(const char *filename, const char *source, const char *build) +{ + FILE *file; + unsigned int line; + size_t length; + char buffer[BUFSIZ]; + const char *start; + struct testlist *listhead, *current; + + /* Create the initial container list that will hold our results. */ + listhead = xcalloc(1, struct testlist); + current = NULL; + + /* + * Open our file of tests to run and read it line by line, creating a new + * struct testlist and struct testset for each line. + */ + file = fopen(filename, "r"); + if (file == NULL) + sysdie("can't open %s", filename); + line = 0; + while (fgets(buffer, sizeof(buffer), file)) { + line++; + length = strlen(buffer) - 1; + if (buffer[length] != '\n') { + fprintf(stderr, "%s:%u: line too long\n", filename, line); + exit(1); + } + buffer[length] = '\0'; + + /* Skip comments, leading spaces, and blank lines. */ + start = skip_whitespace(buffer); + if (strlen(start) == 0) continue; - path = xmalloc(strlen(bases[i]) + strlen(name) + 4); - sprintf(path, "%s/%s-t", bases[i], name); - if (access(path, X_OK) != 0) - path[strlen(path) - 2] = '.'; - if (access(path, X_OK) == 0) - break; - free(path); - path = NULL; + if (start[0] == '#') + continue; + + /* Allocate the new testset structure. */ + if (current == NULL) + current = listhead; + else { + current->next = xcalloc(1, struct testlist); + current = current->next; + } + current->ts = xcalloc(1, struct testset); + current->ts->plan = PLAN_INIT; + + /* Parse the line and store the results in the testset struct. */ + parse_test_list_line(start, current->ts, source, build); } - if (path == NULL) { - path = xmalloc(strlen(name) + 3); - sprintf(path, "%s-t", name); + fclose(file); + + /* If there were no tests, current is still NULL. */ + if (current == NULL) { + free(listhead); + return NULL; } - ts->path = path; + + /* Return the results. */ + return listhead; } /* - * Run a batch of tests from a given file listing each test on a line by - * itself. Takes two additional parameters: the root of the source directory - * and the root of the build directory. Test programs will be first searched - * for in the current directory, then the build directory, then the source - * directory. The file must be rewindable. Returns true iff all tests - * passed. + * Build a list of tests from command line arguments. Takes the argv and argc + * representing the command line arguments and returns a newly allocated test + * list, or NULL if there were no tests. The caller is responsible for + * freeing. + */ +static struct testlist * +build_test_list(char *argv[], int argc, const char *source, const char *build) +{ + int i; + struct testlist *listhead, *current; + + /* Create the initial container list that will hold our results. */ + listhead = xcalloc(1, struct testlist); + current = NULL; + + /* Walk the list of arguments and create test sets for them. */ + for (i = 0; i < argc; i++) { + if (current == NULL) + current = listhead; + else { + current->next = xcalloc(1, struct testlist); + current = current->next; + } + current->ts = xcalloc(1, struct testset); + current->ts->plan = PLAN_INIT; + current->ts->file = xstrdup(argv[i]); + current->ts->command = xcalloc(2, char *); + current->ts->command[0] = find_test(current->ts->file, source, build); + current->ts->command[1] = NULL; + } + + /* If there were no tests, current is still NULL. */ + if (current == NULL) { + free(listhead); + return NULL; + } + + /* Return the results. */ + return listhead; +} + + +/* Free a struct testset. */ +static void +free_testset(struct testset *ts) +{ + size_t i; + + free(ts->file); + for (i = 0; ts->command[i] != NULL; i++) + free(ts->command[i]); + free(ts->command); + free(ts->results); + free(ts->reason); + free(ts); +} + + +/* + * Run a batch of tests. Takes two additional parameters: the root of the + * source directory and the root of the build directory. Test programs will + * be first searched for in the current directory, then the build directory, + * then the source directory. Returns true iff all tests passed, and always + * frees the test list that's passed in. */ static int -test_batch(const char *testlist, const char *source, const char *build) +test_batch(struct testlist *tests, enum test_verbose verbose) { - FILE *tests; - unsigned int length, i; - unsigned int longest = 0; - char buffer[BUFSIZ]; - unsigned int line; - struct testset ts, *tmp; + size_t length, i; + size_t longest = 0; + unsigned int count = 0; + struct testset *ts; struct timeval start, end; struct rusage stats; struct testlist *failhead = NULL; struct testlist *failtail = NULL; - struct testlist *next; + struct testlist *current, *next; + int succeeded; unsigned long total = 0; unsigned long passed = 0; unsigned long skipped = 0; unsigned long failed = 0; unsigned long aborted = 0; - /* - * Open our file of tests to run and scan it, checking for lines that - * are too long and searching for the longest line. - */ - tests = fopen(testlist, "r"); - if (!tests) - sysdie("can't open %s", testlist); - line = 0; - while (fgets(buffer, sizeof(buffer), tests)) { - line++; - length = strlen(buffer) - 1; - if (buffer[length] != '\n') { - fprintf(stderr, "%s:%u: line too long\n", testlist, line); - exit(1); - } + /* Walk the list of tests to find the longest name. */ + for (current = tests; current != NULL; current = current->next) { + length = strlen(current->ts->file); if (length > longest) longest = length; } - if (fseek(tests, 0, SEEK_SET) == -1) - sysdie("can't rewind %s", testlist); /* * Add two to longest and round up to the nearest tab stop. This is how @@ -998,64 +1544,54 @@ test_batch(const char *testlist, const char *source, const char *build) /* Start the wall clock timer. */ gettimeofday(&start, NULL); - /* - * Now, plow through our tests again, running each one. Check line - * length again out of paranoia. - */ - line = 0; - while (fgets(buffer, sizeof(buffer), tests)) { - line++; - length = strlen(buffer) - 1; - if (buffer[length] != '\n') { - fprintf(stderr, "%s:%u: line too long\n", testlist, line); - exit(1); - } - buffer[length] = '\0'; - fputs(buffer, stdout); - for (i = length; i < longest; i++) - putchar('.'); + /* Now, plow through our tests again, running each one. */ + for (current = tests; current != NULL; current = current->next) { + ts = current->ts; + + /* Print out the name of the test file. */ + fputs(ts->file, stdout); + if (verbose) + fputs("\n\n", stdout); + else + for (i = strlen(ts->file); i < longest; i++) + putchar('.'); if (isatty(STDOUT_FILENO)) fflush(stdout); - memset(&ts, 0, sizeof(ts)); - ts.plan = PLAN_INIT; - ts.file = xstrdup(buffer); - find_test(buffer, &ts, source, build); - ts.reason = NULL; - if (test_run(&ts)) { - free(ts.file); - free(ts.path); - free(ts.results); - if (ts.reason != NULL) - free(ts.reason); - } else { - tmp = xmalloc(sizeof(struct testset)); - memcpy(tmp, &ts, sizeof(struct testset)); - if (!failhead) { - failhead = xmalloc(sizeof(struct testset)); - failhead->ts = tmp; - failhead->next = NULL; + + /* Run the test. */ + succeeded = test_run(ts, verbose); + fflush(stdout); + if (verbose) + putchar('\n'); + + /* Record cumulative statistics. */ + aborted += ts->aborted; + total += ts->count + ts->all_skipped; + passed += ts->passed; + skipped += ts->skipped + ts->all_skipped; + failed += ts->failed; + count++; + + /* If the test fails, we shuffle it over to the fail list. */ + if (!succeeded) { + if (failhead == NULL) { + failhead = xcalloc(1, struct testlist); failtail = failhead; } else { - failtail->next = xmalloc(sizeof(struct testset)); + failtail->next = xcalloc(1, struct testlist); failtail = failtail->next; - failtail->ts = tmp; - failtail->next = NULL; } + failtail->ts = ts; + failtail->next = NULL; } - aborted += ts.aborted; - total += ts.count + ts.all_skipped; - passed += ts.passed; - skipped += ts.skipped + ts.all_skipped; - failed += ts.failed; } total -= skipped; - fclose(tests); /* Stop the timer and get our child resource statistics. */ gettimeofday(&end, NULL); getrusage(RUSAGE_CHILDREN, &stats); - /* Print out our final results. */ + /* Summarize the failures and free the failure list. */ if (failhead != NULL) { test_fail_summary(failhead); while (failhead != NULL) { @@ -1064,6 +1600,16 @@ test_batch(const char *testlist, const char *source, const char *build) failhead = next; } } + + /* Free the memory used by the test lists. */ + while (tests != NULL) { + next = tests->next; + free_testset(tests->ts); + free(tests); + tests = next; + } + + /* Print out the final test summary. */ putchar('\n'); if (aborted != 0) { if (aborted == 1) @@ -1071,12 +1617,11 @@ test_batch(const char *testlist, const char *source, const char *build) else printf("Aborted %lu test sets", aborted); printf(", passed %lu/%lu tests", passed, total); - } - else if (failed == 0) + } else if (failed == 0) fputs("All tests successful", stdout); else printf("Failed %lu/%lu tests, %.2f%% okay", failed, total, - (total - failed) * 100.0 / total); + (double) (total - failed) * 100.0 / (double) total); if (skipped != 0) { if (skipped == 1) printf(", %lu test skipped", skipped); @@ -1084,10 +1629,10 @@ test_batch(const char *testlist, const char *source, const char *build) printf(", %lu tests skipped", skipped); } puts("."); - printf("Files=%u, Tests=%lu", line, total); + printf("Files=%u, Tests=%lu", count, total); printf(", %.2f seconds", tv_diff(&end, &start)); - printf(" (%.2f usr + %.2f sys = %.2f CPU)\n", - tv_seconds(&stats.ru_utime), tv_seconds(&stats.ru_stime), + printf(" (%.2f usr + %.2f sys = %.2f CPU)\n", tv_seconds(&stats.ru_utime), + tv_seconds(&stats.ru_stime), tv_sum(&stats.ru_utime, &stats.ru_stime)); return (failed == 0 && aborted == 0); } @@ -1100,18 +1645,18 @@ test_batch(const char *testlist, const char *source, const char *build) static void test_single(const char *program, const char *source, const char *build) { - struct testset ts; + char *path; - memset(&ts, 0, sizeof(ts)); - find_test(program, &ts, source, build); - if (execl(ts.path, ts.path, (char *) 0) == -1) - sysdie("cannot exec %s", ts.path); + path = find_test(program, source, build); + if (execl(path, path, (char *) 0) == -1) + sysdie("cannot exec %s", path); } /* - * Main routine. Set the SOURCE and BUILD environment variables and then, - * given a file listing tests, run each test listed. + * Main routine. Set the C_TAP_SOURCE, C_TAP_BUILD, SOURCE, and BUILD + * environment variables and then, given a file listing tests, run each test + * listed. */ int main(int argc, char *argv[]) @@ -1119,20 +1664,29 @@ main(int argc, char *argv[]) int option; int status = 0; int single = 0; + enum test_verbose verbose = CONCISE; + char *c_tap_source_env = NULL; + char *c_tap_build_env = NULL; char *source_env = NULL; char *build_env = NULL; - const char *list; - const char *source = SOURCE; - const char *build = BUILD; - - while ((option = getopt(argc, argv, "b:hos:")) != EOF) { + const char *program; + const char *shortlist; + const char *list = NULL; + const char *source = C_TAP_SOURCE; + const char *build = C_TAP_BUILD; + struct testlist *tests; + + program = argv[0]; + while ((option = getopt(argc, argv, "b:hl:os:v")) != EOF) { switch (option) { case 'b': build = optarg; break; case 'h': - printf(usage_message, argv[0], argv[0]); + printf(usage_message, program, program, program, usage_extra); exit(0); + case 'l': + list = optarg; break; case 'o': single = 1; @@ -1140,49 +1694,77 @@ main(int argc, char *argv[]) case 's': source = optarg; break; + case 'v': + verbose = VERBOSE; + break; default: exit(1); } } - if (argc - optind != 1) { - fprintf(stderr, usage_message, argv[0], argv[0]); + argv += optind; + argc -= optind; + if ((list == NULL && argc < 1) || (list != NULL && argc > 0)) { + fprintf(stderr, usage_message, program, program, program, usage_extra); exit(1); } - argc -= optind; - argv += optind; + /* + * If C_TAP_VERBOSE is set in the environment, that also turns on verbose + * mode. + */ + if (getenv("C_TAP_VERBOSE") != NULL) + verbose = VERBOSE; + + /* + * Set C_TAP_SOURCE and C_TAP_BUILD environment variables. Also set + * SOURCE and BUILD for backward compatibility, although we're trying to + * migrate to the ones with a C_TAP_* prefix. + */ if (source != NULL) { - source_env = xmalloc(strlen("SOURCE=") + strlen(source) + 1); - sprintf(source_env, "SOURCE=%s", source); + c_tap_source_env = concat("C_TAP_SOURCE=", source, (const char *) 0); + if (putenv(c_tap_source_env) != 0) + sysdie("cannot set C_TAP_SOURCE in the environment"); + source_env = concat("SOURCE=", source, (const char *) 0); if (putenv(source_env) != 0) sysdie("cannot set SOURCE in the environment"); } if (build != NULL) { - build_env = xmalloc(strlen("BUILD=") + strlen(build) + 1); - sprintf(build_env, "BUILD=%s", build); + c_tap_build_env = concat("C_TAP_BUILD=", build, (const char *) 0); + if (putenv(c_tap_build_env) != 0) + sysdie("cannot set C_TAP_BUILD in the environment"); + build_env = concat("BUILD=", build, (const char *) 0); if (putenv(build_env) != 0) sysdie("cannot set BUILD in the environment"); } + /* Run the tests as instructed. */ if (single) test_single(argv[0], source, build); - else { - list = strrchr(argv[0], '/'); - if (list == NULL) - list = argv[0]; + else if (list != NULL) { + shortlist = strrchr(list, '/'); + if (shortlist == NULL) + shortlist = list; else - list++; - printf(banner, list); - status = test_batch(argv[0], source, build) ? 0 : 1; + shortlist++; + printf(banner, shortlist); + tests = read_test_list(list, source, build); + status = test_batch(tests, verbose) ? 0 : 1; + } else { + tests = build_test_list(argv, argc, source, build); + status = test_batch(tests, verbose) ? 0 : 1; } - /* For valgrind cleanliness. */ + /* For valgrind cleanliness, free all our memory. */ if (source_env != NULL) { + putenv((char *) "C_TAP_SOURCE="); putenv((char *) "SOURCE="); + free(c_tap_source_env); free(source_env); } if (build_env != NULL) { + putenv((char *) "C_TAP_BUILD="); putenv((char *) "BUILD="); + free(c_tap_build_env); free(build_env); } exit(status); diff --git a/src/external/c-tap-harness/tests/tap/basic.c b/src/external/c-tap-harness/tests/tap/basic.c index e8196fc..b5f42d0 100644 --- a/src/external/c-tap-harness/tests/tap/basic.c +++ b/src/external/c-tap-harness/tests/tap/basic.c @@ -10,10 +10,11 @@ * up the TAP output format, or finding things in the test environment. * * This file is part of C TAP Harness. The current version plus supporting - * documentation is at . + * documentation is at . * - * Copyright 2009, 2010, 2011, 2012 Russ Allbery - * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012 + * Written by Russ Allbery + * Copyright 2009-2019 Russ Allbery + * Copyright 2001-2002, 2004-2008, 2011-2014 * The Board of Trustees of the Leland Stanford Junior University * * Permission is hereby granted, free of charge, to any person obtaining a @@ -33,17 +34,20 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT */ #include +#include #include #include #include #include #ifdef _WIN32 -# include +# include #else -# include +# include #endif #include #include @@ -52,13 +56,13 @@ /* Windows provides mkdir and rmdir under different names. */ #ifdef _WIN32 -# define mkdir(p, m) _mkdir(p) -# define rmdir(p) _rmdir(p) +# define mkdir(p, m) _mkdir(p) +# define rmdir(p) _rmdir(p) #endif /* * The test count. Always contains the number that will be used for the next - * test status. + * test status. This is exported to callers of the library. */ unsigned long testnum = 1; @@ -66,72 +70,324 @@ unsigned long testnum = 1; * Status information stored so that we can give a test summary at the end of * the test case. We store the planned final test and the count of failures. * We can get the highest test count from testnum. - * - * We also store the PID of the process that called plan() and only summarize + */ +static unsigned long _planned = 0; +static unsigned long _failed = 0; + +/* + * Store the PID of the process that called plan() and only summarize * results when that process exits, so as to not misreport results in forked * processes. - * - * If _lazy is true, we're doing lazy planning and will print out the plan - * based on the last test number at the end of testing. */ -static unsigned long _planned = 0; -static unsigned long _failed = 0; static pid_t _process = 0; + +/* + * If true, we're doing lazy planning and will print out the plan based on the + * last test number at the end of testing. + */ static int _lazy = 0; +/* + * If true, the test was aborted by calling bail(). Currently, this is only + * used to ensure that we pass a false value to any cleanup functions even if + * all tests to that point have passed. + */ +static int _aborted = 0; + +/* + * Registered cleanup functions. These are stored as a linked list and run in + * registered order by finish when the test program exits. Each function is + * passed a boolean value indicating whether all tests were successful. + */ +struct cleanup_func { + test_cleanup_func func; + test_cleanup_func_with_data func_with_data; + void *data; + struct cleanup_func *next; +}; +static struct cleanup_func *cleanup_funcs = NULL; + +/* + * Registered diag files. Any output found in these files will be printed out + * as if it were passed to diag() before any other output we do. This allows + * background processes to log to a file and have that output interleaved with + * the test output. + */ +struct diag_file { + char *name; + FILE *file; + char *buffer; + size_t bufsize; + struct diag_file *next; +}; +static struct diag_file *diag_files = NULL; + +/* + * Print a specified prefix and then the test description. Handles turning + * the argument list into a va_args structure suitable for passing to + * print_desc, which has to be done in a macro. Assumes that format is the + * argument immediately before the variadic arguments. + */ +#define PRINT_DESC(prefix, format) \ + do { \ + if (format != NULL) { \ + va_list args; \ + printf("%s", prefix); \ + va_start(args, format); \ + vprintf(format, args); \ + va_end(args); \ + } \ + } while (0) + + +/* + * Form a new string by concatenating multiple strings. The arguments must be + * terminated by (const char *) 0. + * + * This function only exists because we can't assume asprintf. We can't + * simulate asprintf with snprintf because we're only assuming SUSv3, which + * does not require that snprintf with a NULL buffer return the required + * length. When those constraints are relaxed, this should be ripped out and + * replaced with asprintf or a more trivial replacement with snprintf. + */ +static char * +concat(const char *first, ...) +{ + va_list args; + char *result; + const char *string; + size_t offset; + size_t length = 0; + + /* + * Find the total memory required. Ensure we don't overflow length. See + * the comment for breallocarray for why we're using UINT_MAX here. + */ + va_start(args, first); + for (string = first; string != NULL; string = va_arg(args, const char *)) { + if (length >= UINT_MAX - strlen(string)) + bail("strings too long in concat"); + length += strlen(string); + } + va_end(args); + length++; + + /* Create the string. */ + result = bcalloc_type(length, char); + va_start(args, first); + offset = 0; + for (string = first; string != NULL; string = va_arg(args, const char *)) { + memcpy(result + offset, string, strlen(string)); + offset += strlen(string); + } + va_end(args); + result[offset] = '\0'; + return result; +} + + +/* + * Helper function for check_diag_files to handle a single line in a diag + * file. + * + * The general scheme here used is as follows: read one line of output. If we + * get NULL, check for an error. If there was one, bail out of the test + * program; otherwise, return, and the enclosing loop will check for EOF. + * + * If we get some data, see if it ends in a newline. If it doesn't end in a + * newline, we have one of two cases: our buffer isn't large enough, in which + * case we resize it and try again, or we have incomplete data in the file, in + * which case we rewind the file and will try again next time. + * + * Returns a boolean indicating whether the last line was incomplete. + */ +static int +handle_diag_file_line(struct diag_file *file, fpos_t where) +{ + int size; + size_t length; + + /* Read the next line from the file. */ + size = file->bufsize > INT_MAX ? INT_MAX : (int) file->bufsize; + if (fgets(file->buffer, size, file->file) == NULL) { + if (ferror(file->file)) + sysbail("cannot read from %s", file->name); + return 0; + } + + /* + * See if the line ends in a newline. If not, see which error case we + * have. + */ + length = strlen(file->buffer); + if (file->buffer[length - 1] != '\n') { + int incomplete = 0; + + /* Check whether we ran out of buffer space and resize if so. */ + if (length < file->bufsize - 1) + incomplete = 1; + else { + file->bufsize += BUFSIZ; + file->buffer = + breallocarray_type(file->buffer, file->bufsize, char); + } + + /* + * On either incomplete lines or too small of a buffer, rewind + * and read the file again (on the next pass, if incomplete). + * It's simpler than trying to double-buffer the file. + */ + if (fsetpos(file->file, &where) < 0) + sysbail("cannot set position in %s", file->name); + return incomplete; + } + + /* We saw a complete line. Print it out. */ + printf("# %s", file->buffer); + return 0; +} + + +/* + * Check all registered diag_files for any output. We only print out the + * output if we see a complete line; otherwise, we wait for the next newline. + */ +static void +check_diag_files(void) +{ + struct diag_file *file; + fpos_t where; + int incomplete; + + /* + * Walk through each file and read each line of output available. + */ + for (file = diag_files; file != NULL; file = file->next) { + clearerr(file->file); + + /* Store the current position in case we have to rewind. */ + if (fgetpos(file->file, &where) < 0) + sysbail("cannot get position in %s", file->name); + + /* Continue until we get EOF or an incomplete line of data. */ + incomplete = 0; + while (!feof(file->file) && !incomplete) { + incomplete = handle_diag_file_line(file, where); + } + } +} + /* * Our exit handler. Called on completion of the test to report a summary of * results provided we're still in the original process. This also handles * printing out the plan if we used plan_lazy(), although that's suppressed if - * we never ran a test (due to an early bail, for example). + * we never ran a test (due to an early bail, for example), and running any + * registered cleanup functions. */ static void finish(void) { + int success, primary; + struct cleanup_func *current; unsigned long highest = testnum - 1; + struct diag_file *file, *tmp; + + /* Check for pending diag_file output. */ + check_diag_files(); + + /* Free the diag_files. */ + file = diag_files; + while (file != NULL) { + tmp = file; + file = file->next; + fclose(tmp->file); + free(tmp->name); + free(tmp->buffer); + free(tmp); + } + diag_files = NULL; + + /* + * Determine whether all tests were successful, which is needed before + * calling cleanup functions since we pass that fact to the functions. + */ + if (_planned == 0 && _lazy) + _planned = highest; + success = (!_aborted && _planned == highest && _failed == 0); + + /* + * If there are any registered cleanup functions, we run those first. We + * always run them, even if we didn't run a test. Don't do anything + * except free the diag_files and call cleanup functions if we aren't the + * primary process (the process in which plan or plan_lazy was called), + * and tell the cleanup functions that fact. + */ + primary = (_process == 0 || getpid() == _process); + while (cleanup_funcs != NULL) { + if (cleanup_funcs->func_with_data) { + void *data = cleanup_funcs->data; + + cleanup_funcs->func_with_data(success, primary, data); + } else { + cleanup_funcs->func(success, primary); + } + current = cleanup_funcs; + cleanup_funcs = cleanup_funcs->next; + free(current); + } + if (!primary) + return; + + /* Don't do anything further if we never planned a test. */ + if (_planned == 0) + return; - if (_planned == 0 && !_lazy) + /* If we're aborting due to bail, don't print summaries. */ + if (_aborted) return; + + /* Print out the lazy plan if needed. */ fflush(stderr); - if (_process != 0 && getpid() == _process) { - if (_lazy && highest > 0) { - printf("1..%lu\n", highest); - _planned = highest; - } - if (_planned > highest) - printf("# Looks like you planned %lu test%s but only ran %lu\n", - _planned, (_planned > 1 ? "s" : ""), highest); - else if (_planned < highest) - printf("# Looks like you planned %lu test%s but ran %lu extra\n", - _planned, (_planned > 1 ? "s" : ""), highest - _planned); - else if (_failed > 0) - printf("# Looks like you failed %lu test%s of %lu\n", _failed, - (_failed > 1 ? "s" : ""), _planned); - else if (_planned > 1) - printf("# All %lu tests successful or skipped\n", _planned); - else - printf("# %lu test successful or skipped\n", _planned); - } + if (_lazy && _planned > 0) + printf("1..%lu\n", _planned); + + /* Print out a summary of the results. */ + if (_planned > highest) + diag("Looks like you planned %lu test%s but only ran %lu", _planned, + (_planned > 1 ? "s" : ""), highest); + else if (_planned < highest) + diag("Looks like you planned %lu test%s but ran %lu extra", _planned, + (_planned > 1 ? "s" : ""), highest - _planned); + else if (_failed > 0) + diag("Looks like you failed %lu test%s of %lu", _failed, + (_failed > 1 ? "s" : ""), _planned); + else if (_planned != 1) + diag("All %lu tests successful or skipped", _planned); + else + diag("%lu test successful or skipped", _planned); } /* * Initialize things. Turns on line buffering on stdout and then prints out - * the number of tests in the test suite. + * the number of tests in the test suite. We intentionally don't check for + * pending diag_file output here, since it should really come after the plan. */ void plan(unsigned long count) { if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0) - fprintf(stderr, "# cannot set stdout to line buffered: %s\n", - strerror(errno)); + sysdiag("cannot set stdout to line buffered"); fflush(stderr); printf("1..%lu\n", count); testnum = 1; _planned = count; _process = getpid(); - atexit(finish); + if (atexit(finish) != 0) { + sysdiag("cannot register exit handler"); + diag("cleanups will not be run"); + } } @@ -143,83 +399,66 @@ void plan_lazy(void) { if (setvbuf(stdout, NULL, _IOLBF, BUFSIZ) != 0) - fprintf(stderr, "# cannot set stdout to line buffered: %s\n", - strerror(errno)); + sysdiag("cannot set stdout to line buffered"); testnum = 1; _process = getpid(); _lazy = 1; - atexit(finish); + if (atexit(finish) != 0) + sysbail("cannot register exit handler to display plan"); } /* * Skip the entire test suite and exits. Should be called instead of plan(), - * not after it, since it prints out a special plan line. + * not after it, since it prints out a special plan line. Ignore diag_file + * output here, since it's not clear if it's allowed before the plan. */ void skip_all(const char *format, ...) { fflush(stderr); printf("1..0 # skip"); - if (format != NULL) { - va_list args; - - putchar(' '); - va_start(args, format); - vprintf(format, args); - va_end(args); - } + PRINT_DESC(" ", format); putchar('\n'); exit(0); } /* - * Print the test description. - */ -static void -print_desc(const char *format, va_list args) -{ - printf(" - "); - vprintf(format, args); -} - - -/* * Takes a boolean success value and assumes the test passes if that value * is true and fails if that value is false. */ -void +int ok(int success, const char *format, ...) { fflush(stderr); + check_diag_files(); printf("%sok %lu", success ? "" : "not ", testnum++); if (!success) _failed++; - if (format != NULL) { - va_list args; - - va_start(args, format); - print_desc(format, args); - va_end(args); - } + PRINT_DESC(" - ", format); putchar('\n'); + return success; } /* * Same as ok(), but takes the format arguments as a va_list. */ -void +int okv(int success, const char *format, va_list args) { fflush(stderr); + check_diag_files(); printf("%sok %lu", success ? "" : "not ", testnum++); if (!success) _failed++; - if (format != NULL) - print_desc(format, args); + if (format != NULL) { + printf(" - "); + vprintf(format, args); + } putchar('\n'); + return success; } @@ -230,15 +469,9 @@ void skip(const char *reason, ...) { fflush(stderr); + check_diag_files(); printf("ok %lu # skip", testnum++); - if (reason != NULL) { - va_list args; - - va_start(args, reason); - putchar(' '); - vprintf(reason, args); - va_end(args); - } + PRINT_DESC(" ", reason); putchar('\n'); } @@ -246,25 +479,21 @@ skip(const char *reason, ...) /* * Report the same status on the next count tests. */ -void -ok_block(unsigned long count, int status, const char *format, ...) +int +ok_block(unsigned long count, int success, const char *format, ...) { unsigned long i; fflush(stderr); + check_diag_files(); for (i = 0; i < count; i++) { - printf("%sok %lu", status ? "" : "not ", testnum++); - if (!status) + printf("%sok %lu", success ? "" : "not ", testnum++); + if (!success) _failed++; - if (format != NULL) { - va_list args; - - va_start(args, format); - print_desc(format, args); - va_end(args); - } + PRINT_DESC(" - ", format); putchar('\n'); } + return success; } @@ -277,101 +506,157 @@ skip_block(unsigned long count, const char *reason, ...) unsigned long i; fflush(stderr); + check_diag_files(); for (i = 0; i < count; i++) { printf("ok %lu # skip", testnum++); - if (reason != NULL) { - va_list args; - - va_start(args, reason); - putchar(' '); - vprintf(reason, args); - va_end(args); - } + PRINT_DESC(" ", reason); putchar('\n'); } } /* - * Takes an expected integer and a seen integer and assumes the test passes - * if those two numbers match. + * Takes two boolean values and requires the truth value of both match. */ -void -is_int(long wanted, long seen, const char *format, ...) +int +is_bool(int left, int right, const char *format, ...) { + int success; + fflush(stderr); - if (wanted == seen) + check_diag_files(); + success = (!!left == !!right); + if (success) printf("ok %lu", testnum++); else { - printf("# wanted: %ld\n# seen: %ld\n", wanted, seen); + diag(" left: %s", !!left ? "true" : "false"); + diag("right: %s", !!right ? "true" : "false"); printf("not ok %lu", testnum++); _failed++; } - if (format != NULL) { - va_list args; - - va_start(args, format); - print_desc(format, args); - va_end(args); - } + PRINT_DESC(" - ", format); putchar('\n'); + return success; } /* - * Takes a string and what the string should be, and assumes the test passes - * if those strings match (using strcmp). + * Takes two integer values and requires they match. */ -void -is_string(const char *wanted, const char *seen, const char *format, ...) +int +is_int(long left, long right, const char *format, ...) { - if (wanted == NULL) - wanted = "(null)"; - if (seen == NULL) - seen = "(null)"; + int success; + fflush(stderr); - if (strcmp(wanted, seen) == 0) + check_diag_files(); + success = (left == right); + if (success) printf("ok %lu", testnum++); else { - printf("# wanted: %s\n# seen: %s\n", wanted, seen); + diag(" left: %ld", left); + diag("right: %ld", right); printf("not ok %lu", testnum++); _failed++; } - if (format != NULL) { - va_list args; + PRINT_DESC(" - ", format); + putchar('\n'); + return success; +} - va_start(args, format); - print_desc(format, args); - va_end(args); + +/* + * Takes two strings and requires they match (using strcmp). NULL arguments + * are permitted and handled correctly. + */ +int +is_string(const char *left, const char *right, const char *format, ...) +{ + int success; + + fflush(stderr); + check_diag_files(); + + /* Compare the strings, being careful of NULL. */ + if (left == NULL) + success = (right == NULL); + else if (right == NULL) + success = 0; + else + success = (strcmp(left, right) == 0); + + /* Report the results. */ + if (success) + printf("ok %lu", testnum++); + else { + diag(" left: %s", left == NULL ? "(null)" : left); + diag("right: %s", right == NULL ? "(null)" : right); + printf("not ok %lu", testnum++); + _failed++; } + PRINT_DESC(" - ", format); putchar('\n'); + return success; } /* - * Takes an expected unsigned long and a seen unsigned long and assumes the - * test passes if the two numbers match. Otherwise, reports them in hex. + * Takes two unsigned longs and requires they match. On failure, reports them + * in hex. */ -void -is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) +int +is_hex(unsigned long left, unsigned long right, const char *format, ...) { + int success; + fflush(stderr); - if (wanted == seen) + check_diag_files(); + success = (left == right); + if (success) printf("ok %lu", testnum++); else { - printf("# wanted: %lx\n# seen: %lx\n", (unsigned long) wanted, - (unsigned long) seen); + diag(" left: %lx", (unsigned long) left); + diag("right: %lx", (unsigned long) right); printf("not ok %lu", testnum++); _failed++; } - if (format != NULL) { - va_list args; + PRINT_DESC(" - ", format); + putchar('\n'); + return success; +} + + +/* + * Takes pointers to a regions of memory and requires that len bytes from each + * match. Otherwise reports any bytes which didn't match. + */ +int +is_blob(const void *left, const void *right, size_t len, const char *format, + ...) +{ + int success; + size_t i; + + fflush(stderr); + check_diag_files(); + success = (memcmp(left, right, len) == 0); + if (success) + printf("ok %lu", testnum++); + else { + const unsigned char *left_c = (const unsigned char *) left; + const unsigned char *right_c = (const unsigned char *) right; - va_start(args, format); - print_desc(format, args); - va_end(args); + for (i = 0; i < len; i++) { + if (left_c[i] != right_c[i]) + diag("offset %lu: left %02x, right %02x", (unsigned long) i, + left_c[i], right_c[i]); + } + printf("not ok %lu", testnum++); + _failed++; } + PRINT_DESC(" - ", format); putchar('\n'); + return success; } @@ -383,14 +668,16 @@ bail(const char *format, ...) { va_list args; + _aborted = 1; fflush(stderr); + check_diag_files(); fflush(stdout); printf("Bail out! "); va_start(args, format); vprintf(format, args); va_end(args); printf("\n"); - exit(1); + exit(255); } @@ -403,51 +690,110 @@ sysbail(const char *format, ...) va_list args; int oerrno = errno; + _aborted = 1; fflush(stderr); + check_diag_files(); fflush(stdout); printf("Bail out! "); va_start(args, format); vprintf(format, args); va_end(args); printf(": %s\n", strerror(oerrno)); - exit(1); + exit(255); } /* - * Report a diagnostic to stderr. + * Report a diagnostic to stderr. Always returns 1 to allow embedding in + * compound statements. */ -void +int diag(const char *format, ...) { va_list args; fflush(stderr); + check_diag_files(); fflush(stdout); printf("# "); va_start(args, format); vprintf(format, args); va_end(args); printf("\n"); + return 1; } /* - * Report a diagnostic to stderr, appending strerror(errno). + * Report a diagnostic to stderr, appending strerror(errno). Always returns 1 + * to allow embedding in compound statements. */ -void +int sysdiag(const char *format, ...) { va_list args; int oerrno = errno; fflush(stderr); + check_diag_files(); fflush(stdout); printf("# "); va_start(args, format); vprintf(format, args); va_end(args); printf(": %s\n", strerror(oerrno)); + return 1; +} + + +/* + * Register a new file for diag_file processing. + */ +void +diag_file_add(const char *name) +{ + struct diag_file *file, *prev; + + file = bcalloc_type(1, struct diag_file); + file->name = bstrdup(name); + file->file = fopen(file->name, "r"); + if (file->file == NULL) + sysbail("cannot open %s", name); + file->buffer = bcalloc_type(BUFSIZ, char); + file->bufsize = BUFSIZ; + if (diag_files == NULL) + diag_files = file; + else { + for (prev = diag_files; prev->next != NULL; prev = prev->next) + ; + prev->next = file; + } +} + + +/* + * Remove a file from diag_file processing. If the file is not found, do + * nothing, since there are some situations where it can be removed twice + * (such as if it's removed from a cleanup function, since cleanup functions + * are called after freeing all the diag_files). + */ +void +diag_file_remove(const char *name) +{ + struct diag_file *file; + struct diag_file **prev = &diag_files; + + for (file = diag_files; file != NULL; file = file->next) { + if (strcmp(file->name, name) == 0) { + *prev = file->next; + fclose(file->file); + free(file->name); + free(file->buffer); + free(file); + return; + } + prev = &file->next; + } } @@ -461,7 +807,7 @@ bcalloc(size_t n, size_t size) p = calloc(n, size); if (p == NULL) - sysbail("failed to calloc %lu", (unsigned long)(n * size)); + sysbail("failed to calloc %lu", (unsigned long) (n * size)); return p; } @@ -495,6 +841,34 @@ brealloc(void *p, size_t size) /* + * The same as brealloc, but determine the size by multiplying an element + * count by a size, similar to calloc. The multiplication is checked for + * integer overflow. + * + * We should technically use SIZE_MAX here for the overflow check, but + * SIZE_MAX is C99 and we're only assuming C89 + SUSv3, which does not + * guarantee that it exists. They do guarantee that UINT_MAX exists, and we + * can assume that UINT_MAX <= SIZE_MAX. + * + * (In theory, C89 and C99 permit size_t to be smaller than unsigned int, but + * I disbelieve in the existence of such systems and they will have to cope + * without overflow checks.) + */ +void * +breallocarray(void *p, size_t n, size_t size) +{ + if (n > 0 && UINT_MAX / n <= size) + bail("reallocarray too large"); + if (n == 0) + n = 1; + p = realloc(p, n * size); + if (p == NULL) + sysbail("failed to realloc %lu bytes", (unsigned long) (n * size)); + return p; +} + + +/* * Copy a string, reporting a fatal error with bail on failure. */ char * @@ -504,7 +878,7 @@ bstrdup(const char *s) size_t len; len = strlen(s) + 1; - p = malloc(len); + p = (char *) malloc(len); if (p == NULL) sysbail("failed to strdup %lu bytes", (unsigned long) len); memcpy(p, s, len); @@ -525,11 +899,11 @@ bstrndup(const char *s, size_t n) size_t length; /* Don't assume that the source string is nul-terminated. */ - for (p = s; (size_t) (p - s) < n && *p != '\0'; p++) + for (p = s; (size_t)(p - s) < n && *p != '\0'; p++) ; - length = p - s; - copy = malloc(length + 1); - if (p == NULL) + length = (size_t)(p - s); + copy = (char *) malloc(length + 1); + if (copy == NULL) sysbail("failed to strndup %lu bytes", (unsigned long) length); memcpy(copy, s, length); copy[length] = '\0'; @@ -538,31 +912,24 @@ bstrndup(const char *s, size_t n) /* - * Locate a test file. Given the partial path to a file, look under BUILD and - * then SOURCE for the file and return the full path to the file. Returns - * NULL if the file doesn't exist. A non-NULL return should be freed with - * test_file_path_free(). - * - * This function uses sprintf because it attempts to be independent of all - * other portability layers. The use immediately after a memory allocation - * should be safe without using snprintf or strlcpy/strlcat. + * Locate a test file. Given the partial path to a file, look under + * C_TAP_BUILD and then C_TAP_SOURCE for the file and return the full path to + * the file. Returns NULL if the file doesn't exist. A non-NULL return + * should be freed with test_file_path_free(). */ char * test_file_path(const char *file) { char *base; char *path = NULL; - size_t length; - const char *envs[] = { "BUILD", "SOURCE", NULL }; + const char *envs[] = {"C_TAP_BUILD", "C_TAP_SOURCE", NULL}; int i; for (i = 0; envs[i] != NULL; i++) { base = getenv(envs[i]); if (base == NULL) continue; - length = strlen(base) + 1 + strlen(file) + 1; - path = bmalloc(length); - sprintf(path, "%s/%s", base, file); + path = concat(base, "/", file, (const char *) 0); if (access(path, R_OK) == 0) break; free(path); @@ -580,13 +947,12 @@ test_file_path(const char *file) void test_file_path_free(char *path) { - if (path != NULL) - free(path); + free(path); } /* - * Create a temporary directory, tmp, under BUILD if set and the current + * Create a temporary directory, tmp, under C_TAP_BUILD if set and the current * directory if it does not. Returns the path to the temporary directory in * newly allocated memory, and calls bail on any failure. The return value * should be freed with test_tmpdir_free. @@ -600,14 +966,11 @@ test_tmpdir(void) { const char *build; char *path = NULL; - size_t length; - build = getenv("BUILD"); + build = getenv("C_TAP_BUILD"); if (build == NULL) build = "."; - length = strlen(build) + strlen("/tmp") + 1; - path = bmalloc(length); - sprintf(path, "%s/tmp", build); + path = concat(build, "/tmp", (const char *) 0); if (access(path, X_OK) < 0) if (mkdir(path, 0777) < 0) sysbail("error creating temporary directory %s", path); @@ -623,7 +986,44 @@ test_tmpdir(void) void test_tmpdir_free(char *path) { - rmdir(path); if (path != NULL) - free(path); + rmdir(path); + free(path); +} + +static void +register_cleanup(test_cleanup_func func, + test_cleanup_func_with_data func_with_data, void *data) +{ + struct cleanup_func *cleanup, **last; + + cleanup = bcalloc_type(1, struct cleanup_func); + cleanup->func = func; + cleanup->func_with_data = func_with_data; + cleanup->data = data; + cleanup->next = NULL; + last = &cleanup_funcs; + while (*last != NULL) + last = &(*last)->next; + *last = cleanup; +} + +/* + * Register a cleanup function that is called when testing ends. All such + * registered functions will be run by finish. + */ +void +test_cleanup_register(test_cleanup_func func) +{ + register_cleanup(func, NULL, NULL); +} + +/* + * Same as above, but also allows an opaque pointer to be passed to the cleanup + * function. + */ +void +test_cleanup_register_with_data(test_cleanup_func_with_data func, void *data) +{ + register_cleanup(NULL, func, data); } diff --git a/src/external/c-tap-harness/tests/tap/basic.h b/src/external/c-tap-harness/tests/tap/basic.h index fa4adaf..45f15f2 100644 --- a/src/external/c-tap-harness/tests/tap/basic.h +++ b/src/external/c-tap-harness/tests/tap/basic.h @@ -2,10 +2,11 @@ * Basic utility routines for the TAP protocol. * * This file is part of C TAP Harness. The current version plus supporting - * documentation is at . + * documentation is at . * - * Copyright 2009, 2010, 2011, 2012 Russ Allbery - * Copyright 2001, 2002, 2004, 2005, 2006, 2007, 2008, 2011, 2012 + * Written by Russ Allbery + * Copyright 2009-2019 Russ Allbery + * Copyright 2001-2002, 2004-2008, 2011-2012, 2014 * The Board of Trustees of the Leland Stanford Junior University * * Permission is hereby granted, free of charge, to any person obtaining a @@ -25,14 +26,16 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT */ #ifndef TAP_BASIC_H #define TAP_BASIC_H 1 +#include /* va_list */ +#include /* size_t */ #include -#include /* va_list */ -#include /* size_t */ /* * Used for iterating through arrays. ARRAY_SIZE returns the number of @@ -40,8 +43,8 @@ * ARRAY_END returns a pointer to the element past the end (ISO C99 makes it * legal to refer to such a pointer as long as it's never dereferenced). */ -#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) -#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)]) +#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) +#define ARRAY_END(array) (&(array)[ARRAY_SIZE(array)]) BEGIN_DECLS @@ -55,7 +58,7 @@ extern unsigned long testnum; void plan(unsigned long count); /* - * Prepare for lazy planning, in which the plan will be printed automatically + * Prepare for lazy planning, in which the plan will be printed automatically * at the end of the test program. */ void plan_lazy(void); @@ -67,27 +70,40 @@ void skip_all(const char *format, ...) /* * Basic reporting functions. The okv() function is the same as ok() but * takes the test description as a va_list to make it easier to reuse the - * reporting infrastructure when writing new tests. + * reporting infrastructure when writing new tests. ok() and okv() return the + * value of the success argument. */ -void ok(int success, const char *format, ...) +int ok(int success, const char *format, ...) __attribute__((__format__(printf, 2, 3))); -void okv(int success, const char *format, va_list args); -void skip(const char *reason, ...) - __attribute__((__format__(printf, 1, 2))); +int okv(int success, const char *format, va_list args) + __attribute__((__format__(printf, 2, 0))); +void skip(const char *reason, ...) __attribute__((__format__(printf, 1, 2))); -/* Report the same status on, or skip, the next count tests. */ -void ok_block(unsigned long count, int success, const char *format, ...) +/* + * Report the same status on, or skip, the next count tests. ok_block() + * returns the value of the success argument. + */ +int ok_block(unsigned long count, int success, const char *format, ...) __attribute__((__format__(printf, 3, 4))); void skip_block(unsigned long count, const char *reason, ...) __attribute__((__format__(printf, 2, 3))); -/* Check an expected value against a seen value. */ -void is_int(long wanted, long seen, const char *format, ...) +/* + * Compare two values. Returns true if the test passes and false if it fails. + * is_bool takes an int since the bool type isn't fully portable yet, but + * interprets both arguments for their truth value, not for their numeric + * value. + */ +int is_bool(int, int, const char *format, ...) __attribute__((__format__(printf, 3, 4))); -void is_string(const char *wanted, const char *seen, const char *format, ...) +int is_int(long, long, const char *format, ...) __attribute__((__format__(printf, 3, 4))); -void is_hex(unsigned long wanted, unsigned long seen, const char *format, ...) +int is_string(const char *, const char *, const char *format, ...) __attribute__((__format__(printf, 3, 4))); +int is_hex(unsigned long, unsigned long, const char *format, ...) + __attribute__((__format__(printf, 3, 4))); +int is_blob(const void *, const void *, size_t, const char *format, ...) + __attribute__((__format__(printf, 4, 5))); /* Bail out with an error. sysbail appends strerror(errno). */ void bail(const char *format, ...) @@ -96,39 +112,81 @@ void sysbail(const char *format, ...) __attribute__((__noreturn__, __nonnull__, __format__(printf, 1, 2))); /* Report a diagnostic to stderr prefixed with #. */ -void diag(const char *format, ...) +int diag(const char *format, ...) __attribute__((__nonnull__, __format__(printf, 1, 2))); -void sysdiag(const char *format, ...) +int sysdiag(const char *format, ...) __attribute__((__nonnull__, __format__(printf, 1, 2))); +/* + * Register or unregister a file that contains supplementary diagnostics. + * Before any other output, all registered files will be read, line by line, + * and each line will be reported as a diagnostic as if it were passed to + * diag(). Nul characters are not supported in these files and will result in + * truncated output. + */ +void diag_file_add(const char *file) __attribute__((__nonnull__)); +void diag_file_remove(const char *file) __attribute__((__nonnull__)); + /* Allocate memory, reporting a fatal error with bail on failure. */ void *bcalloc(size_t, size_t) - __attribute__((__alloc_size__(1, 2), __malloc__)); + __attribute__((__alloc_size__(1, 2), __malloc__, __warn_unused_result__)); void *bmalloc(size_t) - __attribute__((__alloc_size__(1), __malloc__)); + __attribute__((__alloc_size__(1), __malloc__, __warn_unused_result__)); +void *breallocarray(void *, size_t, size_t) + __attribute__((__alloc_size__(2, 3), __malloc__, __warn_unused_result__)); void *brealloc(void *, size_t) - __attribute__((__alloc_size__(2), __malloc__)); + __attribute__((__alloc_size__(2), __malloc__, __warn_unused_result__)); char *bstrdup(const char *) - __attribute__((__malloc__, __nonnull__)); + __attribute__((__malloc__, __nonnull__, __warn_unused_result__)); char *bstrndup(const char *, size_t) - __attribute__((__malloc__, __nonnull__)); + __attribute__((__malloc__, __nonnull__, __warn_unused_result__)); /* - * Find a test file under BUILD or SOURCE, returning the full path. The - * returned path should be freed with test_file_path_free(). + * Macros that cast the return value from b* memory functions, making them + * usable in C++ code and providing some additional type safety. + */ +#define bcalloc_type(n, type) ((type *) bcalloc((n), sizeof(type))) +#define breallocarray_type(p, n, type) \ + ((type *) breallocarray((p), (n), sizeof(type))) + +/* + * Find a test file under C_TAP_BUILD or C_TAP_SOURCE, returning the full + * path. The returned path should be freed with test_file_path_free(). */ char *test_file_path(const char *file) - __attribute__((__malloc__, __nonnull__)); + __attribute__((__malloc__, __nonnull__, __warn_unused_result__)); void test_file_path_free(char *path); /* - * Create a temporary directory relative to BUILD and return the path. The - * returned path should be freed with test_tmpdir_free. + * Create a temporary directory relative to C_TAP_BUILD and return the path. + * The returned path should be freed with test_tmpdir_free(). */ -char *test_tmpdir(void) - __attribute__((__malloc__)); +char *test_tmpdir(void) __attribute__((__malloc__, __warn_unused_result__)); void test_tmpdir_free(char *path); +/* + * Register a cleanup function that is called when testing ends. All such + * registered functions will be run during atexit handling (and are therefore + * subject to all the same constraints and caveats as atexit functions). + * + * The function must return void and will be passed two arguments: an int that + * will be true if the test completed successfully and false otherwise, and an + * int that will be true if the cleanup function is run in the primary process + * (the one that called plan or plan_lazy) and false otherwise. If + * test_cleanup_register_with_data is used instead, a generic pointer can be + * provided and will be passed to the cleanup function as a third argument. + * + * test_cleanup_register_with_data is the better API and should have been the + * only API. test_cleanup_register was an API error preserved for backward + * cmpatibility. + */ +typedef void (*test_cleanup_func)(int, int); +typedef void (*test_cleanup_func_with_data)(int, int, void *); + +void test_cleanup_register(test_cleanup_func) __attribute__((__nonnull__)); +void test_cleanup_register_with_data(test_cleanup_func_with_data, void *) + __attribute__((__nonnull__)); + END_DECLS #endif /* TAP_BASIC_H */ diff --git a/src/external/c-tap-harness/tests/tap/float.c b/src/external/c-tap-harness/tests/tap/float.c index 67dd555..8fbd441 100644 --- a/src/external/c-tap-harness/tests/tap/float.c +++ b/src/external/c-tap-harness/tests/tap/float.c @@ -8,9 +8,9 @@ * otherwise care about floating point. * * This file is part of C TAP Harness. The current version plus supporting - * documentation is at . + * documentation is at . * - * Copyright 2008, 2010, 2012 Russ Allbery + * Copyright 2008, 2010, 2012-2019 Russ Allbery * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -29,13 +29,15 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT */ /* Required for isnan() and isinf(). */ #if defined(__STRICT_ANSI__) || defined(PEDANTIC) -# ifndef _XOPEN_SOURCE -# define _XOPEN_SOURCE 600 -# endif +# ifndef _XOPEN_SOURCE +# define _XOPEN_SOURCE 600 +# endif #endif #include @@ -46,22 +48,49 @@ #include /* - * Takes an expected double and a seen double and assumes the test passes if - * those two numbers are within delta of each other. + * Clang 4.0.1 gets very confused by this file and produces warnings about + * floating point implicit conversion from the isnan() and isinf() macros. + */ +#if defined(__llvm__) || defined(__clang__) +# pragma clang diagnostic ignored "-Wconversion" +# pragma clang diagnostic ignored "-Wdouble-promotion" +#endif + +/* + * Returns true if the two doubles are equal infinities, false otherwise. + * This requires a bit of machination since isinf is not required to return + * different values for positive and negative infinity, and we're trying to + * avoid direct comparisons between floating point numbers. + */ +static int +is_equal_infinity(double left, double right) +{ + if (!isinf(left) || !isinf(right)) + return 0; + return !!(left < 0) == !!(right < 0); +} + +/* + * Takes two doubles and requires they be within epsilon of each other. */ -void -is_double(double wanted, double seen, double epsilon, const char *format, ...) +int +is_double(double left, double right, double epsilon, const char *format, ...) { va_list args; + int success; va_start(args, format); fflush(stderr); - if ((isnan(wanted) && isnan(seen)) - || (isinf(wanted) && isinf(seen) && wanted == seen) - || fabs(wanted - seen) <= epsilon) + if ((isnan(left) && isnan(right)) || is_equal_infinity(left, right) + || fabs(left - right) <= epsilon) { + success = 1; okv(1, format, args); - else { - printf("# wanted: %g\n# seen: %g\n", wanted, seen); + } else { + success = 0; + diag(" left: %g", left); + diag("right: %g", right); okv(0, format, args); } + va_end(args); + return success; } diff --git a/src/external/c-tap-harness/tests/tap/float.h b/src/external/c-tap-harness/tests/tap/float.h index 7464535..bdf8451 100644 --- a/src/external/c-tap-harness/tests/tap/float.h +++ b/src/external/c-tap-harness/tests/tap/float.h @@ -2,9 +2,9 @@ * Floating point check function for the TAP protocol. * * This file is part of C TAP Harness. The current version plus supporting - * documentation is at . + * documentation is at . * - * Copyright 2008, 2010, 2012 Russ Allbery + * Copyright 2008, 2010, 2012, 2014, 2018 Russ Allbery * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -23,6 +23,8 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT */ #ifndef TAP_FLOAT_H @@ -32,9 +34,8 @@ BEGIN_DECLS -/* Check an expected value against a seen value within epsilon. */ -void is_double(double wanted, double seen, double epsilon, - const char *format, ...) +/* Compare two values within epsilon. */ +int is_double(double, double, double epsilon, const char *format, ...) __attribute__((__format__(printf, 4, 5))); END_DECLS diff --git a/src/external/c-tap-harness/tests/tap/libtap.sh b/src/external/c-tap-harness/tests/tap/libtap.sh index f9347d8..1827a68 100644 --- a/src/external/c-tap-harness/tests/tap/libtap.sh +++ b/src/external/c-tap-harness/tests/tap/libtap.sh @@ -7,11 +7,11 @@ # # This file provides a TAP-compatible shell function library useful for # writing test cases. It is part of C TAP Harness, which can be found at -# . +# . # -# Written by Russ Allbery -# Copyright 2009, 2010, 2011, 2012 Russ Allbery -# Copyright 2006, 2007, 2008 +# Written by Russ Allbery +# Copyright 2009-2012, 2016 Russ Allbery +# Copyright 2006-2008, 2013 # The Board of Trustees of the Leland Stanford Junior University # # Permission is hereby granted, free of charge, to any person obtaining a copy @@ -31,6 +31,8 @@ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +# +# SPDX-License-Identifier: MIT # Print out the number of test cases we expect to run. plan () { @@ -204,7 +206,7 @@ strip_colon_error() { # Bail out with an error message. bail () { echo 'Bail out!' "$@" - exit 1 + exit 255 } # Output a diagnostic on standard error, preceded by the required # mark. @@ -212,32 +214,32 @@ diag () { echo '#' "$@" } -# Search for the given file first in $BUILD and then in $SOURCE and echo the -# path where the file was found, or the empty string if the file wasn't -# found. +# Search for the given file first in $C_TAP_BUILD and then in $C_TAP_SOURCE +# and echo the path where the file was found, or the empty string if the file +# wasn't found. # # This macro uses puts, so don't run it using backticks inside double quotes # or bizarre quoting behavior will happen with Solaris sh. test_file_path () { - if [ -n "$BUILD" ] && [ -f "$BUILD/$1" ] ; then - puts "$BUILD/$1" - elif [ -n "$SOURCE" ] && [ -f "$SOURCE/$1" ] ; then - puts "$SOURCE/$1" + if [ -n "$C_TAP_BUILD" ] && [ -f "$C_TAP_BUILD/$1" ] ; then + puts "$C_TAP_BUILD/$1" + elif [ -n "$C_TAP_SOURCE" ] && [ -f "$C_TAP_SOURCE/$1" ] ; then + puts "$C_TAP_SOURCE/$1" else echo '' fi } -# Create $BUILD/tmp for use by tests for storing temporary files and return -# the path (via standard output). +# Create $C_TAP_BUILD/tmp for use by tests for storing temporary files and +# return the path (via standard output). # # This macro uses puts, so don't run it using backticks inside double quotes # or bizarre quoting behavior will happen with Solaris sh. test_tmpdir () { - if [ -z "$BUILD" ] ; then + if [ -z "$C_TAP_BUILD" ] ; then tap_tmpdir="./tmp" else - tap_tmpdir="$BUILD"/tmp + tap_tmpdir="$C_TAP_BUILD"/tmp fi if [ ! -d "$tap_tmpdir" ] ; then mkdir "$tap_tmpdir" || bail "Error creating $tap_tmpdir" diff --git a/src/external/c-tap-harness/tests/tap/macros.h b/src/external/c-tap-harness/tests/tap/macros.h index 33fee42..c2c8b5c 100644 --- a/src/external/c-tap-harness/tests/tap/macros.h +++ b/src/external/c-tap-harness/tests/tap/macros.h @@ -6,9 +6,9 @@ * everyone can pull them in. * * This file is part of C TAP Harness. The current version plus supporting - * documentation is at . + * documentation is at . * - * Copyright 2008, 2012 Russ Allbery + * Copyright 2008, 2012-2013, 2015 Russ Allbery * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -27,6 +27,8 @@ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. + * + * SPDX-License-Identifier: MIT */ #ifndef TAP_MACROS_H @@ -40,9 +42,9 @@ * the other attributes to work with GCC versions between 2.7 and 2.96. */ #ifndef __attribute__ -# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) -# define __attribute__(spec) /* empty */ -# endif +# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 96) +# define __attribute__(spec) /* empty */ +# endif #endif /* @@ -53,9 +55,18 @@ * variadic macro support. */ #if !defined(__attribute__) && !defined(__alloc_size__) -# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) -# define __alloc_size__(spec, args...) /* empty */ -# endif +# if defined(__GNUC__) && !defined(__clang__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 3) +# define __alloc_size__(spec, args...) /* empty */ +# endif +# endif +#endif + +/* Suppress __warn_unused_result__ if gcc is too old. */ +#if !defined(__attribute__) && !defined(__warn_unused_result__) +# if __GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 4) +# define __warn_unused_result__ /* empty */ +# endif #endif /* @@ -65,7 +76,7 @@ * compilation context, but there's no push and pop available. */ #if !defined(__attribute__) && (defined(__llvm__) || defined(__clang__)) -# pragma GCC diagnostic ignored "-Wattributes" +# pragma GCC diagnostic ignored "-Wattributes" #endif /* Used for unused parameters to silence gcc warnings. */ @@ -78,11 +89,11 @@ #undef BEGIN_DECLS #undef END_DECLS #ifdef __cplusplus -# define BEGIN_DECLS extern "C" { -# define END_DECLS } +# define BEGIN_DECLS extern "C" { +# define END_DECLS } #else -# define BEGIN_DECLS /* empty */ -# define END_DECLS /* empty */ +# define BEGIN_DECLS /* empty */ +# define END_DECLS /* empty */ #endif #endif /* TAP_MACROS_H */ -- 1.9.4