[v1.2.0] Merge branch 'bleeding'
This commit is contained in:
commit
ffb7e9390d
53 changed files with 1360 additions and 251 deletions
4
.ci/install-pip.sh
Executable file
4
.ci/install-pip.sh
Executable file
|
@ -0,0 +1,4 @@
|
|||
#!/bin/sh
|
||||
curl -O https://bootstrap.pypa.io/get-pip.py
|
||||
python3 get-pip.py
|
||||
rm -f get-pip.py
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -7,6 +7,7 @@
|
|||
!*.h
|
||||
!*.rst
|
||||
!samples/tests/*.sh
|
||||
!*.po
|
||||
|
||||
!LICENSE
|
||||
!HEADER
|
||||
|
|
|
@ -4,11 +4,11 @@ compiler:
|
|||
before_install:
|
||||
- sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get -qq install -y check gcc-4.9
|
||||
- sudo apt-get -qq install -y check gcc-4.9 gettext autopoint
|
||||
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 90
|
||||
- sudo pip install cpp-coveralls
|
||||
script:
|
||||
- ./autogen.sh && ./configure --enable-gcov CFLAGS="-g -O0" && make && make check
|
||||
- ./autogen.sh && ./configure --enable-gcov CFLAGS="-g -O0" && make && make -C samples check
|
||||
after_success:
|
||||
- coveralls --gcov gcov-4.9 --exclude samples --exclude dependencies --gcov-options '\-lp' -b .
|
||||
after_failure:
|
||||
|
|
13
Makefile.am
13
Makefile.am
|
@ -1,5 +1,6 @@
|
|||
ACLOCAL_AMFLAGS = -I m4
|
||||
SUBDIRS = dependencies/csptr samples
|
||||
AM_CPPFLAGS = -DLOCALEDIR='"$(localedir)"'
|
||||
SUBDIRS = po dependencies/csptr samples
|
||||
|
||||
lib_LTLIBRARIES = libcriterion.la
|
||||
|
||||
|
@ -14,16 +15,17 @@ libcriterion_la_CFLAGS = \
|
|||
-I$(top_srcdir)/dependencies/csptr/include/ \
|
||||
$(COVERAGE_CFLAGS)
|
||||
|
||||
libcriterion_la_LDFLAGS = $(COVERAGE_LDFLAGS)
|
||||
libcriterion_la_LDFLAGS = $(COVERAGE_LDFLAGS) -version-info 1:0:0
|
||||
|
||||
# dirty but unless someone has a better alternative...
|
||||
libcriterion_la_LIBADD = dependencies/csptr/src/libcsptr_la-*.lo
|
||||
|
||||
EXTRA_DIST = LICENSE
|
||||
EXTRA_DIST = config.rpath LICENSE
|
||||
|
||||
subdirincludedir = $(includedir)/criterion/
|
||||
subdirinclude_HEADERS = \
|
||||
include/criterion/assert.h \
|
||||
include/criterion/abort.h \
|
||||
include/criterion/common.h \
|
||||
include/criterion/criterion.h \
|
||||
include/criterion/event.h \
|
||||
|
@ -35,6 +37,8 @@ subdirinclude_HEADERS = \
|
|||
include/criterion/stats.h
|
||||
|
||||
libcriterion_la_SOURCES = \
|
||||
src/abort.c \
|
||||
src/abort.h \
|
||||
src/event.c \
|
||||
src/event.h \
|
||||
src/report.c \
|
||||
|
@ -51,7 +55,10 @@ libcriterion_la_SOURCES = \
|
|||
src/options.c \
|
||||
src/timer.c \
|
||||
src/timer.h \
|
||||
src/i18n.c \
|
||||
src/i18n.h \
|
||||
src/ordered-set.c \
|
||||
src/posix-compat.c \
|
||||
src/main.c
|
||||
|
||||
TARGET = $(PACKAGE)-$(VERSION)
|
||||
|
|
49
README.md
49
README.md
|
@ -2,8 +2,8 @@
|
|||
Criterion
|
||||
=========
|
||||
|
||||
[](https://travis-ci.org/Snaipe/Criterion)
|
||||
[](https://coveralls.io/r/Snaipe/Criterion?branch=master)
|
||||
[](https://travis-ci.org/Snaipe/Criterion)
|
||||
[](https://coveralls.io/r/Snaipe/Criterion?branch=bleeding)
|
||||
[](https://github.com/Snaipe/Criterion/blob/master/LICENSE)
|
||||
[](https://github.com/Snaipe/Criterion/releases)
|
||||
|
||||
|
@ -31,14 +31,14 @@ the user would have with other frameworks:
|
|||
reported and tested.
|
||||
* [x] Progress and statistics can be followed in real time with report hooks.
|
||||
* [x] TAP output format can be enabled with an option.
|
||||
* [x] Runs on Linux, FreeBSD, Mac OS X, and Windows (compiles only with Cygwin for the moment).
|
||||
* [x] Runs on Linux, FreeBSD, Mac OS X, and Windows (Compiles only with MinGW or Cygwin).
|
||||
|
||||
## Downloads
|
||||
|
||||
* [Linux (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v1.1.0/criterion-1.1.0-linux-x86_64.tar.bz2)
|
||||
* [OS X (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v1.1.0/criterion-1.1.0-osx-x86_64.tar.bz2)
|
||||
* [Windows (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v1.1.0/criterion-1.1.0-win-x86_64.tar.bz2)
|
||||
* [FreeBSD (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v1.1.0/criterion-1.1.0-freebsd-x86_64.tar.bz2)
|
||||
* [Linux (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v1.2.0/criterion-1.2.0-linux-x86_64.tar.bz2)
|
||||
* [OS X (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v1.2.0/criterion-1.2.0-osx-x86_64.tar.bz2)
|
||||
* [Windows (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v1.2.0/criterion-1.2.0-win-x86_64.tar.bz2)
|
||||
* [FreeBSD (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v1.2.0/criterion-1.2.0-freebsd-x86_64.tar.bz2)
|
||||
|
||||
If you have a different platform, you can still [build the library from source](http://criterion.readthedocs.org/en/latest/setup.html#installation)
|
||||
|
||||
|
@ -58,6 +58,34 @@ Sample tests can be found in the [sample directory][samples].
|
|||
* [Tests with signals][sample-signal]
|
||||
* [Using report hooks][sample-report]
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcomed, but must follow a simple set of rules in order to
|
||||
be merged.
|
||||
|
||||
**Please follow these conventions if you want your pull request(s) accepted.**
|
||||
|
||||
### General
|
||||
|
||||
* Use 4 (four) spaces for indentation.
|
||||
* No trailing whitespaces.
|
||||
* 80 chars column limit.
|
||||
* No trash files. Trash files are by-products of the compilation process, or
|
||||
generated files that does not need to be under version control.
|
||||
* Pull requests must compile and work properly.
|
||||
* Pull requests must be mergeable automatically.
|
||||
* Number of commits in a pull request should be kept to one commit and all
|
||||
additional commits must be squashed.
|
||||
* You may have more than one commit in a pull request if the commits are
|
||||
separate changes, otherwise squash them.
|
||||
|
||||
### Translations
|
||||
|
||||
* You can contribute new translation files for output messages, on the
|
||||
condition that you are fluent with the language itself.
|
||||
* Each correction on existing translations must be followed by a
|
||||
rationale ("why would the translation be better if the change is applied?")
|
||||
|
||||
## F.A.Q.
|
||||
|
||||
**Q. What's wrong with other test frameworks?**
|
||||
|
@ -72,12 +100,7 @@ A. I worked with CUnit and Check, and I must say that they do their job
|
|||
**Q. Where has this been tested?**
|
||||
A. Currently, on Linux 2.6.32 and Linux 3.15.7, although it should work on
|
||||
most \*nix systems; Mac OS X Yosemite 10.10, FreeBSD 10.0, and finally
|
||||
Windows 7 (with the Cygwin port of GCC).
|
||||
|
||||
**Q. Can I use it on Windows without Cygwin?**
|
||||
A. Yes, you can, Cygwin is only required to compile the static library if
|
||||
you build it from source -- otherwise, a GNU C compatible compiler (like
|
||||
GCC or Clang) is needed to compile your tests with the library.
|
||||
Windows 7 (with the MinGW and Cygwin ports of GCC).
|
||||
|
||||
[online-docs]: http://criterion.readthedocs.org/
|
||||
[pdf-docs]: http://readthedocs.org/projects/criterion/downloads/pdf/latest/
|
||||
|
|
17
appveyor.yml
17
appveyor.yml
|
@ -14,9 +14,10 @@ init:
|
|||
-P mingw-gcc-core \
|
||||
-P mingw-pthreads \
|
||||
-P mingw-w32api \
|
||||
-P mingw64-i686-gcc-core \
|
||||
-P libtool \
|
||||
-P make \
|
||||
-P python \
|
||||
-P python3 \
|
||||
-P gettext-devel \
|
||||
-P gettext \
|
||||
-P expat \
|
||||
|
@ -27,7 +28,6 @@ init:
|
|||
-P git \
|
||||
-P wget \
|
||||
-P curl
|
||||
- "%CYG_BASH% -lc 'echo $PATH'"
|
||||
|
||||
environment:
|
||||
global:
|
||||
|
@ -35,6 +35,8 @@ environment:
|
|||
CYG_MIRROR: http://cygwin.mirror.constant.com
|
||||
CYG_CACHE: C:\cygwin\var\cache\setup
|
||||
CYG_BASH: C:\cygwin\bin\bash
|
||||
COVERALLS_TOKEN:
|
||||
secure: 5nuCg+faxFPeppoNNcSwVobswAVFUf8ut83vw8CX/4W2y0kZkGmwEfCUxSQWiQDU
|
||||
|
||||
cache:
|
||||
- '%CYG_CACHE%'
|
||||
|
@ -46,22 +48,27 @@ matrix:
|
|||
|
||||
platform:
|
||||
- x86
|
||||
- Any CPU
|
||||
|
||||
configuration: Release
|
||||
|
||||
install:
|
||||
- 'set GCOV_PREFIX=%APPVEYOR_BUILD_FOLDER%'
|
||||
- "%CYG_BASH% -lc 'cd $APPVEYOR_BUILD_FOLDER; ./autogen.sh'"
|
||||
- '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; ./configure"'
|
||||
- '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; ./configure CC=i686-w64-mingw32-gcc CFLAGS=\"-g -O0\" --enable-gcov"'
|
||||
|
||||
build_script:
|
||||
- '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; make"'
|
||||
|
||||
before_test:
|
||||
- "%CYG_BASH% -lc 'cd $APPVEYOR_BUILD_FOLDER; .ci/install-pip.sh'"
|
||||
- "%CYG_BASH% -lc 'pip install cpp-coveralls'"
|
||||
|
||||
test_script:
|
||||
- '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; make check"'
|
||||
- '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER/samples; exec 0</dev/null; make check"'
|
||||
|
||||
after_test:
|
||||
- '%CYG_BASH% -lc "cat $(find $APPVEYOR_BUILD_FOLDER/samples -iname \"*.log\") /dev/null'
|
||||
- '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; coveralls --gcov i686-w64-mingw32-gcov --exclude samples --exclude dependencies --gcov-options \"\-lp\" -b . -t $COVERALLS_TOKEN'
|
||||
|
||||
notifications:
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#!/bin/sh
|
||||
git submodule update --init --recursive
|
||||
autopoint
|
||||
autoreconf -i
|
||||
|
|
14
configure.ac
14
configure.ac
|
@ -1,6 +1,6 @@
|
|||
AC_PREREQ([2.60])
|
||||
|
||||
AC_INIT([criterion], [1.1.0], [], [criterion], [franklinmathieu@gmail.com])
|
||||
AC_INIT([criterion], [1.2.0], [], [criterion], [franklinmathieu@gmail.com])
|
||||
AC_CONFIG_SRCDIR([src/runner.c])
|
||||
|
||||
LT_PREREQ([2.2.4])
|
||||
|
@ -20,6 +20,16 @@ AC_PROG_LN_S
|
|||
AC_PROG_MAKE_SET
|
||||
AC_SUBST([LIBTOOL_DEPS])
|
||||
|
||||
AC_FUNC_FNMATCH
|
||||
|
||||
enable_rt_tests="no"
|
||||
AC_CHECK_LIB([rt], [clock_gettime], [enable_rt_tests="yes"])
|
||||
|
||||
AM_CONDITIONAL([ENABLE_RT_TESTS], [test "x$enable_rt_tests" != "xno"])
|
||||
|
||||
AM_GNU_GETTEXT([external])
|
||||
AM_GNU_GETTEXT_VERSION([0.18])
|
||||
|
||||
AC_ARG_ENABLE([gcov],
|
||||
[AS_HELP_STRING([--enable-gcov],
|
||||
[Compile the project with converage enabled])],
|
||||
|
@ -31,7 +41,7 @@ AC_ARG_ENABLE([gcov],
|
|||
[])
|
||||
|
||||
AC_CONFIG_HEADERS([src/config.h])
|
||||
AC_CONFIG_FILES([Makefile samples/Makefile])
|
||||
AC_CONFIG_FILES([Makefile samples/Makefile po/Makefile.in])
|
||||
|
||||
AC_CONFIG_SUBDIRS([dependencies/csptr])
|
||||
|
||||
|
|
2
dependencies/csptr
vendored
2
dependencies/csptr
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 15b825ffb8ffe7309bf524659eb900d6bb1d7c04
|
||||
Subproject commit 4f3e63aca586939ed734f4e76c4f7f7f8c07d247
|
|
@ -39,7 +39,7 @@ copyright = u'2015, Franklin "Snaipe" Mathieu'
|
|||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '1.1.0'
|
||||
version = '1.2.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ Command line arguments
|
|||
* ``-f or --fail-fast``: Exit after the first test failure.
|
||||
* ``--ascii``: Don't use fancy unicode symbols or colors in the output.
|
||||
* ``--pattern [PATTERN]``: Run tests whose string identifier matches
|
||||
the given shell wildcard pattern (see dedicated section below).
|
||||
the given shell wildcard pattern (see dedicated section below). (\*nix only)
|
||||
* ``--no-early-exit``: The test workers shall not prematurely exit when done and
|
||||
will properly return from the main, cleaning up their process space.
|
||||
This is useful when tracking memory leaks with ``valgrind --tool=memcheck``.
|
||||
|
@ -27,7 +27,8 @@ Shell Wildcard Pattern
|
|||
----------------------
|
||||
|
||||
Patterns in criterion are matched against a test's string identifier with
|
||||
``fnmatch``.
|
||||
``fnmatch``. This feature is only available on \*nix systems where ``fnmatch``
|
||||
is provided.
|
||||
|
||||
Special characters used in shell-style wildcard patterns are:
|
||||
|
||||
|
@ -61,4 +62,4 @@ Environment variables are alternatives to command line switches when set to 1.
|
|||
* ``CRITERION_VERBOSITY_LEVEL``: Same as ``--verbose``. Sets the verbosity level
|
||||
to its value.
|
||||
* ``CRITERION_TEST_PATTERN``: Same as ``--pattern``. Sets the test pattern
|
||||
to its value.
|
||||
to its value. (\*nix only)
|
||||
|
|
|
@ -14,4 +14,5 @@ There are plenty of ways to fix that behaviour:
|
|||
**Q. I'm having an issue with the library, what can I do ?**
|
||||
|
||||
A. Open a new issue on the `github issue tracker <https://github.com/Snaipe/Criterion/issues>`_,
|
||||
and describe the problem you are experiencing.
|
||||
and describe the problem you are experiencing, along with the platform you are
|
||||
running criterion on.
|
||||
|
|
|
@ -17,6 +17,10 @@ A report hook can be declared using the ``ReportHook`` macro:
|
|||
The macro takes a Phase parameter that indicates the phase at which the function
|
||||
shall be run. Valid phases are described below.
|
||||
|
||||
**Note**: there are no guarantees regarding the order of execution of report hooks
|
||||
on the same phase. In other words, all report hooks of a specific phase could
|
||||
be executed in any order.
|
||||
|
||||
Testing Phases
|
||||
--------------
|
||||
|
||||
|
@ -49,3 +53,18 @@ Valid types for each phases are:
|
|||
* ``struct criterion_test_stats *`` for ``POST_TEST``, ``POST_FINI``, and ``TEST_CRASH``.
|
||||
* ``struct criterion_suite_stats *`` for ``POST_SUITE``.
|
||||
* ``struct criterion_global_stats *`` for ``POST_ALL``.
|
||||
|
||||
For instance, these are valid report hook declarations for the ``PRE_TEST`` phase:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include <criterion/criterion.h>
|
||||
#include <criterion/hooks.h>
|
||||
|
||||
ReportHook(PRE_TEST)() {
|
||||
// not using the parameter
|
||||
}
|
||||
|
||||
ReportHook(PRE_TEST)(struct criterion_test *test) {
|
||||
// using the parameter
|
||||
}
|
||||
|
|
|
@ -9,4 +9,5 @@ Criterion
|
|||
starter
|
||||
hooks
|
||||
env
|
||||
internal
|
||||
faq
|
||||
|
|
54
doc/internal.rst
Normal file
54
doc/internal.rst
Normal file
|
@ -0,0 +1,54 @@
|
|||
Changing the internals
|
||||
======================
|
||||
|
||||
Providing your own main
|
||||
-----------------------
|
||||
|
||||
If you are not satisfied with the default CLI or environment variables, you
|
||||
can define your own main function.
|
||||
|
||||
Configuring the test runner
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You'd usually want to configure the test runner before calling it.
|
||||
Configuration is done by setting fields in a global variable named
|
||||
``criterion_options`` (include criterion/options.h).
|
||||
|
||||
Here is an exhaustive list of these fields:
|
||||
|
||||
=================== ================================== ==============================================================
|
||||
Field Type Description
|
||||
=================== ================================== ==============================================================
|
||||
logging_threshold enum criterion_logging_level The logging level
|
||||
------------------- ---------------------------------- --------------------------------------------------------------
|
||||
output_provider struct criterion_output_provider * The output provider (see below)
|
||||
------------------- ---------------------------------- --------------------------------------------------------------
|
||||
no_early_exit bool True iff the test worker should exit early
|
||||
------------------- ---------------------------------- --------------------------------------------------------------
|
||||
always_succeed bool True iff criterion_run_all_tests should always returns 1
|
||||
------------------- ---------------------------------- --------------------------------------------------------------
|
||||
use_ascii bool True iff the outputs should use the ASCII charset
|
||||
------------------- ---------------------------------- --------------------------------------------------------------
|
||||
fail_fast bool True iff the test runner should abort after the first failure
|
||||
------------------- ---------------------------------- --------------------------------------------------------------
|
||||
pattern const char * The pattern of the tests that should be executed
|
||||
=================== ================================== ==============================================================
|
||||
|
||||
Starting the test runner
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The test runner can be called with ``criterion_run_all_tests``. The function
|
||||
returns 0 if one test or more failed, 1 otherwise.
|
||||
|
||||
Implementing your own output provider
|
||||
-------------------------------------
|
||||
|
||||
In case you are not satisfied by the default output provider, you can implement
|
||||
yours. To do so, simply set the ``output_provider`` option to your custom
|
||||
output provider.
|
||||
|
||||
Each function contained in the structure is called during one of the standard
|
||||
phase of the criterion runner.
|
||||
|
||||
For more insight on how to implement this, see other existing output providers
|
||||
in ``src/log/``.
|
113
doc/starter.rst
113
doc/starter.rst
|
@ -50,26 +50,52 @@ parameter, and an optional failure message:
|
|||
|
||||
On top of those, more assertions are available for common operations:
|
||||
|
||||
* ``{assert,expect}_not(Actual, Expected, [Message])``
|
||||
* ``{assert,expect}_eq(Actual, Expected, [Message])``
|
||||
* ``{assert,expect}_neq(Actual, Unexpected, [Message])``
|
||||
* ``{assert,expect}_lt(Actual, Expected, [Message])``
|
||||
* ``{assert,expect}_leq(Actual, Expected, [Message])``
|
||||
* ``{assert,expect}_gt(Actual, Expected, [Message])``
|
||||
* ``{assert,expect}_geq(Actual, Expected, [Message])``
|
||||
* ``{assert,expect}_float_eq(Actual, Expected, Epsilon, [Message])``
|
||||
* ``{assert,expect}_float_neq(Actual, Unexpected, Epsilon, [Message])``
|
||||
* ``{assert,expect}_strings_eq(Actual, Expected, [Message])``
|
||||
* ``{assert,expect}_strings_neq(Actual, Unexpected, [Message])``
|
||||
* ``{assert,expect}_strings_lt(Actual, Expected, [Message])``
|
||||
* ``{assert,expect}_strings_leq(Actual, Expected, [Message])``
|
||||
* ``{assert,expect}_strings_gt(Actual, Expected, [Message])``
|
||||
* ``{assert,expect}_strings_geq(Actual, Expected, [Message])``
|
||||
* ``{assert,expect}_arrays_eq(Actual, Expected, Size, [Message])``
|
||||
* ``{assert,expect}_arrays_neq(Actual, Unexpected, Size, [Message])``
|
||||
* ``assert_null(Ptr, [Message])``: passes if Ptr is NULL.
|
||||
* ``assert_eq(Actual, Expected, [Message])``: passes if Actual == Expected.
|
||||
* ``assert_lt(Actual, Expected, [Message])``: passes if Actual < Expected.
|
||||
* ``assert_leq(Actual, Expected, [Message])``: passes if Actual <= Expected.
|
||||
* ``assert_gt(Actual, Expected, [Message])``: passes if Actual > Expected.
|
||||
* ``assert_geq(Actual, Expected, [Message])``: passes if Actual >= Expected.
|
||||
* ``assert_float_eq(Actual, Expected, Epsilon, [Message])``:
|
||||
passes if Actual == Expected with an error of Epsilon.
|
||||
* ``assert_arrays_eq(Actual, Expected, Size, [Message])``:
|
||||
passes if all elements of Actual (from 0 to Size - 1) are equals to those
|
||||
of Expected.
|
||||
* ``assert_arrays_eq_cmp(Actual, Expected, Size, Cmp, [Message])``:
|
||||
Same as ``arrays_eq`` but equality is defined by the result of the binary
|
||||
Cmp function.
|
||||
|
||||
Equality and lexical comparison assertions are also available for strings:
|
||||
|
||||
* ``assert_strings_eq(Actual, Expected, [Message])``
|
||||
* ``assert_strings_lt(Actual, Expected, [Message])``
|
||||
* ``assert_strings_leq(Actual, Expected, [Message])``
|
||||
* ``assert_strings_gt(Actual, Expected, [Message])``
|
||||
* ``assert_strings_geq(Actual, Expected, [Message])``
|
||||
|
||||
And some assertions have a logical negative counterpart:
|
||||
|
||||
* ``assert_not(Condition, [Message])``
|
||||
* ``assert_not_null(Ptr, [Message])``
|
||||
* ``assert_neq(Actual, Unexpected, [Message])``
|
||||
* ``assert_float_neq(Actual, Unexpected, Epsilon, [Message])``
|
||||
* ``assert_strings_neq(Actual, Unexpected, [Message])``
|
||||
* ``assert_arrays_neq(Actual, Unexpected, Size, [Message])``
|
||||
* ``assert_arrays_neq_cmp(Actual, Unexpected, Size, Cmp, [Message])``
|
||||
|
||||
Of course, every ``assert`` has an ``expect`` counterpart.
|
||||
|
||||
Please note that ``arrays_(n)eq`` assertions should not be used on padded
|
||||
structures -- please use ``arrays_(n)eq_cmp`` instead.
|
||||
|
||||
Configuring tests
|
||||
-----------------
|
||||
|
||||
Tests may receive optional configuration parameters to alter their behaviour
|
||||
or provide additional metadata.
|
||||
|
||||
Fixtures
|
||||
--------
|
||||
~~~~~~~~
|
||||
|
||||
Tests that need some setup and teardown can register functions that will
|
||||
run before and after the test function:
|
||||
|
@ -91,8 +117,13 @@ run before and after the test function:
|
|||
// test contents
|
||||
}
|
||||
|
||||
If a setup crashes, you will get a warning message, and the test will be aborted
|
||||
and marked as a failure.
|
||||
Is a teardown crashes, you will get a warning message, and the test will keep
|
||||
its result.
|
||||
|
||||
Testing signals
|
||||
---------------
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
If a test receives a signal, it will by default be marked as a failure.
|
||||
You can, however, expect a test to only pass if a special kind of signal
|
||||
|
@ -115,3 +146,47 @@ is received:
|
|||
int *ptr = NULL;
|
||||
*ptr = 42;
|
||||
}
|
||||
|
||||
This feature will of course not work on Windows.
|
||||
|
||||
Configuration reference
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Here is an exhaustive list of all possible configuration parameters you can
|
||||
pass:
|
||||
|
||||
============= =============== ==============================================================
|
||||
Parameter Type Description
|
||||
============= =============== ==============================================================
|
||||
.description const char * Adds a description. Cannot be ``NULL``.
|
||||
------------- --------------- --------------------------------------------------------------
|
||||
.init void (*)(void) Adds a setup function the be executed before the test.
|
||||
------------- --------------- --------------------------------------------------------------
|
||||
.fini void (*)(void) Adds a teardown function the be executed after the test.
|
||||
------------- --------------- --------------------------------------------------------------
|
||||
.disabled bool Disables the test.
|
||||
------------- --------------- --------------------------------------------------------------
|
||||
.signal int Expect the test to raise the specified signal.
|
||||
============= =============== ==============================================================
|
||||
|
||||
Setting up suite-wise configuration
|
||||
-----------------------------------
|
||||
|
||||
Tests under the same suite can have a suite-wise configuration -- this is done
|
||||
using the ``TestSuite`` macro:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#include <criterion/criterion.h>
|
||||
|
||||
TestSuite(suite_name, [params...]);
|
||||
|
||||
Test(suite_name, test_1) {
|
||||
}
|
||||
|
||||
Test(suite_name, test_2) {
|
||||
}
|
||||
|
||||
Configuration parameters are the same as above, but applied to the suite itself.
|
||||
|
||||
Suite fixtures are run *along with* test fixtures.
|
||||
|
|
31
include/criterion/abort.h
Normal file
31
include/criterion/abort.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright © 2015 Franklin "Snaipe" Mathieu <http://snai.pe/>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef CRITERION_ABORT_H_
|
||||
# define CRITERION_ABORT_H_
|
||||
|
||||
# include "common.h"
|
||||
|
||||
NORETURN void criterion_abort_test(void);
|
||||
|
||||
#endif /* !CRITERION_ABORT_H_ */
|
|
@ -31,6 +31,7 @@
|
|||
# include "stats.h"
|
||||
# include "hooks.h"
|
||||
# include "event.h"
|
||||
# include "abort.h"
|
||||
|
||||
enum criterion_assert_kind {
|
||||
NORMAL,
|
||||
|
@ -60,7 +61,7 @@ struct criterion_assert_args {
|
|||
}; \
|
||||
send_event(ASSERT, &stat, sizeof (stat)); \
|
||||
if (!passed && (Kind) == FATAL) \
|
||||
return; \
|
||||
criterion_abort_test(); \
|
||||
} while (0)
|
||||
|
||||
// Common asserts
|
||||
|
@ -110,6 +111,22 @@ struct criterion_assert_args {
|
|||
# define assert_geq(...) assert_op_(>=, __VA_ARGS__, .sentinel_ = 0)
|
||||
# define expect_geq(...) expect_op_(>=, __VA_ARGS__, .sentinel_ = 0)
|
||||
|
||||
# define assert_null_(Value, ...) \
|
||||
assert_impl(FATAL, (Value) == NULL, __VA_ARGS__)
|
||||
# define expect_null_(Value, ...) \
|
||||
assert_impl(NORMAL, (Value) == NULL, __VA_ARGS__)
|
||||
|
||||
# define assert_null(...) assert_null_(__VA_ARGS__, .sentinel_ = 0)
|
||||
# define expect_null(...) expect_null_(__VA_ARGS__, .sentinel_ = 0)
|
||||
|
||||
# define assert_not_null_(Value, ...) \
|
||||
assert_impl(FATAL, (Value) != NULL, __VA_ARGS__)
|
||||
# define expect_not_null_(Value, ...) \
|
||||
assert_impl(NORMAL, (Value) != NULL, __VA_ARGS__)
|
||||
|
||||
# define assert_not_null(...) assert_not_null_(__VA_ARGS__, .sentinel_ = 0)
|
||||
# define expect_not_null(...) expect_not_null_(__VA_ARGS__, .sentinel_ = 0)
|
||||
|
||||
// Floating-point asserts
|
||||
|
||||
# define assert_float_eq(...) \
|
||||
|
@ -172,23 +189,82 @@ struct criterion_assert_args {
|
|||
// Array asserts
|
||||
|
||||
# define assert_arrays_eq(...) \
|
||||
assert_arrays_eq_(__VA_ARGS__, .sentinel = 0)
|
||||
assert_arrays_eq_(__VA_ARGS__, .sentinel_ = 0)
|
||||
# define expect_arrays_eq(...) \
|
||||
expect_arrays_eq_(__VA_ARGS__, .sentinel = 0)
|
||||
expect_arrays_eq_(__VA_ARGS__, .sentinel_ = 0)
|
||||
|
||||
# define assert_arrays_neq(...) \
|
||||
assert_arrays_neq_(__VA_ARGS__, .sentinel = 0)
|
||||
assert_arrays_neq_(__VA_ARGS__, .sentinel_ = 0)
|
||||
# define expect_arrays_neq(...) \
|
||||
expect_arrays_neq_(__VA_ARGS__, .sentinel = 0)
|
||||
expect_arrays_neq_(__VA_ARGS__, .sentinel_ = 0)
|
||||
|
||||
# define assert_arrays_eq_(A, B, Size, ...) \
|
||||
assert_impl(FATAL, !memcmp((A), (B), (Size)), __VA_ARGS__)
|
||||
# define assert_arrays_eq_(A, B, Size, ...) \
|
||||
assert_impl(FATAL, !memcmp((A), (B), (Size)), \
|
||||
.default_msg = "Arrays are not equal.", \
|
||||
__VA_ARGS__)
|
||||
# define expect_arrays_eq_(A, B, Size, ...) \
|
||||
assert_impl(NORMAL, !memcmp((A), (B), (Size)), __VA_ARGS__)
|
||||
assert_impl(NORMAL, !memcmp((A), (B), (Size)), \
|
||||
.default_msg = "Arrays are not equal.", \
|
||||
__VA_ARGS__)
|
||||
|
||||
# define assert_arrays_neq_(A, B, Size, ...) \
|
||||
assert_impl(FATAL, memcmp((A), (B), (Size)), __VA_ARGS__)
|
||||
# define expect_arrays_neq_(A, B, Size, ...) \
|
||||
assert_impl(NORMAL, memcmp((A), (B), (Size)), __VA_ARGS__)
|
||||
# define assert_arrays_neq_(A, B, Size, ...) \
|
||||
assert_impl(FATAL, memcmp((A), (B), (Size)), \
|
||||
.default_msg = "Arrays are equal", \
|
||||
__VA_ARGS__)
|
||||
# define expect_arrays_neq_(A, B, Size, ...) \
|
||||
assert_impl(NORMAL, memcmp((A), (B), (Size)), \
|
||||
.default_msg = "Arrays are equal", \
|
||||
__VA_ARGS__)
|
||||
|
||||
# ifdef __GNUC__
|
||||
# define CRIT_ARR_COMPARE_(A, B, Size, Cmp, Result) \
|
||||
__typeof__(&(A)[0]) first = (A); \
|
||||
__typeof__(&(B)[0]) second = (B); \
|
||||
int equals = 1; \
|
||||
for (size_t i = 0, size = (Size); equals && i < size; ++i) \
|
||||
equals = equals && !Cmp(first + i, second + i)
|
||||
|
||||
# define assert_arrays_eq_cmp_(A, B, Size, Cmp, ...) \
|
||||
do { \
|
||||
CRIT_ARR_COMPARE_(A, B, Size, Cmp, equals); \
|
||||
assert_impl(FATAL, equals, \
|
||||
.default_msg = "Arrays are not equal", \
|
||||
__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
# define expect_arrays_eq_cmp_(A, B, Size, Cmp, ...) \
|
||||
do { \
|
||||
CRIT_ARR_COMPARE_(A, B, Size, Cmp, equals); \
|
||||
assert_impl(NORMAL, equals, \
|
||||
.default_msg = "Arrays are not equal", \
|
||||
__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
# define assert_arrays_eq_cmp(...) \
|
||||
assert_arrays_eq_cmp_(__VA_ARGS__, .sentinel_ = 0)
|
||||
# define expect_arrays_eq_cmp(...) \
|
||||
expect_arrays_eq_cmp_(__VA_ARGS__, .sentinel_ = 0)
|
||||
|
||||
# define assert_arrays_neq_cmp_(A, B, Size, Cmp, ...) \
|
||||
do { \
|
||||
CRIT_ARR_COMPARE_(A, B, Size, Cmp, equals); \
|
||||
assert_impl(FATAL, !equals, \
|
||||
.default_msg = "Arrays not equal", \
|
||||
__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
# define expect_arrays_neq_cmp_(A, B, Size, Cmp, ...) \
|
||||
do { \
|
||||
CRIT_ARR_COMPARE_(A, B, Size, Cmp, equals); \
|
||||
assert_impl(NORMAL, equals, \
|
||||
.default_msg = "Arrays not equal", \
|
||||
__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
# define assert_arrays_neq_cmp(...) \
|
||||
assert_arrays_eq_cmp_(__VA_ARGS__, .sentinel_ = 0)
|
||||
# define expect_arrays_neq_cmp(...) \
|
||||
expect_arrays_eq_cmp_(__VA_ARGS__, .sentinel_ = 0)
|
||||
# endif /* !__GNUC__ */
|
||||
|
||||
#endif /* !CRITERION_ASSERT_H_ */
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
Type *const SECTION_END(Name) = &SECTION_END_(Name)
|
||||
|
||||
# define UNUSED __attribute__((unused))
|
||||
# define NORETURN __attribute__((noreturn))
|
||||
|
||||
# ifdef _WIN32
|
||||
# define SIZE_T_FORMAT "%Iu"
|
||||
|
|
|
@ -25,8 +25,9 @@
|
|||
# define CRITERION_EVENT_H_
|
||||
|
||||
# include <stddef.h>
|
||||
# include <stdio.h>
|
||||
|
||||
extern int EVENT_PIPE;
|
||||
extern FILE *g_event_pipe;
|
||||
|
||||
void send_event(int kind, void *data, size_t size);
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
# define CRITERION_LOGGING_H_
|
||||
|
||||
# include <stdbool.h>
|
||||
# include <stdarg.h>
|
||||
# include "common.h"
|
||||
# include "ordered-set.h"
|
||||
# include "stats.h"
|
||||
|
@ -34,23 +35,73 @@ enum criterion_logging_level {
|
|||
CRITERION_IMPORTANT,
|
||||
};
|
||||
|
||||
enum criterion_logging_prefix {
|
||||
CRITERION_LOGGING_PREFIX_DASHES,
|
||||
CRITERION_LOGGING_PREFIX_EQUALS,
|
||||
CRITERION_LOGGING_PREFIX_RUN,
|
||||
CRITERION_LOGGING_PREFIX_SKIP,
|
||||
CRITERION_LOGGING_PREFIX_PASS,
|
||||
CRITERION_LOGGING_PREFIX_FAIL,
|
||||
};
|
||||
|
||||
struct criterion_prefix_data {
|
||||
const char *prefix;
|
||||
const char *color;
|
||||
};
|
||||
|
||||
# ifdef CRITERION_LOGGING_COLORS
|
||||
# define CRIT_COLOR_NORMALIZE(Str) (criterion_options.use_ascii ? "" : Str)
|
||||
|
||||
# define CRIT_FG_BOLD "\e[0;1m"
|
||||
# define CRIT_FG_RED "\e[0;31m"
|
||||
# define CRIT_FG_GREEN "\e[0;32m"
|
||||
# define CRIT_FG_GOLD "\e[0;33m"
|
||||
# define CRIT_FG_BLUE "\e[0;34m"
|
||||
# define CRIT_RESET "\e[0m"
|
||||
|
||||
# define FG_BOLD CRIT_COLOR_NORMALIZE(CRIT_FG_BOLD)
|
||||
# define FG_RED CRIT_COLOR_NORMALIZE(CRIT_FG_RED)
|
||||
# define FG_GREEN CRIT_COLOR_NORMALIZE(CRIT_FG_GREEN)
|
||||
# define FG_GOLD CRIT_COLOR_NORMALIZE(CRIT_FG_GOLD)
|
||||
# define FG_BLUE CRIT_COLOR_NORMALIZE(CRIT_FG_BLUE)
|
||||
# define RESET CRIT_COLOR_NORMALIZE(CRIT_RESET)
|
||||
# endif
|
||||
|
||||
extern const struct criterion_prefix_data g_criterion_logging_prefixes[];
|
||||
|
||||
# define CRITERION_PREFIX_DASHES (&g_criterion_logging_prefixes[CRITERION_LOGGING_PREFIX_DASHES])
|
||||
# define CRITERION_PREFIX_EQUALS (&g_criterion_logging_prefixes[CRITERION_LOGGING_PREFIX_EQUALS])
|
||||
# define CRITERION_PREFIX_RUN (&g_criterion_logging_prefixes[CRITERION_LOGGING_PREFIX_RUN ])
|
||||
# define CRITERION_PREFIX_SKIP (&g_criterion_logging_prefixes[CRITERION_LOGGING_PREFIX_SKIP ])
|
||||
# define CRITERION_PREFIX_PASS (&g_criterion_logging_prefixes[CRITERION_LOGGING_PREFIX_PASS ])
|
||||
# define CRITERION_PREFIX_FAIL (&g_criterion_logging_prefixes[CRITERION_LOGGING_PREFIX_FAIL ])
|
||||
|
||||
void criterion_vlog(enum criterion_logging_level level, const char *msg, va_list args);
|
||||
|
||||
FORMAT(printf, 3, 4)
|
||||
void criterion_plog(enum criterion_logging_level level, const struct criterion_prefix_data *prefix, const char *msg, ...);
|
||||
|
||||
FORMAT(printf, 2, 3)
|
||||
void criterion_log(enum criterion_logging_level level, const char *msg, ...);
|
||||
|
||||
# define criterion_info(...) criterion_log(CRITERION_INFO, __VA_ARGS__)
|
||||
# define criterion_important(...) criterion_log(CRITERION_IMPORTANT, __VA_ARGS__)
|
||||
|
||||
# define criterion_pinfo(...) criterion_plog(CRITERION_INFO, __VA_ARGS__)
|
||||
# define criterion_pimportant(...) criterion_plog(CRITERION_IMPORTANT, __VA_ARGS__)
|
||||
|
||||
struct criterion_output_provider {
|
||||
void (*log_pre_all )(struct criterion_test_set *set);
|
||||
void (*log_pre_suite )(struct criterion_suite_set *set);
|
||||
void (*log_pre_init )(struct criterion_test *test);
|
||||
void (*log_pre_test )(struct criterion_test *test);
|
||||
void (*log_assert )(struct criterion_assert_stats *stats);
|
||||
void (*log_test_crash)(struct criterion_test_stats *stats);
|
||||
void (*log_post_test )(struct criterion_test_stats *stats);
|
||||
void (*log_post_fini )(struct criterion_test_stats *stats);
|
||||
void (*log_post_suite)(struct criterion_suite_stats *stats);
|
||||
void (*log_post_all )(struct criterion_global_stats *stats);
|
||||
void (*log_pre_all )(struct criterion_test_set *set);
|
||||
void (*log_pre_suite )(struct criterion_suite_set *set);
|
||||
void (*log_pre_init )(struct criterion_test *test);
|
||||
void (*log_pre_test )(struct criterion_test *test);
|
||||
void (*log_assert )(struct criterion_assert_stats *stats);
|
||||
void (*log_test_crash )(struct criterion_test_stats *stats);
|
||||
void (*log_other_crash)(struct criterion_test_stats *stats);
|
||||
void (*log_post_test )(struct criterion_test_stats *stats);
|
||||
void (*log_post_fini )(struct criterion_test_stats *stats);
|
||||
void (*log_post_suite )(struct criterion_suite_stats *stats);
|
||||
void (*log_post_all )(struct criterion_global_stats *stats);
|
||||
};
|
||||
|
||||
extern struct criterion_output_provider normal_logging;
|
||||
|
|
|
@ -26,10 +26,12 @@
|
|||
|
||||
# include "types.h"
|
||||
|
||||
typedef int (*f_criterion_cmp)(void *, void *);
|
||||
|
||||
struct criterion_ordered_set {
|
||||
struct criterion_ordered_set_node *first;
|
||||
size_t size;
|
||||
int (*const cmp)(void *, void *);
|
||||
f_criterion_cmp cmp;
|
||||
void (*const dtor)(void *, void *);
|
||||
};
|
||||
|
||||
|
@ -48,12 +50,16 @@ struct criterion_test_set {
|
|||
size_t tests;
|
||||
};
|
||||
|
||||
struct criterion_ordered_set *new_ordered_set(int (*cmp)(void *, void *), void (*dtor)(void *, void *));
|
||||
void *insert_ordered_set(struct criterion_ordered_set *l, void *ptr, size_t size);
|
||||
struct criterion_ordered_set *new_ordered_set(f_criterion_cmp cmp,
|
||||
void (*dtor)(void *, void *));
|
||||
|
||||
# define FOREACH_SET(Elt, Set) \
|
||||
for (struct criterion_ordered_set_node *n = Set->first; n; n = n->next) \
|
||||
for (int cond = 1; cond;) \
|
||||
void *insert_ordered_set(struct criterion_ordered_set *l,
|
||||
void *ptr,
|
||||
size_t size);
|
||||
|
||||
# define FOREACH_SET(Elt, Set) \
|
||||
for (struct criterion_ordered_set_node *n = Set->first; n; n = n->next) \
|
||||
for (int cond = 1; cond;) \
|
||||
for (Elt = (void*) n->data; cond && (cond = 0, 1);)
|
||||
|
||||
#endif /* !CRITERION_ORDERED_SET_H_ */
|
||||
|
|
|
@ -56,6 +56,7 @@ struct criterion_suite_stats {
|
|||
struct criterion_test_stats *tests;
|
||||
size_t nb_tests;
|
||||
size_t nb_asserts;
|
||||
size_t tests_skipped;
|
||||
size_t tests_failed;
|
||||
size_t tests_crashed;
|
||||
size_t tests_passed;
|
||||
|
@ -70,6 +71,7 @@ struct criterion_global_stats {
|
|||
size_t nb_suites;
|
||||
size_t nb_tests;
|
||||
size_t nb_asserts;
|
||||
size_t tests_skipped;
|
||||
size_t tests_failed;
|
||||
size_t tests_crashed;
|
||||
size_t tests_passed;
|
||||
|
|
|
@ -29,9 +29,9 @@
|
|||
|
||||
struct criterion_test_extra_data {
|
||||
int sentinel_;
|
||||
const char *const identifier_;
|
||||
const char *const file_;
|
||||
const unsigned line_;
|
||||
const char *identifier_;
|
||||
const char *file_;
|
||||
unsigned line_;
|
||||
void (*init)(void);
|
||||
void (*fini)(void);
|
||||
int signal;
|
||||
|
@ -44,12 +44,14 @@ struct criterion_test {
|
|||
const char *name;
|
||||
const char *category;
|
||||
void (*test)(void);
|
||||
struct criterion_test_extra_data *const data;
|
||||
struct criterion_test_extra_data *data;
|
||||
};
|
||||
|
||||
struct criterion_suite {
|
||||
const char *name;
|
||||
struct criterion_test_extra_data *const data;
|
||||
struct criterion_test_extra_data *data;
|
||||
};
|
||||
|
||||
typedef void (*f_worker_func)(struct criterion_test *, struct criterion_suite *);
|
||||
|
||||
#endif /* !CRITERION_TYPES_H_ */
|
||||
|
|
1
po/LINGUAS
Normal file
1
po/LINGUAS
Normal file
|
@ -0,0 +1 @@
|
|||
fr
|
78
po/Makevars
Normal file
78
po/Makevars
Normal file
|
@ -0,0 +1,78 @@
|
|||
# Makefile variables for PO directory in any package using GNU gettext.
|
||||
|
||||
# Usually the message domain is the same as the package name.
|
||||
DOMAIN = $(PACKAGE)
|
||||
|
||||
# These two variables depend on the location of this directory.
|
||||
subdir = po
|
||||
top_builddir = ..
|
||||
|
||||
# These options get passed to xgettext.
|
||||
XGETTEXT_OPTIONS = --keyword=_ --keyword=N_
|
||||
|
||||
# This is the copyright holder that gets inserted into the header of the
|
||||
# $(DOMAIN).pot file. Set this to the copyright holder of the surrounding
|
||||
# package. (Note that the msgstr strings, extracted from the package's
|
||||
# sources, belong to the copyright holder of the package.) Translators are
|
||||
# expected to transfer the copyright for their translations to this person
|
||||
# or entity, or to disclaim their copyright. The empty string stands for
|
||||
# the public domain; in this case the translators are expected to disclaim
|
||||
# their copyright.
|
||||
COPYRIGHT_HOLDER = Franklin "Snaipe" Mathieu
|
||||
|
||||
# This tells whether or not to prepend "GNU " prefix to the package
|
||||
# name that gets inserted into the header of the $(DOMAIN).pot file.
|
||||
# Possible values are "yes", "no", or empty. If it is empty, try to
|
||||
# detect it automatically by scanning the files in $(top_srcdir) for
|
||||
# "GNU packagename" string.
|
||||
PACKAGE_GNU = no
|
||||
|
||||
# This is the email address or URL to which the translators shall report
|
||||
# bugs in the untranslated strings:
|
||||
# - Strings which are not entire sentences, see the maintainer guidelines
|
||||
# in the GNU gettext documentation, section 'Preparing Strings'.
|
||||
# - Strings which use unclear terms or require additional context to be
|
||||
# understood.
|
||||
# - Strings which make invalid assumptions about notation of date, time or
|
||||
# money.
|
||||
# - Pluralisation problems.
|
||||
# - Incorrect English spelling.
|
||||
# - Incorrect formatting.
|
||||
# It can be your email address, or a mailing list address where translators
|
||||
# can write to without being subscribed, or the URL of a web page through
|
||||
# which the translators can contact you.
|
||||
MSGID_BUGS_ADDRESS =
|
||||
|
||||
# This is the list of locale categories, beyond LC_MESSAGES, for which the
|
||||
# message catalogs shall be used. It is usually empty.
|
||||
EXTRA_LOCALE_CATEGORIES =
|
||||
|
||||
# This tells whether the $(DOMAIN).pot file contains messages with an 'msgctxt'
|
||||
# context. Possible values are "yes" and "no". Set this to yes if the
|
||||
# package uses functions taking also a message context, like pgettext(), or
|
||||
# if in $(XGETTEXT_OPTIONS) you define keywords with a context argument.
|
||||
USE_MSGCTXT = no
|
||||
|
||||
# These options get passed to msgmerge.
|
||||
# Useful options are in particular:
|
||||
# --previous to keep previous msgids of translated messages,
|
||||
# --quiet to reduce the verbosity.
|
||||
MSGMERGE_OPTIONS =
|
||||
|
||||
# These options get passed to msginit.
|
||||
# If you want to disable line wrapping when writing PO files, add
|
||||
# --no-wrap to MSGMERGE_OPTIONS, XGETTEXT_OPTIONS, and
|
||||
# MSGINIT_OPTIONS.
|
||||
MSGINIT_OPTIONS =
|
||||
|
||||
# This tells whether or not to regenerate a PO file when $(DOMAIN).pot
|
||||
# has changed. Possible values are "yes" and "no". Set this to no if
|
||||
# the POT file is checked in the repository and the version control
|
||||
# program ignores timestamps.
|
||||
PO_DEPENDS_ON_POT = yes
|
||||
|
||||
# This tells whether or not to forcibly update $(DOMAIN).pot and
|
||||
# regenerate PO files on "make dist". Possible values are "yes" and
|
||||
# "no". Set this to no if the POT file and PO files are maintained
|
||||
# externally.
|
||||
DIST_DEPENDS_ON_UPDATE_PO = yes
|
2
po/POTFILES.in
Normal file
2
po/POTFILES.in
Normal file
|
@ -0,0 +1,2 @@
|
|||
# List of source files which contain translatable strings.
|
||||
src/log/normal.c
|
77
po/fr.po
Normal file
77
po/fr.po
Normal file
|
@ -0,0 +1,77 @@
|
|||
# French translations for criterion package
|
||||
# Traductions françaises du paquet criterion.
|
||||
# Copyright (C) 2015 Franklin "Snaipe" Mathieu
|
||||
# This file is distributed under the same license as the criterion package.
|
||||
# <franklinmathieu@gmail.com>, 2015.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: criterion 1.0.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-04-10 23:08+0200\n"
|
||||
"PO-Revision-Date: 2015-04-03 17:58+0200\n"
|
||||
"Last-Translator: <franklinmathieu@gmail.com>\n"
|
||||
"Language-Team: French\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: src/log/normal.c:38
|
||||
#, c-format
|
||||
msgid "Criterion v%s\n"
|
||||
msgstr "Criterion v%s\n"
|
||||
|
||||
#: src/log/normal.c:42
|
||||
#, c-format
|
||||
msgid "%1$s::%2$s\n"
|
||||
msgstr "%1$s::%2$s\n"
|
||||
|
||||
#: src/log/normal.c:47 src/log/normal.c:121
|
||||
#, c-format
|
||||
msgid " %s\n"
|
||||
msgstr " %s\n"
|
||||
|
||||
#: src/log/normal.c:76
|
||||
#, c-format
|
||||
msgid "%1$s::%2$s: Test is disabled\n"
|
||||
msgstr "%1$s::%2$s: Le test est désactivé\n"
|
||||
|
||||
#: src/log/normal.c:77
|
||||
#, c-format
|
||||
msgid "%1$s::%2$s: Suite is disabled\n"
|
||||
msgstr "%1$s::%2$s: La suite est désactivée\n"
|
||||
|
||||
#: src/log/normal.c:94
|
||||
#, c-format
|
||||
msgid ""
|
||||
"%1$sSynthesis: Tested: %2$s%3$lu%4$s | Passing: %5$s%6$lu%7$s | Failing: %8$s"
|
||||
"%9$lu%10$s | Crashing: %11$s%12$lu%13$s %14$s\n"
|
||||
msgstr ""
|
||||
"%1$sSynthèse: Testés: %2$s%3$lu%4$s | Validés: %5$s%6$lu%7$s | Échoués: %8$s"
|
||||
"%9$lu%10$s | Plantages: %11$s%12$lu%13$s %14$s\n"
|
||||
|
||||
#: src/log/normal.c:115
|
||||
#, c-format
|
||||
msgid "%1$s%2$s%3$s:%4$s%5$d%6$s: Assertion failed: %7$s\n"
|
||||
msgstr "%1$s%2$s%3$s:%4$s%5$d%6$s: Échec d'assertion: %7$s\n"
|
||||
|
||||
#: src/log/normal.c:128
|
||||
#, c-format
|
||||
msgid "%1$s%2$s%3$s:%4$s%5$u%6$s: Unexpected signal caught below this line!\n"
|
||||
msgstr ""
|
||||
"%1$s%2$s%3$s:%4$s%5$u%6$s: Un signal inattendu a été reçu après cette "
|
||||
"ligne!\n"
|
||||
|
||||
#: src/log/normal.c:132
|
||||
#, c-format
|
||||
msgid "%1$s::%2$s: CRASH!\n"
|
||||
msgstr "%1$s::%2$s: PLANTAGE!\n"
|
||||
|
||||
#: src/log/normal.c:139
|
||||
#, c-format
|
||||
msgid "%1$sWarning! This test crashed during its setup or teardown.%2$s\n"
|
||||
msgstr ""
|
||||
"%1$sAttention! Ce test a planté pendant son initialisation ou sa "
|
||||
"finalisation.%2$s\n"
|
|
@ -7,6 +7,7 @@ BIN_TESTS = \
|
|||
more-suites \
|
||||
long-messages \
|
||||
description \
|
||||
other-crashes \
|
||||
simple
|
||||
|
||||
TESTS_ENVIRONMENT = CRITERION_ALWAYS_SUCCEED=1
|
||||
|
@ -15,6 +16,11 @@ check_PROGRAMS := $(BIN_TESTS)
|
|||
CFLAGS = -I$(top_srcdir)/include/ -std=c99 -Wall -Wextra -pedantic
|
||||
LDADD = -L$(top_srcdir)/ -lcriterion
|
||||
|
||||
if ENABLE_RT_TESTS
|
||||
BIN_TESTS += with-time
|
||||
with_time_LDADD = $(LDADD) -lrt
|
||||
endif
|
||||
|
||||
SCRIPT_TESTS = tests/tap_test.sh \
|
||||
tests/early_exit.sh \
|
||||
tests/verbose.sh \
|
||||
|
|
|
@ -50,3 +50,31 @@ Test(asserts, float) {
|
|||
assert_neq(0.1 * 0.1, 0.01);
|
||||
assert_float_eq(0.1 * 0.1, 0.01, 0.001);
|
||||
}
|
||||
|
||||
struct dummy_struct {
|
||||
char a;
|
||||
size_t b;
|
||||
};
|
||||
|
||||
int eq_dummy(struct dummy_struct *a, struct dummy_struct *b) {
|
||||
return a->a != b->a || a->b != b->b;
|
||||
}
|
||||
|
||||
Test(asserts, array) {
|
||||
int arr1[] = {1, 2, 3, 4};
|
||||
int arr2[] = {4, 3, 2, 1};
|
||||
|
||||
struct dummy_struct s1[] = {{4, 2}, {2, 4}};
|
||||
struct dummy_struct s2[2];
|
||||
memset(s2, 0xFF, sizeof(s2));
|
||||
s2[0].a = 4;
|
||||
s2[0].b = 2;
|
||||
s2[1].a = 2;
|
||||
s2[1].b = 4;
|
||||
|
||||
assert_arrays_eq(arr1, arr1, 4);
|
||||
assert_arrays_neq(arr1, arr2, 4);
|
||||
|
||||
assert_arrays_neq(s1, s2, 2);
|
||||
assert_arrays_eq_cmp(s1, s2, 2, eq_dummy);
|
||||
}
|
||||
|
|
|
@ -3,3 +3,6 @@
|
|||
Test(misc, failing, .description = "Just a failing test") {
|
||||
assert(0);
|
||||
}
|
||||
|
||||
Test(misc, skipped, .description = "This one is skipped", .disabled = true) {
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
#include <criterion/criterion.h>
|
||||
|
||||
void setup_suite(void) {
|
||||
// setup suite
|
||||
}
|
||||
|
||||
TestSuite(suite1, .init = setup_suite);
|
||||
void teardown_suite(void) {
|
||||
}
|
||||
|
||||
TestSuite(suite1, .init = setup_suite, .fini = teardown_suite);
|
||||
|
||||
Test(suite1, test) {
|
||||
assert(1);
|
||||
|
|
14
samples/other-crashes.c
Normal file
14
samples/other-crashes.c
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include <criterion/criterion.h>
|
||||
|
||||
void crash(void) {
|
||||
int *i = NULL;
|
||||
*i = 42;
|
||||
}
|
||||
|
||||
Test(misc, setup_crash, .init = crash) {
|
||||
assert(true);
|
||||
}
|
||||
|
||||
Test(misc, teardown_crash, .fini = crash) {
|
||||
assert(true);
|
||||
}
|
5
samples/with-time.c
Normal file
5
samples/with-time.c
Normal file
|
@ -0,0 +1,5 @@
|
|||
#include <criterion/criterion.h>
|
||||
|
||||
Test(samples, timed) {
|
||||
assert(0);
|
||||
}
|
35
src/abort.c
Normal file
35
src/abort.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright © 2015 Franklin "Snaipe" Mathieu <http://snai.pe/>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include <setjmp.h>
|
||||
#include "abort.h"
|
||||
|
||||
static jmp_buf g_pre_test;
|
||||
|
||||
int setup_abort_test(void) {
|
||||
return !setjmp(g_pre_test);
|
||||
}
|
||||
|
||||
void criterion_abort_test(void) {
|
||||
longjmp(g_pre_test, 1);
|
||||
}
|
31
src/abort.h
Normal file
31
src/abort.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright © 2015 Franklin "Snaipe" Mathieu <http://snai.pe/>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef ABORT_H_
|
||||
# define ABORT_H_
|
||||
|
||||
# include <criterion/abort.h>
|
||||
|
||||
int setup_abort_test(void);
|
||||
|
||||
#endif /* !ABORT_H_ */
|
24
src/event.c
24
src/event.c
|
@ -22,39 +22,44 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <csptr/smart_ptr.h>
|
||||
#include "criterion/stats.h"
|
||||
#include "criterion/common.h"
|
||||
#include "criterion/hooks.h"
|
||||
#include "event.h"
|
||||
|
||||
int EVENT_PIPE = -1;
|
||||
FILE *g_event_pipe = NULL;
|
||||
|
||||
void destroy_event(void *ptr, UNUSED void *meta) {
|
||||
struct event *ev = ptr;
|
||||
free(ev->data);
|
||||
}
|
||||
|
||||
struct event *read_event(int fd) {
|
||||
struct event *read_event(FILE *f) {
|
||||
unsigned kind;
|
||||
if (read(fd, &kind, sizeof (unsigned)) < (ssize_t) sizeof (unsigned))
|
||||
if (fread(&kind, sizeof (unsigned), 1, f) == 0)
|
||||
return NULL;
|
||||
|
||||
switch (kind) {
|
||||
case ASSERT: {
|
||||
const size_t assert_size = sizeof (struct criterion_assert_stats);
|
||||
unsigned char *buf = malloc(assert_size);
|
||||
if (read(fd, buf, assert_size) < (ssize_t) assert_size)
|
||||
if (fread(buf, assert_size, 1, f) == 0)
|
||||
return NULL;
|
||||
|
||||
return unique_ptr(struct event, { .kind = kind, .data = buf }, destroy_event);
|
||||
return unique_ptr(struct event,
|
||||
.value = { .kind = kind, .data = buf },
|
||||
.dtor = destroy_event);
|
||||
}
|
||||
case POST_TEST: {
|
||||
double *elapsed_time = malloc(sizeof (double));
|
||||
if (read(fd, elapsed_time, sizeof (double)) < (ssize_t) sizeof (double))
|
||||
if (fread(elapsed_time, sizeof (double), 1, f) == 0)
|
||||
return NULL;
|
||||
|
||||
return unique_ptr(struct event, { .kind = kind, .data = elapsed_time }, destroy_event);
|
||||
return unique_ptr(struct event,
|
||||
.value = { .kind = kind, .data = elapsed_time },
|
||||
.dtor = destroy_event);
|
||||
}
|
||||
default:
|
||||
return unique_ptr(struct event, { .kind = kind, .data = NULL });
|
||||
|
@ -65,5 +70,6 @@ void send_event(int kind, void *data, size_t size) {
|
|||
unsigned char buf[sizeof (int) + size];
|
||||
memcpy(buf, &kind, sizeof (int));
|
||||
memcpy(buf + sizeof (int), data, size);
|
||||
write(EVENT_PIPE, buf, sizeof (int) + size);
|
||||
if (fwrite(buf, sizeof (int) + size, 1, g_event_pipe) == 0)
|
||||
abort();
|
||||
}
|
||||
|
|
|
@ -31,6 +31,6 @@ struct event {
|
|||
void *data;
|
||||
};
|
||||
|
||||
struct event *read_event(int fd);
|
||||
struct event *read_event(FILE *f);
|
||||
|
||||
#endif /* !EVENT_H_ */
|
||||
|
|
8
src/i18n.c
Normal file
8
src/i18n.c
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include "i18n.h"
|
||||
|
||||
#if ENABLE_NLS
|
||||
__attribute__ ((constructor))
|
||||
void init_i18n(void) {
|
||||
bindtextdomain (PACKAGE, LOCALEDIR);
|
||||
}
|
||||
#endif
|
16
src/i18n.h
Normal file
16
src/i18n.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
#ifndef I18N_H_
|
||||
# define I18N_H_
|
||||
|
||||
# include "config.h"
|
||||
|
||||
# if !ENABLE_NLS
|
||||
# define _(String) String
|
||||
# define _s(String, Plural, Quantity) ((Quantity) == 1 ? String : Plural)
|
||||
# else
|
||||
# include <libintl.h>
|
||||
# define _(String) dgettext(PACKAGE, String)
|
||||
# define _s(String, Plural, Quantity) \
|
||||
dngettext(PACKAGE, String, Plural, (Quantity))
|
||||
# endif
|
||||
|
||||
#endif /* !I18N_H_ */
|
|
@ -21,18 +21,54 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#define CRITERION_LOGGING_COLORS
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include "criterion/logging.h"
|
||||
#include "criterion/options.h"
|
||||
#include "i18n.h"
|
||||
|
||||
void criterion_log(enum criterion_logging_level level, const char *msg, ...) {
|
||||
#define LOG_FORMAT "[%1$s%2$s%3$s] %4$s"
|
||||
|
||||
const struct criterion_prefix_data g_criterion_logging_prefixes[] = {
|
||||
[CRITERION_LOGGING_PREFIX_DASHES] = { "----", CRIT_FG_BLUE },
|
||||
[CRITERION_LOGGING_PREFIX_EQUALS] = { "====", CRIT_FG_BLUE },
|
||||
[CRITERION_LOGGING_PREFIX_RUN] = { "RUN ", CRIT_FG_BLUE },
|
||||
[CRITERION_LOGGING_PREFIX_SKIP] = { "SKIP", CRIT_FG_GOLD },
|
||||
[CRITERION_LOGGING_PREFIX_PASS] = { "PASS", CRIT_FG_GREEN },
|
||||
[CRITERION_LOGGING_PREFIX_FAIL] = { "FAIL", CRIT_FG_RED },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
void criterion_plog(enum criterion_logging_level level, const struct criterion_prefix_data *prefix, const char *msg, ...) {
|
||||
va_list args;
|
||||
|
||||
if (level < criterion_options.logging_threshold)
|
||||
return;
|
||||
|
||||
char formatted_msg[1024];
|
||||
va_start(args, msg);
|
||||
vfprintf(stderr, msg, args);
|
||||
vsnprintf(formatted_msg, sizeof formatted_msg, msg, args);
|
||||
va_end(args);
|
||||
|
||||
fprintf(stderr, _(LOG_FORMAT),
|
||||
CRIT_COLOR_NORMALIZE(prefix->color),
|
||||
prefix->prefix,
|
||||
RESET,
|
||||
formatted_msg);
|
||||
}
|
||||
|
||||
void criterion_log(enum criterion_logging_level level, const char *msg, ...) {
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
criterion_vlog(level, msg, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void criterion_vlog(enum criterion_logging_level level, const char *msg, va_list args) {
|
||||
if (level < criterion_options.logging_threshold)
|
||||
return;
|
||||
|
||||
vfprintf(stderr, msg, args);
|
||||
}
|
||||
|
|
148
src/log/normal.c
148
src/log/normal.c
|
@ -22,6 +22,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#define CRITERION_LOGGING_COLORS
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -31,128 +32,131 @@
|
|||
#include "criterion/ordered-set.h"
|
||||
#include "timer.h"
|
||||
#include "config.h"
|
||||
|
||||
#define NORMALIZE(Str) (criterion_options.use_ascii ? "" : Str)
|
||||
|
||||
#define FG_BOLD NORMALIZE("\e[0;1m")
|
||||
#define FG_RED NORMALIZE("\e[0;31m")
|
||||
#define FG_GREEN NORMALIZE("\e[0;32m")
|
||||
#define FG_GOLD NORMALIZE("\e[0;33m")
|
||||
#define FG_BLUE NORMALIZE("\e[0;34m")
|
||||
#define RESET NORMALIZE("\e[0m")
|
||||
#include "i18n.h"
|
||||
|
||||
void normal_log_pre_all(UNUSED struct criterion_test_set *set) {
|
||||
criterion_info("[%s====%s] Criterion v%s\n", FG_BLUE, RESET, VERSION);
|
||||
criterion_pinfo(CRITERION_PREFIX_DASHES, _("Criterion v%s\n"), VERSION);
|
||||
}
|
||||
|
||||
void normal_log_pre_init(struct criterion_test *test) {
|
||||
criterion_info("[%sRUN%s ] %s::%s\n", FG_BLUE, RESET, test->category, test->name);
|
||||
criterion_pinfo(CRITERION_PREFIX_RUN, _("%1$s::%2$s\n"),
|
||||
test->category,
|
||||
test->name);
|
||||
|
||||
if (test->data->description)
|
||||
criterion_info("[%s----%s] %s\n", FG_BLUE, RESET, test->data->description);
|
||||
criterion_pinfo(CRITERION_PREFIX_RUN, _(" %s\n"),
|
||||
test->data->description);
|
||||
}
|
||||
|
||||
void normal_log_post_test(struct criterion_test_stats *stats) {
|
||||
const char *format = can_measure_time() ? "%s::%s: (%3.2fs)\n" : "%s::%s\n";
|
||||
const enum criterion_logging_level level = stats->failed ? CRITERION_IMPORTANT
|
||||
: CRITERION_INFO;
|
||||
const char *color = stats->failed ? FG_RED : FG_GREEN;
|
||||
const char *format = can_measure_time() ? "%1$s::%2$s: (%3$3.2fs)\n"
|
||||
: "%1$s::%2$s\n";
|
||||
|
||||
criterion_log(level, "[%s%s%s] ", color, stats->failed ? "FAIL" : "PASS", RESET);
|
||||
criterion_log(level, format,
|
||||
const enum criterion_logging_level level
|
||||
= stats->failed ? CRITERION_IMPORTANT : CRITERION_INFO;
|
||||
const struct criterion_prefix_data *prefix
|
||||
= stats->failed ? CRITERION_PREFIX_FAIL : CRITERION_PREFIX_PASS;
|
||||
|
||||
criterion_plog(level, prefix, _(format),
|
||||
stats->test->category,
|
||||
stats->test->name,
|
||||
stats->elapsed_time);
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline bool is_disabled(struct criterion_test *t, struct criterion_suite *s) {
|
||||
static inline bool is_disabled(struct criterion_test *t,
|
||||
struct criterion_suite *s) {
|
||||
return t->data->disabled || (s->data && s->data->disabled);
|
||||
}
|
||||
|
||||
void normal_log_post_suite(struct criterion_suite_stats *stats) {
|
||||
for (struct criterion_test_stats *ts = stats->tests; ts; ts = ts->next) {
|
||||
if (is_disabled(ts->test, stats->suite)) {
|
||||
criterion_info("[%sSKIP%s] %s::%s: %s is disabled\n",
|
||||
FG_GOLD,
|
||||
RESET,
|
||||
const char *format = ts->test->data->disabled
|
||||
? _("%1$s::%2$s: Test is disabled\n")
|
||||
: _("%1$s::%2$s: Suite is disabled\n");
|
||||
|
||||
criterion_pinfo(CRITERION_PREFIX_SKIP, format,
|
||||
ts->test->category,
|
||||
ts->test->name,
|
||||
ts->test->data->disabled ? "test" : "suite");
|
||||
ts->test->name);
|
||||
|
||||
if (ts->test->data->description)
|
||||
criterion_info("[%s----%s] %s\n", FG_BLUE, RESET, ts->test->data->description);
|
||||
criterion_pinfo(CRITERION_PREFIX_DASHES, " %s\n",
|
||||
ts->test->data->description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void normal_log_post_all(struct criterion_global_stats *stats) {
|
||||
criterion_important("[%s====%s] ", FG_BLUE, RESET);
|
||||
criterion_important("%sSynthesis: " SIZE_T_FORMAT " test%s run. " SIZE_T_FORMAT " passed, " SIZE_T_FORMAT " failed (with " SIZE_T_FORMAT " crash%s)%s\n",
|
||||
size_t tested = stats->nb_tests - stats->tests_skipped;
|
||||
|
||||
criterion_pimportant(CRITERION_PREFIX_EQUALS,
|
||||
_("%1$sSynthesis: Tested: %2$s%3$lu%4$s "
|
||||
"| Passing: %5$s%6$lu%7$s "
|
||||
"| Failing: %8$s%9$lu%10$s "
|
||||
"| Crashing: %11$s%12$lu%13$s "
|
||||
"%14$s\n"),
|
||||
FG_BOLD,
|
||||
stats->nb_tests,
|
||||
stats->nb_tests == 1 ? " was" : "s were",
|
||||
stats->tests_passed,
|
||||
stats->tests_failed,
|
||||
stats->tests_crashed,
|
||||
stats->tests_crashed == 1 ? "" : "es",
|
||||
FG_BLUE, (unsigned long) tested, FG_BOLD,
|
||||
FG_GREEN, (unsigned long) stats->tests_passed, FG_BOLD,
|
||||
FG_RED, (unsigned long) stats->tests_failed, FG_BOLD,
|
||||
FG_RED, (unsigned long) stats->tests_crashed, FG_BOLD,
|
||||
RESET);
|
||||
}
|
||||
|
||||
void normal_log_assert(struct criterion_assert_stats *stats) {
|
||||
if (!stats->passed) {
|
||||
char *dup = strdup(*stats->message ? stats->message : stats->condition), *saveptr = NULL;
|
||||
char *line = strtok_r(dup, "\n", &saveptr);
|
||||
char *dup = strdup(*stats->message ? stats->message
|
||||
: stats->condition);
|
||||
char *saveptr = NULL;
|
||||
char *line = strtok_r(dup, "\n", &saveptr);
|
||||
|
||||
criterion_important("[%s----%s] ", FG_BLUE, RESET);
|
||||
criterion_important("%s%s%s:%s%d%s: Assertion failed: %s\n",
|
||||
FG_BOLD,
|
||||
stats->file,
|
||||
RESET,
|
||||
FG_RED,
|
||||
stats->line,
|
||||
RESET,
|
||||
criterion_pimportant(CRITERION_PREFIX_DASHES,
|
||||
_("%1$s%2$s%3$s:%4$s%5$d%6$s: Assertion failed: %7$s\n"),
|
||||
FG_BOLD, stats->file, RESET,
|
||||
FG_RED, stats->line, RESET,
|
||||
line);
|
||||
|
||||
while ((line = strtok_r(NULL, "\n", &saveptr)))
|
||||
criterion_important("[%s----%s] %s\n", FG_BLUE, RESET, line);
|
||||
criterion_pimportant(CRITERION_PREFIX_DASHES, _(" %s\n"), line);
|
||||
free(dup);
|
||||
}
|
||||
}
|
||||
|
||||
void normal_log_test_crash(struct criterion_test_stats *stats) {
|
||||
criterion_important("[%s----%s] ", FG_BLUE, RESET);
|
||||
criterion_important("%s%s%s:%s%u%s: Unexpected signal caught below this line!\n",
|
||||
FG_BOLD,
|
||||
stats->file,
|
||||
RESET,
|
||||
FG_RED,
|
||||
stats->progress,
|
||||
RESET);
|
||||
criterion_important("[%sFAIL%s] %s::%s: CRASH!\n",
|
||||
FG_RED,
|
||||
RESET,
|
||||
criterion_pimportant(CRITERION_PREFIX_DASHES,
|
||||
_("%1$s%2$s%3$s:%4$s%5$u%6$s: "
|
||||
"Unexpected signal caught below this line!\n"),
|
||||
FG_BOLD, stats->file, RESET,
|
||||
FG_RED, stats->progress, RESET);
|
||||
criterion_pimportant(CRITERION_PREFIX_FAIL, _("%1$s::%2$s: CRASH!\n"),
|
||||
stats->test->category,
|
||||
stats->test->name);
|
||||
}
|
||||
|
||||
void normal_log_other_crash(UNUSED struct criterion_test_stats *stats) {
|
||||
criterion_pimportant(CRITERION_PREFIX_DASHES,
|
||||
_("%1$sWarning! This test crashed during its setup or teardown.%2$s\n"),
|
||||
FG_BOLD, RESET);
|
||||
}
|
||||
|
||||
void normal_log_pre_suite(struct criterion_suite_set *set) {
|
||||
criterion_info("[%s====%s] ", FG_BLUE, RESET);
|
||||
criterion_info("Running %s" SIZE_T_FORMAT "%s test%s from %s%s%s:\n",
|
||||
FG_BLUE,
|
||||
set->tests->size,
|
||||
RESET,
|
||||
set->tests->size == 1 ? "" : "s",
|
||||
FG_GOLD,
|
||||
set->suite.name,
|
||||
RESET);
|
||||
criterion_pinfo(CRITERION_PREFIX_EQUALS,
|
||||
_s("Running %1$s%2$lu%3$s test from %4$s%5$s%6$s:\n",
|
||||
"Running %1$s%2$lu%3$s tests from %4$s%5$s%6$s:\n",
|
||||
set->tests->size),
|
||||
FG_BLUE, (unsigned long) set->tests->size, RESET,
|
||||
FG_GOLD, set->suite.name, RESET);
|
||||
}
|
||||
|
||||
struct criterion_output_provider normal_logging = {
|
||||
.log_pre_all = normal_log_pre_all,
|
||||
.log_pre_init = normal_log_pre_init,
|
||||
.log_pre_suite = normal_log_pre_suite,
|
||||
.log_assert = normal_log_assert,
|
||||
.log_test_crash = normal_log_test_crash,
|
||||
.log_post_test = normal_log_post_test,
|
||||
.log_post_suite = normal_log_post_suite,
|
||||
.log_post_all = normal_log_post_all,
|
||||
.log_pre_all = normal_log_pre_all,
|
||||
.log_pre_init = normal_log_pre_init,
|
||||
.log_pre_suite = normal_log_pre_suite,
|
||||
.log_assert = normal_log_assert,
|
||||
.log_test_crash = normal_log_test_crash,
|
||||
.log_other_crash = normal_log_other_crash,
|
||||
.log_post_test = normal_log_post_test,
|
||||
.log_post_suite = normal_log_post_suite,
|
||||
.log_post_all = normal_log_post_all,
|
||||
};
|
||||
|
|
46
src/main.c
46
src/main.c
|
@ -26,13 +26,26 @@
|
|||
#include <criterion/options.h>
|
||||
#include <criterion/ordered-set.h>
|
||||
#include <stdio.h>
|
||||
#include <locale.h>
|
||||
#include <getopt.h>
|
||||
#include <csptr/smart_ptr.h>
|
||||
#include "runner.h"
|
||||
#include "config.h"
|
||||
|
||||
#if ENABLE_NLS
|
||||
# include <libintl.h>
|
||||
#endif
|
||||
|
||||
# define VERSION_MSG "Tests compiled with Criterion v" VERSION "\n"
|
||||
|
||||
#ifdef HAVE_FNMATCH
|
||||
# define PATTERN_USAGE \
|
||||
" --pattern [PATTERN]: run tests matching the " \
|
||||
"given pattern\n"
|
||||
#else
|
||||
# define PATTERN_USAGE
|
||||
#endif
|
||||
|
||||
# define USAGE \
|
||||
VERSION_MSG "\n" \
|
||||
"usage: %s OPTIONS\n" \
|
||||
|
@ -44,8 +57,7 @@
|
|||
" -f or --fail-fast: exit after the first failure\n" \
|
||||
" --ascii: don't use fancy unicode symbols " \
|
||||
"or colors in the output\n" \
|
||||
" --pattern [PATTERN]: run tests matching the " \
|
||||
"given pattern\n" \
|
||||
PATTERN_USAGE \
|
||||
" --tap: enables TAP formatting\n" \
|
||||
" --always-succeed: always exit with 0\n" \
|
||||
" --no-early-exit: do not exit the test worker " \
|
||||
|
@ -112,21 +124,31 @@ int main(int argc, char *argv[]) {
|
|||
{"list", no_argument, 0, 'l'},
|
||||
{"ascii", no_argument, 0, 'k'},
|
||||
{"fail-fast", no_argument, 0, 'f'},
|
||||
#ifdef HAVE_FNMATCH
|
||||
{"pattern", required_argument, 0, 'p'},
|
||||
#endif
|
||||
{"always-succeed", no_argument, 0, 'y'},
|
||||
{"no-early-exit", no_argument, 0, 'z'},
|
||||
{0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
criterion_options = (struct criterion_options) {
|
||||
.always_succeed = !strcmp("1", getenv("CRITERION_ALWAYS_SUCCEED") ?: "0"),
|
||||
.no_early_exit = !strcmp("1", getenv("CRITERION_NO_EARLY_EXIT") ?: "0"),
|
||||
.fail_fast = !strcmp("1", getenv("CRITERION_FAIL_FAST") ?: "0"),
|
||||
.use_ascii = !strcmp("1", getenv("CRITERION_USE_ASCII") ?: "0"),
|
||||
.logging_threshold = atoi(getenv("CRITERION_VERBOSITY_LEVEL") ?: "2"),
|
||||
.pattern = getenv("CRITERION_TEST_PATTERN"),
|
||||
.output_provider = NORMAL_LOGGING,
|
||||
};
|
||||
bool use_ascii = !strcmp("1", getenv("CRITERION_USE_ASCII") ?: "0")
|
||||
|| !strcmp("dumb", getenv("TERM") ?: "dumb");
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
#if ENABLE_NLS
|
||||
textdomain (PACKAGE "-test");
|
||||
#endif
|
||||
|
||||
struct criterion_options *opt = &criterion_options;
|
||||
opt->always_succeed = !strcmp("1", getenv("CRITERION_ALWAYS_SUCCEED") ?: "0");
|
||||
opt->no_early_exit = !strcmp("1", getenv("CRITERION_NO_EARLY_EXIT") ?: "0");
|
||||
opt->fail_fast = !strcmp("1", getenv("CRITERION_FAIL_FAST") ?: "0");
|
||||
opt->use_ascii = use_ascii;
|
||||
opt->logging_threshold = atoi(getenv("CRITERION_VERBOSITY_LEVEL") ?: "2");
|
||||
#ifdef HAVE_FNMATCH
|
||||
opt->pattern = getenv("CRITERION_TEST_PATTERN");
|
||||
#endif
|
||||
|
||||
bool use_tap = !strcmp("1", getenv("CRITERION_ENABLE_TAP") ?: "0");
|
||||
|
||||
|
@ -140,7 +162,9 @@ int main(int argc, char *argv[]) {
|
|||
case 'z': criterion_options.no_early_exit = true; break;
|
||||
case 'k': criterion_options.use_ascii = true; break;
|
||||
case 'f': criterion_options.fail_fast = true; break;
|
||||
#ifdef HAVE_FNMATCH
|
||||
case 'p': criterion_options.pattern = optarg; break;
|
||||
#endif
|
||||
case 't': use_tap = true; break;
|
||||
case 'l': do_list_tests = true; break;
|
||||
case 'v': do_print_version = true; break;
|
||||
|
|
|
@ -23,4 +23,7 @@
|
|||
*/
|
||||
# include "criterion/options.h"
|
||||
|
||||
struct criterion_options criterion_options = { .logging_threshold = CRITERION_IMPORTANT };
|
||||
struct criterion_options criterion_options = {
|
||||
.logging_threshold = CRITERION_IMPORTANT,
|
||||
.output_provider = &normal_logging,
|
||||
};
|
||||
|
|
|
@ -39,12 +39,17 @@ static void destroy_ordered_set_node(void *ptr, void *meta) {
|
|||
sfree(((struct criterion_ordered_set_node *) ptr)->next);
|
||||
}
|
||||
|
||||
struct criterion_ordered_set *new_ordered_set(int (*cmp)(void *, void *), f_destructor dtor) {
|
||||
struct criterion_ordered_set *new_ordered_set(f_criterion_cmp cmp,
|
||||
f_destructor dtor) {
|
||||
|
||||
return unique_ptr(struct criterion_ordered_set,
|
||||
{ .cmp = cmp, .dtor = dtor }, destroy_ordered_set);
|
||||
.value = { .cmp = cmp, .dtor = dtor },
|
||||
.dtor = destroy_ordered_set);
|
||||
}
|
||||
|
||||
void *insert_ordered_set(struct criterion_ordered_set *l, void *ptr, size_t size) {
|
||||
void *insert_ordered_set(struct criterion_ordered_set *l,
|
||||
void *ptr,
|
||||
size_t size) {
|
||||
int cmp;
|
||||
struct criterion_ordered_set_node *n, *prev = NULL;
|
||||
for (n = l->first; n && (cmp = l->cmp(ptr, n->data)) > 0; n = n->next)
|
||||
|
|
208
src/posix-compat.c
Normal file
208
src/posix-compat.c
Normal file
|
@ -0,0 +1,208 @@
|
|||
#include "posix-compat.h"
|
||||
#include "process.h"
|
||||
|
||||
#ifdef VANILLA_WIN32
|
||||
# define VC_EXTRALEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
# include <io.h>
|
||||
# include <fcntl.h>
|
||||
# include <winnt.h>
|
||||
# include <stdint.h>
|
||||
|
||||
# define CREATE_SUSPENDED_(Filename, CmdLine, StartupInfo, Info) \
|
||||
CreateProcessW(Filename, \
|
||||
CmdLine, \
|
||||
NULL, \
|
||||
NULL, \
|
||||
TRUE, \
|
||||
CREATE_SUSPENDED, \
|
||||
NULL, \
|
||||
NULL, \
|
||||
&(StartupInfo), \
|
||||
&(Info))
|
||||
|
||||
# define WRITE_PROCESS_(Proc, What, Size) \
|
||||
WriteProcessMemory(Proc, &What, &What, Size, NULL);
|
||||
|
||||
#else
|
||||
# include <unistd.h>
|
||||
# include <sys/wait.h>
|
||||
# include <sys/signal.h>
|
||||
# include <sys/fcntl.h>
|
||||
#endif
|
||||
|
||||
#include <csptr/smart_ptr.h>
|
||||
|
||||
struct proc_handle {
|
||||
#ifdef VANILLA_WIN32
|
||||
HANDLE handle;
|
||||
#else
|
||||
pid_t pid;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pipe_handle {
|
||||
#ifdef VANILLA_WIN32
|
||||
HANDLE fhs[2];
|
||||
#else
|
||||
int fds[2];
|
||||
#endif
|
||||
};
|
||||
|
||||
struct worker_context g_worker_context = {.test = NULL};
|
||||
|
||||
#ifdef VANILLA_WIN32
|
||||
static struct criterion_test child_test;
|
||||
static struct criterion_test_extra_data child_test_data;
|
||||
static struct criterion_suite child_suite;
|
||||
static struct criterion_test_extra_data child_suite_data;
|
||||
static struct pipe_handle child_pipe;
|
||||
#endif
|
||||
|
||||
int resume_child(void) {
|
||||
if (g_worker_context.test) {
|
||||
run_worker(&g_worker_context);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
s_proc_handle *fork_process() {
|
||||
#ifdef VANILLA_WIN32
|
||||
PROCESS_INFORMATION info;
|
||||
STARTUPINFOW si = { .cb = sizeof (STARTUPINFOW) };
|
||||
|
||||
ZeroMemory(&info, sizeof (info));
|
||||
|
||||
// Create the suspended child process
|
||||
wchar_t filename[MAX_PATH];
|
||||
GetModuleFileNameW(NULL, filename, MAX_PATH);
|
||||
|
||||
if (!CREATE_SUSPENDED_(filename, GetCommandLineW(), si, info))
|
||||
return (void *) -1;
|
||||
|
||||
// Copy context over
|
||||
f_worker_func child_func = g_worker_context.func;
|
||||
|
||||
child_test = *g_worker_context.test;
|
||||
child_test_data = *g_worker_context.test->data;
|
||||
child_suite = *g_worker_context.suite;
|
||||
child_pipe = *g_worker_context.pipe;
|
||||
|
||||
g_worker_context = (struct worker_context) {
|
||||
&child_test,
|
||||
&child_suite,
|
||||
child_func,
|
||||
&child_pipe
|
||||
};
|
||||
|
||||
child_test.data = &child_test_data;
|
||||
|
||||
if (g_worker_context.suite->data) {
|
||||
child_suite_data = *g_worker_context.suite->data;
|
||||
child_suite.data = &child_suite_data;
|
||||
WRITE_PROCESS_(info.hProcess, child_suite_data, sizeof (child_suite_data));
|
||||
}
|
||||
|
||||
WRITE_PROCESS_(info.hProcess, child_test, sizeof (child_test));
|
||||
WRITE_PROCESS_(info.hProcess, child_test_data, sizeof (child_test_data));
|
||||
WRITE_PROCESS_(info.hProcess, child_suite, sizeof (child_suite));
|
||||
WRITE_PROCESS_(info.hProcess, child_pipe, sizeof (child_pipe));
|
||||
WRITE_PROCESS_(info.hProcess, g_worker_context, sizeof (struct worker_context));
|
||||
|
||||
ResumeThread(info.hThread);
|
||||
CloseHandle(info.hThread);
|
||||
|
||||
return unique_ptr(s_proc_handle, { info.hProcess });
|
||||
#else
|
||||
pid_t pid = fork();
|
||||
if (pid == -1)
|
||||
return (void *) -1;
|
||||
if (pid == 0)
|
||||
return NULL;
|
||||
return unique_ptr(s_proc_handle, { pid });
|
||||
#endif
|
||||
}
|
||||
|
||||
void wait_process(s_proc_handle *handle, int *status) {
|
||||
#ifdef VANILLA_WIN32
|
||||
WaitForSingleObject(handle->handle, INFINITE);
|
||||
DWORD exit_code;
|
||||
GetExitCodeProcess(handle->handle, &exit_code);
|
||||
CloseHandle(handle->handle);
|
||||
*status = exit_code << 8;
|
||||
#else
|
||||
waitpid(handle->pid, status, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
FILE *pipe_in(s_pipe_handle *p) {
|
||||
#ifdef VANILLA_WIN32
|
||||
CloseHandle(p->fhs[1]);
|
||||
int fd = _open_osfhandle((intptr_t) p->fhs[0], _O_RDONLY);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
FILE *in = _fdopen(fd, "r");
|
||||
#else
|
||||
close(p->fds[1]);
|
||||
FILE *in = fdopen(p->fds[0], "r");
|
||||
#endif
|
||||
if (!in)
|
||||
return NULL;
|
||||
|
||||
setvbuf(in, NULL, _IONBF, 0);
|
||||
return in;
|
||||
}
|
||||
|
||||
FILE *pipe_out(s_pipe_handle *p) {
|
||||
#ifdef VANILLA_WIN32
|
||||
CloseHandle(p->fhs[0]);
|
||||
int fd = _open_osfhandle((intptr_t) p->fhs[1], _O_WRONLY);
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
FILE *out = _fdopen(fd, "w");
|
||||
#else
|
||||
close(p->fds[0]);
|
||||
FILE *out = fdopen(p->fds[1], "w");
|
||||
#endif
|
||||
if (!out)
|
||||
return NULL;
|
||||
|
||||
setvbuf(out, NULL, _IONBF, 0);
|
||||
return out;
|
||||
}
|
||||
|
||||
s_pipe_handle *stdpipe() {
|
||||
#ifdef VANILLA_WIN32
|
||||
HANDLE fhs[2];
|
||||
SECURITY_ATTRIBUTES attr = {
|
||||
.nLength = sizeof (SECURITY_ATTRIBUTES),
|
||||
.bInheritHandle = TRUE
|
||||
};
|
||||
if (!CreatePipe(fhs, fhs + 1, &attr, 0))
|
||||
return NULL;
|
||||
return unique_ptr(s_pipe_handle, {{ fhs[0], fhs[1] }});
|
||||
#else
|
||||
int fds[2] = { -1, -1 };
|
||||
if (pipe(fds) == -1)
|
||||
return NULL;
|
||||
return unique_ptr(s_pipe_handle, {{ fds[0], fds[1] }});
|
||||
#endif
|
||||
}
|
||||
|
||||
s_proc_handle *get_current_process() {
|
||||
#ifdef VANILLA_WIN32
|
||||
return unique_ptr(s_proc_handle, { GetCurrentProcess() });
|
||||
#else
|
||||
return unique_ptr(s_proc_handle, { getpid() });
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_current_process(s_proc_handle *proc) {
|
||||
#ifdef VANILLA_WIN32
|
||||
return GetProcessId(proc->handle) == GetProcessId(GetCurrentProcess());
|
||||
#else
|
||||
return proc->pid == getpid();
|
||||
#endif
|
||||
}
|
56
src/posix-compat.h
Normal file
56
src/posix-compat.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
#ifndef POSIX_COMPAT_H_
|
||||
# define POSIX_COMPAT_H_
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
# define VANILLA_WIN32
|
||||
#endif
|
||||
|
||||
# if !defined(_POSIX_SOURCE)
|
||||
# define _POSIX_SOURCE 1
|
||||
# define TMP_POSIX
|
||||
# endif
|
||||
# include <stdio.h>
|
||||
# ifdef TMP_POSIX
|
||||
# undef _POSIX_SOURCE
|
||||
# undef TMP_POSIX
|
||||
# endif
|
||||
|
||||
# ifdef VANILLA_WIN32
|
||||
# define WEXITSTATUS(Status) (((Status) & 0xFF00) >> 8)
|
||||
# define WTERMSIG(Status) ((Status) & 0x7F)
|
||||
# define WIFEXITED(Status) (WTERMSIG(Status) == 0)
|
||||
# define WIFSIGNALED(Status) (((signed char) (WTERMSIG(Status) + 1) >> 1) > 0)
|
||||
# else
|
||||
# include <sys/wait.h>
|
||||
# endif
|
||||
|
||||
#include <criterion/types.h>
|
||||
|
||||
struct proc_handle;
|
||||
typedef struct proc_handle s_proc_handle;
|
||||
|
||||
struct pipe_handle;
|
||||
typedef struct pipe_handle s_pipe_handle;
|
||||
|
||||
struct worker_context {
|
||||
struct criterion_test *test;
|
||||
struct criterion_suite *suite;
|
||||
f_worker_func func;
|
||||
struct pipe_handle *pipe;
|
||||
};
|
||||
|
||||
extern struct worker_context g_worker_context;
|
||||
|
||||
int resume_child(void);
|
||||
|
||||
s_pipe_handle *stdpipe();
|
||||
FILE *pipe_in(s_pipe_handle *p);
|
||||
FILE *pipe_out(s_pipe_handle *p);
|
||||
|
||||
s_proc_handle *fork_process();
|
||||
void wait_process(s_proc_handle *handle, int *status);
|
||||
|
||||
s_proc_handle *get_current_process();
|
||||
bool is_current_process(s_proc_handle *proc);
|
||||
|
||||
#endif /* !POSIX_COMPAT_H_ */
|
|
@ -22,77 +22,98 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdbool.h>
|
||||
#include <csptr/smart_ptr.h>
|
||||
|
||||
#include "criterion/types.h"
|
||||
#include "criterion/options.h"
|
||||
#include "process.h"
|
||||
#include "event.h"
|
||||
#include "posix-compat.h"
|
||||
|
||||
struct process {
|
||||
pid_t pid;
|
||||
int in;
|
||||
s_proc_handle *proc;
|
||||
FILE *in;
|
||||
};
|
||||
|
||||
static pid_t g_runner_pid;
|
||||
static s_proc_handle *g_current_proc;
|
||||
|
||||
void set_runner_pid(void) {
|
||||
g_runner_pid = getpid();
|
||||
void set_runner_process(void) {
|
||||
g_current_proc = get_current_process();
|
||||
}
|
||||
|
||||
void unset_runner_process(void) {
|
||||
sfree(g_current_proc);
|
||||
}
|
||||
|
||||
bool is_runner(void) {
|
||||
return g_runner_pid == getpid();
|
||||
return is_current_process(g_current_proc);
|
||||
}
|
||||
|
||||
static void close_process(void *ptr, UNUSED void *meta) {
|
||||
close(((struct process *) ptr)->in);
|
||||
struct process *proc = ptr;
|
||||
fclose(proc->in);
|
||||
sfree(proc->proc);
|
||||
}
|
||||
|
||||
struct event *worker_read_event(struct process *proc) {
|
||||
return read_event(proc->in);
|
||||
}
|
||||
|
||||
void run_worker(struct worker_context *ctx) {
|
||||
fclose(stdin);
|
||||
g_event_pipe = pipe_out(ctx->pipe);
|
||||
|
||||
ctx->func(ctx->test, ctx->suite);
|
||||
fclose(g_event_pipe);
|
||||
|
||||
fflush(NULL); // flush all opened streams
|
||||
if (criterion_options.no_early_exit)
|
||||
return;
|
||||
_Exit(0);
|
||||
}
|
||||
|
||||
struct process *spawn_test_worker(struct criterion_test *test,
|
||||
struct criterion_suite *suite,
|
||||
void (*func)(struct criterion_test *, struct criterion_suite *)) {
|
||||
int fds[2];
|
||||
if (pipe(fds) == -1)
|
||||
f_worker_func func) {
|
||||
smart s_pipe_handle *pipe = stdpipe();
|
||||
if (pipe == NULL)
|
||||
abort();
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == -1) {
|
||||
g_worker_context = (struct worker_context) {
|
||||
.test = test,
|
||||
.suite = suite,
|
||||
.func = func,
|
||||
.pipe = pipe
|
||||
};
|
||||
s_proc_handle *proc = fork_process();
|
||||
if (proc == (void *) -1) {
|
||||
return NULL;
|
||||
} else if (proc == NULL) {
|
||||
run_worker(&g_worker_context);
|
||||
return NULL;
|
||||
} else if (!pid) {
|
||||
close(STDIN_FILENO);
|
||||
close(fds[0]);
|
||||
EVENT_PIPE = fds[1];
|
||||
|
||||
func(test, suite);
|
||||
close(fds[1]);
|
||||
|
||||
fflush(NULL); // flush all opened streams
|
||||
if (criterion_options.no_early_exit)
|
||||
return NULL;
|
||||
else
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
close(fds[1]);
|
||||
return unique_ptr(struct process, { .pid = pid, .in = fds[0] }, close_process);
|
||||
return unique_ptr(struct process,
|
||||
.value = { .proc = proc, .in = pipe_in(pipe) },
|
||||
.dtor = close_process);
|
||||
}
|
||||
|
||||
struct process_status wait_proc(struct process *proc) {
|
||||
int status;
|
||||
waitpid(proc->pid, &status, 0);
|
||||
wait_process(proc->proc, &status);
|
||||
|
||||
if (WIFEXITED(status))
|
||||
return (struct process_status) { .kind = EXIT_STATUS, .status = WEXITSTATUS(status) };
|
||||
return (struct process_status) {
|
||||
.kind = EXIT_STATUS,
|
||||
.status = WEXITSTATUS(status)
|
||||
};
|
||||
|
||||
if (WIFSIGNALED(status))
|
||||
return (struct process_status) { .kind = SIGNAL, .status = WTERMSIG(status) };
|
||||
return (struct process_status) {
|
||||
.kind = SIGNAL,
|
||||
.status = WTERMSIG(status)
|
||||
};
|
||||
|
||||
return (struct process_status) { .kind = STOPPED };
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
# define PROCESS_H_
|
||||
|
||||
# include <stdbool.h>
|
||||
# include "posix-compat.h"
|
||||
|
||||
struct process;
|
||||
|
||||
|
@ -39,7 +40,9 @@ struct process_status {
|
|||
int status;
|
||||
};
|
||||
|
||||
void set_runner_pid(void);
|
||||
void run_worker(struct worker_context *ctx);
|
||||
void set_runner_process(void);
|
||||
void unset_runner_process(void);
|
||||
bool is_runner(void);
|
||||
struct process_status wait_proc(struct process *proc);
|
||||
struct process *spawn_test_worker(struct criterion_test *test,
|
||||
|
|
35
src/report.c
35
src/report.c
|
@ -24,13 +24,17 @@
|
|||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fnmatch.h>
|
||||
#include "criterion/types.h"
|
||||
#include "criterion/stats.h"
|
||||
#include "criterion/logging.h"
|
||||
#include "criterion/options.h"
|
||||
#include "criterion/ordered-set.h"
|
||||
#include "report.h"
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_FNMATCH
|
||||
#include <fnmatch.h>
|
||||
#endif
|
||||
|
||||
#define IMPL_CALL_REPORT_HOOKS(Kind) \
|
||||
IMPL_SECTION_LIMITS(f_report_hook, crit_ ## Kind); \
|
||||
|
@ -46,24 +50,29 @@
|
|||
IMPL_CALL_REPORT_HOOKS(Type); \
|
||||
ReportHook(Type)
|
||||
|
||||
#define log(Type, Arg) \
|
||||
(criterion_options.output_provider->log_ ## Type ?: nothing)(Arg);
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void nothing() {}
|
||||
|
||||
IMPL_REPORT_HOOK(PRE_ALL)(struct criterion_test_set *set) {
|
||||
if (criterion_options.pattern) {
|
||||
FOREACH_SET(struct criterion_suite_set *s, set->suites) {
|
||||
if ((s->suite.data && s->suite.data->disabled) || !s->tests)
|
||||
continue;
|
||||
#ifdef HAVE_FNMATCH
|
||||
void disable_unmatching(struct criterion_test_set *set) {
|
||||
FOREACH_SET(struct criterion_suite_set *s, set->suites) {
|
||||
if ((s->suite.data && s->suite.data->disabled) || !s->tests)
|
||||
continue;
|
||||
|
||||
FOREACH_SET(struct criterion_test *test, s->tests) {
|
||||
if (fnmatch(criterion_options.pattern, test->data->identifier_, 0))
|
||||
test->data->disabled = true;
|
||||
}
|
||||
FOREACH_SET(struct criterion_test *test, s->tests) {
|
||||
if (fnmatch(criterion_options.pattern, test->data->identifier_, 0))
|
||||
test->data->disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
IMPL_REPORT_HOOK(PRE_ALL)(struct criterion_test_set *set) {
|
||||
#ifdef HAVE_FNMATCH
|
||||
if (criterion_options.pattern) {
|
||||
disable_unmatching(set);
|
||||
}
|
||||
#endif
|
||||
log(pre_all, set);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,4 +43,7 @@ DECL_CALL_REPORT_HOOKS(POST_FINI);
|
|||
DECL_CALL_REPORT_HOOKS(POST_SUITE);
|
||||
DECL_CALL_REPORT_HOOKS(POST_ALL);
|
||||
|
||||
#define log(Type, Arg) \
|
||||
(criterion_options.output_provider->log_ ## Type ?: nothing)(Arg);
|
||||
|
||||
#endif /* !REPORT_H_ */
|
||||
|
|
69
src/runner.c
69
src/runner.c
|
@ -23,7 +23,6 @@
|
|||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <csptr/smart_ptr.h>
|
||||
#include "criterion/criterion.h"
|
||||
#include "criterion/options.h"
|
||||
|
@ -34,11 +33,15 @@
|
|||
#include "event.h"
|
||||
#include "process.h"
|
||||
#include "timer.h"
|
||||
#include "posix-compat.h"
|
||||
#include "abort.h"
|
||||
|
||||
IMPL_SECTION_LIMITS(struct criterion_test, criterion_tests);
|
||||
IMPL_SECTION_LIMITS(struct criterion_suite, crit_suites);
|
||||
|
||||
TestSuite(default);
|
||||
// This is here to make the test suite & test sections non-empty
|
||||
TestSuite();
|
||||
Test(,) {};
|
||||
|
||||
int cmp_suite(void *a, void *b) {
|
||||
struct criterion_suite *s1 = a, *s2 = b;
|
||||
|
@ -70,7 +73,11 @@ struct criterion_test_set *criterion_init(void) {
|
|||
insert_ordered_set(suites, &css, sizeof (css));
|
||||
}
|
||||
|
||||
size_t nb_tests = 0;
|
||||
FOREACH_TEST_SEC(test) {
|
||||
if (!*test->category)
|
||||
continue;
|
||||
|
||||
struct criterion_suite_set css = {
|
||||
.suite = { .name = test->category },
|
||||
};
|
||||
|
@ -79,11 +86,9 @@ struct criterion_test_set *criterion_init(void) {
|
|||
s->tests = new_ordered_set(cmp_test, NULL);
|
||||
|
||||
insert_ordered_set(s->tests, test, sizeof(*test));
|
||||
++nb_tests;
|
||||
}
|
||||
|
||||
const size_t nb_tests = SECTION_END(criterion_tests)
|
||||
- SECTION_START(criterion_tests);
|
||||
|
||||
return unique_ptr(struct criterion_test_set, {
|
||||
suites,
|
||||
nb_tests,
|
||||
|
@ -95,7 +100,10 @@ typedef void (*f_test_run)(struct criterion_global_stats *,
|
|||
struct criterion_test *,
|
||||
struct criterion_suite *);
|
||||
|
||||
static void map_tests(struct criterion_test_set *set, struct criterion_global_stats *stats, f_test_run fun) {
|
||||
static void map_tests(struct criterion_test_set *set,
|
||||
struct criterion_global_stats *stats,
|
||||
f_test_run fun) {
|
||||
|
||||
FOREACH_SET(struct criterion_suite_set *s, set->suites) {
|
||||
if (!s->tests)
|
||||
continue;
|
||||
|
@ -122,7 +130,9 @@ static void map_tests(struct criterion_test_set *set, struct criterion_global_st
|
|||
__attribute__ ((always_inline))
|
||||
static inline void nothing() {}
|
||||
|
||||
static void run_test_child(struct criterion_test *test, struct criterion_suite *suite) {
|
||||
static void run_test_child(struct criterion_test *test,
|
||||
struct criterion_suite *suite) {
|
||||
|
||||
send_event(PRE_INIT, NULL, 0);
|
||||
if (suite->data)
|
||||
(suite->data->init ?: nothing)();
|
||||
|
@ -130,8 +140,11 @@ static void run_test_child(struct criterion_test *test, struct criterion_suite *
|
|||
send_event(PRE_TEST, NULL, 0);
|
||||
|
||||
struct timespec_compat ts;
|
||||
timer_start(&ts);
|
||||
(test->test ?: nothing)();
|
||||
if (setup_abort_test()) {
|
||||
timer_start(&ts);
|
||||
(test->test ?: nothing)();
|
||||
}
|
||||
|
||||
double elapsed_time;
|
||||
if (!timer_end(&elapsed_time, &ts))
|
||||
elapsed_time = -1;
|
||||
|
@ -144,7 +157,9 @@ static void run_test_child(struct criterion_test *test, struct criterion_suite *
|
|||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline bool is_disabled(struct criterion_test *t, struct criterion_suite *s) {
|
||||
static inline bool is_disabled(struct criterion_test *t,
|
||||
struct criterion_suite *s) {
|
||||
|
||||
return t->data->disabled || (s->data && s->data->disabled);
|
||||
}
|
||||
|
||||
|
@ -165,7 +180,10 @@ static void run_test(struct criterion_global_stats *stats,
|
|||
smart struct criterion_test_stats *test_stats = test_stats_init(test);
|
||||
|
||||
if (is_disabled(test, suite)) {
|
||||
stat_push_event(stats, suite_stats, test_stats, &(struct event) { .kind = PRE_TEST });
|
||||
stat_push_event(stats,
|
||||
suite_stats,
|
||||
test_stats,
|
||||
&(struct event) { .kind = PRE_INIT });
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -173,14 +191,20 @@ static void run_test(struct criterion_global_stats *stats,
|
|||
if (proc == NULL && !is_runner())
|
||||
return;
|
||||
|
||||
bool test_started = false;
|
||||
bool normal_finish = false;
|
||||
struct event *ev;
|
||||
while ((ev = worker_read_event(proc)) != NULL) {
|
||||
stat_push_event(stats, suite_stats, test_stats, ev);
|
||||
switch (ev->kind) {
|
||||
case PRE_INIT: report(PRE_INIT, test); break;
|
||||
case PRE_TEST: report(PRE_TEST, test); break;
|
||||
case PRE_TEST: report(PRE_TEST, test);
|
||||
test_started = true;
|
||||
break;
|
||||
case ASSERT: report(ASSERT, ev->data); break;
|
||||
case POST_TEST: report(POST_TEST, test_stats); break;
|
||||
case POST_TEST: report(POST_TEST, test_stats);
|
||||
normal_finish = true;
|
||||
break;
|
||||
case POST_FINI: report(POST_FINI, test_stats); break;
|
||||
}
|
||||
sfree(ev);
|
||||
|
@ -188,6 +212,16 @@ static void run_test(struct criterion_global_stats *stats,
|
|||
|
||||
struct process_status status = wait_proc(proc);
|
||||
if (status.kind == SIGNAL) {
|
||||
if (normal_finish || !test_started) {
|
||||
log(other_crash, test_stats);
|
||||
if (!test_started) {
|
||||
stat_push_event(stats,
|
||||
suite_stats,
|
||||
test_stats,
|
||||
&(struct event) { .kind = TEST_CRASH });
|
||||
}
|
||||
return;
|
||||
}
|
||||
test_stats->signal = status.status;
|
||||
if (test->data->signal == 0) {
|
||||
push_event(TEST_CRASH);
|
||||
|
@ -200,10 +234,12 @@ static void run_test(struct criterion_global_stats *stats,
|
|||
}
|
||||
|
||||
static int criterion_run_all_tests_impl(void) {
|
||||
if (resume_child()) // (windows only) resume from the fork
|
||||
return -1;
|
||||
|
||||
smart struct criterion_test_set *set = criterion_init();
|
||||
|
||||
report(PRE_ALL, set);
|
||||
set_runner_pid();
|
||||
|
||||
smart struct criterion_global_stats *stats = stats_init();
|
||||
map_tests(set, stats, run_test);
|
||||
|
@ -216,9 +252,12 @@ static int criterion_run_all_tests_impl(void) {
|
|||
}
|
||||
|
||||
int criterion_run_all_tests(void) {
|
||||
set_runner_process();
|
||||
int res = criterion_run_all_tests_impl();
|
||||
unset_runner_process();
|
||||
|
||||
if (res == -1) // if this is the test worker terminating
|
||||
_exit(0);
|
||||
exit(0);
|
||||
|
||||
return criterion_options.always_succeed || res;
|
||||
}
|
||||
|
|
20
src/stats.c
20
src/stats.c
|
@ -29,7 +29,7 @@
|
|||
|
||||
static void nothing() {};
|
||||
static void push_pre_suite();
|
||||
static void push_pre_test();
|
||||
static void push_pre_init();
|
||||
static void push_assert();
|
||||
static void push_post_test();
|
||||
static void push_test_crash();
|
||||
|
@ -81,8 +81,8 @@ void stat_push_event(s_glob_stats *stats,
|
|||
static void (*const handles[])(s_glob_stats *, s_suite_stats *, s_test_stats *, void *) = {
|
||||
nothing, // PRE_ALL
|
||||
push_pre_suite, // PRE_SUITE
|
||||
nothing, // PRE_INIT
|
||||
push_pre_test, // PRE_TEST
|
||||
push_pre_init, // PRE_INIT
|
||||
nothing, // PRE_TEST
|
||||
push_assert, // ASSERT
|
||||
push_test_crash, // TEST_CRASH
|
||||
push_post_test, // POST_TEST
|
||||
|
@ -106,7 +106,14 @@ static void push_pre_suite(s_glob_stats *stats,
|
|||
++stats->nb_suites;
|
||||
}
|
||||
|
||||
static void push_pre_test(s_glob_stats *stats,
|
||||
__attribute__((always_inline))
|
||||
static inline bool is_disabled(struct criterion_test *t,
|
||||
struct criterion_suite *s) {
|
||||
|
||||
return t->data->disabled || (s->data && s->data->disabled);
|
||||
}
|
||||
|
||||
static void push_pre_init(s_glob_stats *stats,
|
||||
s_suite_stats *suite,
|
||||
s_test_stats *test,
|
||||
UNUSED void *ptr) {
|
||||
|
@ -114,6 +121,11 @@ static void push_pre_test(s_glob_stats *stats,
|
|||
suite->tests = sref(test);
|
||||
++stats->nb_tests;
|
||||
++suite->nb_tests;
|
||||
|
||||
if (is_disabled(test->test, suite->suite)) {
|
||||
++stats->tests_skipped;
|
||||
++suite->tests_skipped;
|
||||
}
|
||||
}
|
||||
|
||||
static void destroy_assert(void *ptr, UNUSED void *meta) {
|
||||
|
|
Loading…
Add table
Reference in a new issue