[v1.3.0] Merge branch 'bleeding' (Version release)
Changelog: criterion: version 1.3.0 * Turned the library into a shared library. * Added extended globbing for --pattern (requires PCRE) * Switched to a CMake build system * Fixed windows builds & output * Added basic windows SEH-to-signal translator
This commit is contained in:
commit
4e36f68ff3
50 changed files with 2060 additions and 379 deletions
|
@ -1,10 +1,12 @@
|
|||
[bumpversion]
|
||||
current_version = 1.2.2
|
||||
current_version = 1.3.0
|
||||
commit = True
|
||||
|
||||
[bumpversion:file:configure.ac]
|
||||
[bumpversion:file:CMakeLists.txt]
|
||||
|
||||
[bumpversion:file:doc/conf.py]
|
||||
|
||||
[bumpversion:file:appveyor.yml]
|
||||
|
||||
[bumpversion:file:README.md]
|
||||
|
||||
|
|
14
.ci/install-libcsptr.sh
Executable file
14
.ci/install-libcsptr.sh
Executable file
|
@ -0,0 +1,14 @@
|
|||
#!/bin/bash
|
||||
repo="https://github.com/Snaipe/libcsptr.git"
|
||||
tag="v2.0.4"
|
||||
|
||||
mkdir dependencies
|
||||
git clone --branch ${tag} --depth 1 ${repo} dependencies/libcsptr &&
|
||||
(
|
||||
cd dependencies/libcsptr &&
|
||||
mkdir build &&
|
||||
cd $_ &&
|
||||
cmake -DCMAKE_INSTALL_PREFIX=$LOCAL_INSTALL "$@" .. &&
|
||||
make &&
|
||||
make install
|
||||
)
|
128
.cmake/Modules/Coveralls.cmake
Normal file
128
.cmake/Modules/Coveralls.cmake
Normal file
|
@ -0,0 +1,128 @@
|
|||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
|
||||
#
|
||||
|
||||
set(_CMAKE_SCRIPT_PATH ${CMAKE_CURRENT_LIST_DIR}) # must be outside coveralls_setup() to get correct path
|
||||
|
||||
#
|
||||
# Param _COVERAGE_SRCS A list of source files that coverage should be collected for.
|
||||
# Param _COVERALLS_UPLOAD Upload the result to coveralls?
|
||||
#
|
||||
|
||||
function(coveralls_setup _COVERAGE_SRCS _COVERALLS_UPLOAD)
|
||||
|
||||
if (ARGC GREATER 2)
|
||||
set(_CMAKE_SCRIPT_PATH ${ARGN})
|
||||
message(STATUS "Coveralls: Using alternate CMake script dir: ${_CMAKE_SCRIPT_PATH}")
|
||||
endif()
|
||||
|
||||
if (NOT EXISTS "${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake")
|
||||
message(FATAL_ERROR "Coveralls: Missing ${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake")
|
||||
endif()
|
||||
|
||||
if (NOT EXISTS "${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake")
|
||||
message(FATAL_ERROR "Coveralls: Missing ${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake")
|
||||
endif()
|
||||
|
||||
# When passing a CMake list to an external process, the list
|
||||
# will be converted from the format "1;2;3" to "1 2 3".
|
||||
# This means the script we're calling won't see it as a list
|
||||
# of sources, but rather just one long path. We remedy this
|
||||
# by replacing ";" with "*" and then reversing that in the script
|
||||
# that we're calling.
|
||||
# http://cmake.3232098.n2.nabble.com/Passing-a-CMake-list-quot-as-is-quot-to-a-custom-target-td6505681.html
|
||||
set(COVERAGE_SRCS_TMP ${_COVERAGE_SRCS})
|
||||
set(COVERAGE_SRCS "")
|
||||
foreach (COVERAGE_SRC ${COVERAGE_SRCS_TMP})
|
||||
set(COVERAGE_SRCS "${COVERAGE_SRCS}*${COVERAGE_SRC}")
|
||||
endforeach()
|
||||
|
||||
#message("Coverage sources: ${COVERAGE_SRCS}")
|
||||
set(COVERALLS_FILE ${PROJECT_BINARY_DIR}/coveralls.json)
|
||||
|
||||
add_custom_target(coveralls_generate
|
||||
|
||||
# Zero the coverage counters.
|
||||
COMMAND ${CMAKE_COMMAND} -DPROJECT_BINARY_DIR="${PROJECT_BINARY_DIR}" -P "${_CMAKE_SCRIPT_PATH}/CoverallsClear.cmake"
|
||||
|
||||
# Run regress tests.
|
||||
COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
|
||||
|
||||
# Generate Gcov and translate it into coveralls JSON.
|
||||
# We do this by executing an external CMake script.
|
||||
# (We don't want this to run at CMake generation time, but after compilation and everything has run).
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DCOVERAGE_SRCS="${COVERAGE_SRCS}" # TODO: This is passed like: "a b c", not "a;b;c"
|
||||
-DCOVERALLS_OUTPUT_FILE="${COVERALLS_FILE}"
|
||||
-DCOV_PATH="${PROJECT_BINARY_DIR}"
|
||||
-DPROJECT_ROOT="${PROJECT_SOURCE_DIR}"
|
||||
-P "${_CMAKE_SCRIPT_PATH}/CoverallsGenerateGcov.cmake"
|
||||
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
COMMENT "Generating coveralls output..."
|
||||
)
|
||||
|
||||
if (_COVERALLS_UPLOAD)
|
||||
message("COVERALLS UPLOAD: ON")
|
||||
|
||||
find_program(CURL_EXECUTABLE curl)
|
||||
|
||||
if (NOT CURL_EXECUTABLE)
|
||||
message(FATAL_ERROR "Coveralls: curl not found! Aborting")
|
||||
endif()
|
||||
|
||||
add_custom_target(coveralls_upload
|
||||
# Upload the JSON to coveralls.
|
||||
COMMAND ${CURL_EXECUTABLE}
|
||||
-S -F json_file=@${COVERALLS_FILE}
|
||||
https://coveralls.io/api/v1/jobs
|
||||
|
||||
DEPENDS coveralls_generate
|
||||
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
|
||||
COMMENT "Uploading coveralls output...")
|
||||
|
||||
add_custom_target(coveralls DEPENDS coveralls_upload)
|
||||
else()
|
||||
message("COVERALLS UPLOAD: OFF")
|
||||
add_custom_target(coveralls DEPENDS coveralls_generate)
|
||||
endif()
|
||||
|
||||
endfunction()
|
||||
|
||||
macro(coveralls_turn_on_coverage)
|
||||
if(NOT (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
AND (NOT "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang"))
|
||||
message(FATAL_ERROR "Coveralls: Compiler ${CMAKE_C_COMPILER_ID} is not GNU gcc! Aborting... You can set this on the command line using CC=/usr/bin/gcc CXX=/usr/bin/g++ cmake <options> ..")
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message(FATAL_ERROR "Coveralls: Code coverage results with an optimised (non-Debug) build may be misleading! Add -DCMAKE_BUILD_TYPE=Debug")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
|
||||
endmacro()
|
||||
|
||||
|
||||
|
31
.cmake/Modules/CoverallsClear.cmake
Normal file
31
.cmake/Modules/CoverallsClear.cmake
Normal file
|
@ -0,0 +1,31 @@
|
|||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
|
||||
#
|
||||
|
||||
# do not follow symlinks in file(GLOB_RECURSE ...)
|
||||
cmake_policy(SET CMP0009 NEW)
|
||||
|
||||
file(GLOB_RECURSE GCDA_FILES "${PROJECT_BINARY_DIR}/*.gcda")
|
||||
if(NOT GCDA_FILES STREQUAL "")
|
||||
file(REMOVE ${GCDA_FILES})
|
||||
endif()
|
480
.cmake/Modules/CoverallsGenerateGcov.cmake
Normal file
480
.cmake/Modules/CoverallsGenerateGcov.cmake
Normal file
|
@ -0,0 +1,480 @@
|
|||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Copyright (C) 2014 Joakim Söderberg <joakim.soderberg@gmail.com>
|
||||
#
|
||||
# This is intended to be run by a custom target in a CMake project like this.
|
||||
# 0. Compile program with coverage support.
|
||||
# 1. Clear coverage data. (Recursively delete *.gcda in build dir)
|
||||
# 2. Run the unit tests.
|
||||
# 3. Run this script specifying which source files the coverage should be performed on.
|
||||
#
|
||||
# This script will then use gcov to generate .gcov files in the directory specified
|
||||
# via the COV_PATH var. This should probably be the same as your cmake build dir.
|
||||
#
|
||||
# It then parses the .gcov files to convert them into the Coveralls JSON format:
|
||||
# https://coveralls.io/docs/api
|
||||
#
|
||||
# Example for running as standalone CMake script from the command line:
|
||||
# (Note it is important the -P is at the end...)
|
||||
# $ cmake -DCOV_PATH=$(pwd)
|
||||
# -DCOVERAGE_SRCS="catcierge_rfid.c;catcierge_timer.c"
|
||||
# -P ../cmake/CoverallsGcovUpload.cmake
|
||||
#
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||
|
||||
|
||||
#
|
||||
# Make sure we have the needed arguments.
|
||||
#
|
||||
if (NOT COVERALLS_OUTPUT_FILE)
|
||||
message(FATAL_ERROR "Coveralls: No coveralls output file specified. Please set COVERALLS_OUTPUT_FILE")
|
||||
endif()
|
||||
|
||||
if (NOT COV_PATH)
|
||||
message(FATAL_ERROR "Coveralls: Missing coverage directory path where gcov files will be generated. Please set COV_PATH")
|
||||
endif()
|
||||
|
||||
if (NOT COVERAGE_SRCS)
|
||||
message(FATAL_ERROR "Coveralls: Missing the list of source files that we should get the coverage data for COVERAGE_SRCS")
|
||||
endif()
|
||||
|
||||
if (NOT PROJECT_ROOT)
|
||||
message(FATAL_ERROR "Coveralls: Missing PROJECT_ROOT.")
|
||||
endif()
|
||||
|
||||
# Since it's not possible to pass a CMake list properly in the
|
||||
# "1;2;3" format to an external process, we have replaced the
|
||||
# ";" with "*", so reverse that here so we get it back into the
|
||||
# CMake list format.
|
||||
string(REGEX REPLACE "\\*" ";" COVERAGE_SRCS ${COVERAGE_SRCS})
|
||||
|
||||
# convert all paths in COVERAGE_SRCS to absolute paths
|
||||
set(COVERAGE_SRCS_TMP "")
|
||||
foreach (COVERAGE_SRC ${COVERAGE_SRCS})
|
||||
if (NOT "${COVERAGE_SRC}" MATCHES "^/")
|
||||
set(COVERAGE_SRC ${PROJECT_ROOT}/${COVERAGE_SRC})
|
||||
endif()
|
||||
list(APPEND COVERAGE_SRCS_TMP ${COVERAGE_SRC})
|
||||
endforeach()
|
||||
set(COVERAGE_SRCS ${COVERAGE_SRCS_TMP})
|
||||
unset(COVERAGE_SRCS_TMP)
|
||||
|
||||
if (NOT DEFINED ENV{GCOV})
|
||||
find_program(GCOV_EXECUTABLE gcov)
|
||||
else()
|
||||
find_program(GCOV_EXECUTABLE $ENV{GCOV})
|
||||
endif()
|
||||
|
||||
if (NOT GCOV_EXECUTABLE)
|
||||
message(FATAL_ERROR "gcov not found! Aborting...")
|
||||
endif()
|
||||
|
||||
find_package(Git)
|
||||
|
||||
set(JSON_REPO_TEMPLATE
|
||||
"{
|
||||
\"head\": {
|
||||
\"id\": \"\@GIT_COMMIT_HASH\@\",
|
||||
\"author_name\": \"\@GIT_AUTHOR_NAME\@\",
|
||||
\"author_email\": \"\@GIT_AUTHOR_EMAIL\@\",
|
||||
\"committer_name\": \"\@GIT_COMMITTER_NAME\@\",
|
||||
\"committer_email\": \"\@GIT_COMMITTER_EMAIL\@\",
|
||||
\"message\": \"\@GIT_COMMIT_MESSAGE\@\"
|
||||
},
|
||||
\"branch\": \"@GIT_BRANCH@\",
|
||||
\"remotes\": []
|
||||
}"
|
||||
)
|
||||
|
||||
# TODO: Fill in git remote data
|
||||
if (GIT_FOUND)
|
||||
# Branch.
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_BRANCH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
macro (git_log_format FORMAT_CHARS VAR_NAME)
|
||||
execute_process(
|
||||
COMMAND ${GIT_EXECUTABLE} log -1 --pretty=format:%${FORMAT_CHARS}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE ${VAR_NAME}
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
endmacro()
|
||||
|
||||
git_log_format(an GIT_AUTHOR_NAME)
|
||||
git_log_format(ae GIT_AUTHOR_EMAIL)
|
||||
git_log_format(cn GIT_COMMITTER_NAME)
|
||||
git_log_format(ce GIT_COMMITTER_EMAIL)
|
||||
git_log_format(B GIT_COMMIT_MESSAGE)
|
||||
git_log_format(H GIT_COMMIT_HASH)
|
||||
|
||||
message("Git exe: ${GIT_EXECUTABLE}")
|
||||
message("Git branch: ${GIT_BRANCH}")
|
||||
message("Git author: ${GIT_AUTHOR_NAME}")
|
||||
message("Git e-mail: ${GIT_AUTHOR_EMAIL}")
|
||||
message("Git commiter name: ${GIT_COMMITTER_NAME}")
|
||||
message("Git commiter e-mail: ${GIT_COMMITTER_EMAIL}")
|
||||
message("Git commit hash: ${GIT_COMMIT_HASH}")
|
||||
message("Git commit message: ${GIT_COMMIT_MESSAGE}")
|
||||
|
||||
string(CONFIGURE ${JSON_REPO_TEMPLATE} JSON_REPO_DATA)
|
||||
else()
|
||||
set(JSON_REPO_DATA "{}")
|
||||
endif()
|
||||
|
||||
############################# Macros #########################################
|
||||
|
||||
#
|
||||
# This macro converts from the full path format gcov outputs:
|
||||
#
|
||||
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
|
||||
#
|
||||
# to the original source file path the .gcov is for:
|
||||
#
|
||||
# /path/to/project/root/subdir/the_file.c
|
||||
#
|
||||
macro(get_source_path_from_gcov_filename _SRC_FILENAME _GCOV_FILENAME)
|
||||
|
||||
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
|
||||
# ->
|
||||
# #path#to#project#root#subdir#the_file.c.gcov
|
||||
get_filename_component(_GCOV_FILENAME_WEXT ${_GCOV_FILENAME} NAME)
|
||||
|
||||
# #path#to#project#root#subdir#the_file.c.gcov -> /path/to/project/root/subdir/the_file.c
|
||||
string(REGEX REPLACE "\\.gcov$" "" SRC_FILENAME_TMP ${_GCOV_FILENAME_WEXT})
|
||||
string(REGEX REPLACE "\#" "/" SRC_FILENAME_TMP ${SRC_FILENAME_TMP})
|
||||
string(REGEX REPLACE "~" ":" SRC_FILENAME_TMP ${SRC_FILENAME_TMP})
|
||||
set(${_SRC_FILENAME} "${SRC_FILENAME_TMP}")
|
||||
endmacro()
|
||||
|
||||
##############################################################################
|
||||
|
||||
# Get the coverage data.
|
||||
file(GLOB_RECURSE GCDA_FILES "${COV_PATH}/*.gcda")
|
||||
message("GCDA files:")
|
||||
|
||||
# Get a list of all the object directories needed by gcov
|
||||
# (The directories the .gcda files and .o files are found in)
|
||||
# and run gcov on those.
|
||||
foreach(GCDA ${GCDA_FILES})
|
||||
message("Process: ${GCDA}")
|
||||
message("------------------------------------------------------------------------------")
|
||||
get_filename_component(GCDA_DIR ${GCDA} PATH)
|
||||
|
||||
#
|
||||
# The -p below refers to "Preserve path components",
|
||||
# This means that the generated gcov filename of a source file will
|
||||
# keep the original files entire filepath, but / is replaced with #.
|
||||
# Example:
|
||||
#
|
||||
# /path/to/project/root/build/CMakeFiles/the_file.dir/subdir/the_file.c.gcda
|
||||
# ------------------------------------------------------------------------------
|
||||
# File '/path/to/project/root/subdir/the_file.c'
|
||||
# Lines executed:68.34% of 199
|
||||
# /path/to/project/root/subdir/the_file.c:creating '#path#to#project#root#subdir#the_file.c.gcov'
|
||||
#
|
||||
# If -p is not specified then the file is named only "the_file.c.gcov"
|
||||
#
|
||||
execute_process(
|
||||
COMMAND ${GCOV_EXECUTABLE} -p -o ${GCDA_DIR} ${GCDA}
|
||||
WORKING_DIRECTORY ${COV_PATH}
|
||||
)
|
||||
endforeach()
|
||||
|
||||
# TODO: Make these be absolute path
|
||||
file(GLOB ALL_GCOV_FILES ${COV_PATH}/*.gcov)
|
||||
|
||||
# Get only the filenames to use for filtering.
|
||||
#set(COVERAGE_SRCS_NAMES "")
|
||||
#foreach (COVSRC ${COVERAGE_SRCS})
|
||||
# get_filename_component(COVSRC_NAME ${COVSRC} NAME)
|
||||
# message("${COVSRC} -> ${COVSRC_NAME}")
|
||||
# list(APPEND COVERAGE_SRCS_NAMES "${COVSRC_NAME}")
|
||||
#endforeach()
|
||||
|
||||
#
|
||||
# Filter out all but the gcov files we want.
|
||||
#
|
||||
# We do this by comparing the list of COVERAGE_SRCS filepaths that the
|
||||
# user wants the coverage data for with the paths of the generated .gcov files,
|
||||
# so that we only keep the relevant gcov files.
|
||||
#
|
||||
# Example:
|
||||
# COVERAGE_SRCS =
|
||||
# /path/to/project/root/subdir/the_file.c
|
||||
#
|
||||
# ALL_GCOV_FILES =
|
||||
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
|
||||
# /path/to/project/root/build/#path#to#project#root#subdir#other_file.c.gcov
|
||||
#
|
||||
# Result should be:
|
||||
# GCOV_FILES =
|
||||
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
|
||||
#
|
||||
set(GCOV_FILES "")
|
||||
#message("Look in coverage sources: ${COVERAGE_SRCS}")
|
||||
message("\nFilter out unwanted GCOV files:")
|
||||
message("===============================")
|
||||
|
||||
set(COVERAGE_SRCS_REMAINING ${COVERAGE_SRCS})
|
||||
|
||||
foreach (GCOV_FILE ${ALL_GCOV_FILES})
|
||||
|
||||
#
|
||||
# /path/to/project/root/build/#path#to#project#root#subdir#the_file.c.gcov
|
||||
# ->
|
||||
# /path/to/project/root/subdir/the_file.c
|
||||
get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
|
||||
file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
|
||||
|
||||
# Is this in the list of source files?
|
||||
# TODO: We want to match against relative path filenames from the source file root...
|
||||
list(FIND COVERAGE_SRCS ${GCOV_SRC_PATH} WAS_FOUND)
|
||||
|
||||
if (NOT WAS_FOUND EQUAL -1)
|
||||
message("YES: ${GCOV_FILE}")
|
||||
list(APPEND GCOV_FILES ${GCOV_FILE})
|
||||
|
||||
# We remove it from the list, so we don't bother searching for it again.
|
||||
# Also files left in COVERAGE_SRCS_REMAINING after this loop ends should
|
||||
# have coverage data generated from them (no lines are covered).
|
||||
list(REMOVE_ITEM COVERAGE_SRCS_REMAINING ${GCOV_SRC_PATH})
|
||||
else()
|
||||
message("NO: ${GCOV_FILE}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# TODO: Enable setting these
|
||||
set(JSON_SERVICE_NAME "travis-ci")
|
||||
set(JSON_SERVICE_JOB_ID $ENV{TRAVIS_JOB_ID})
|
||||
set(JSON_REPO_TOKEN $ENV{COVERALLS_REPO_TOKEN})
|
||||
|
||||
set(JSON_TEMPLATE
|
||||
"{
|
||||
\"repo_token\": \"\@JSON_REPO_TOKEN\@\",
|
||||
\"service_name\": \"\@JSON_SERVICE_NAME\@\",
|
||||
\"service_job_id\": \"\@JSON_SERVICE_JOB_ID\@\",
|
||||
\"source_files\": \@JSON_GCOV_FILES\@,
|
||||
\"git\": \@JSON_REPO_DATA\@
|
||||
}"
|
||||
)
|
||||
|
||||
set(SRC_FILE_TEMPLATE
|
||||
"{
|
||||
\"name\": \"\@GCOV_SRC_REL_PATH\@\",
|
||||
\"source_digest\": \"\@GCOV_CONTENTS_MD5\@\",
|
||||
\"coverage\": \@GCOV_FILE_COVERAGE\@
|
||||
}"
|
||||
)
|
||||
|
||||
message("\nGenerate JSON for files:")
|
||||
message("=========================")
|
||||
|
||||
set(JSON_GCOV_FILES "[")
|
||||
|
||||
# Read the GCOV files line by line and get the coverage data.
|
||||
foreach (GCOV_FILE ${GCOV_FILES})
|
||||
|
||||
get_source_path_from_gcov_filename(GCOV_SRC_PATH ${GCOV_FILE})
|
||||
file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
|
||||
|
||||
# The new coveralls API doesn't need the entire source (Yay!)
|
||||
# However, still keeping that part for now. Will cleanup in the future.
|
||||
file(MD5 "${GCOV_SRC_PATH}" GCOV_CONTENTS_MD5)
|
||||
message("MD5: ${GCOV_SRC_PATH} = ${GCOV_CONTENTS_MD5}")
|
||||
|
||||
# Loads the gcov file as a list of lines.
|
||||
# (We first open the file and replace all occurences of [] with _
|
||||
# because CMake will fail to parse a line containing unmatched brackets...
|
||||
# also the \ to escaped \n in macros screws up things.)
|
||||
# https://public.kitware.com/Bug/view.php?id=15369
|
||||
file(READ ${GCOV_FILE} GCOV_CONTENTS)
|
||||
string(REPLACE "[" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
|
||||
string(REPLACE "]" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
|
||||
string(REPLACE "\\" "_" GCOV_CONTENTS "${GCOV_CONTENTS}")
|
||||
|
||||
# Remove file contents to avoid encoding issues (cmake 2.8 has no ENCODING option)
|
||||
string(REGEX REPLACE "([^:]*):([^:]*):([^\n]*)\n" "\\1:\\2: \n" GCOV_CONTENTS "${GCOV_CONTENTS}")
|
||||
file(WRITE ${GCOV_FILE}_tmp "${GCOV_CONTENTS}")
|
||||
|
||||
file(STRINGS ${GCOV_FILE}_tmp GCOV_LINES)
|
||||
list(LENGTH GCOV_LINES LINE_COUNT)
|
||||
|
||||
# Instead of trying to parse the source from the
|
||||
# gcov file, simply read the file contents from the source file.
|
||||
# (Parsing it from the gcov is hard because C-code uses ; in many places
|
||||
# which also happens to be the same as the CMake list delimeter).
|
||||
file(READ ${GCOV_SRC_PATH} GCOV_FILE_SOURCE)
|
||||
|
||||
string(REPLACE "\\" "\\\\" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
string(REGEX REPLACE "\"" "\\\\\"" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
string(REPLACE "\t" "\\\\t" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
string(REPLACE "\r" "\\\\r" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
string(REPLACE "\n" "\\\\n" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
# According to http://json.org/ these should be escaped as well.
|
||||
# Don't know how to do that in CMake however...
|
||||
#string(REPLACE "\b" "\\\\b" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
#string(REPLACE "\f" "\\\\f" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
#string(REGEX REPLACE "\u([a-fA-F0-9]{4})" "\\\\u\\1" GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}")
|
||||
|
||||
# We want a json array of coverage data as a single string
|
||||
# start building them from the contents of the .gcov
|
||||
set(GCOV_FILE_COVERAGE "[")
|
||||
|
||||
set(GCOV_LINE_COUNT 1) # Line number for the .gcov.
|
||||
set(DO_SKIP 0)
|
||||
foreach (GCOV_LINE ${GCOV_LINES})
|
||||
#message("${GCOV_LINE}")
|
||||
# Example of what we're parsing:
|
||||
# Hitcount |Line | Source
|
||||
# " 8: 26: if (!allowed || (strlen(allowed) == 0))"
|
||||
string(REGEX REPLACE
|
||||
"^([^:]*):([^:]*):(.*)$"
|
||||
"\\1;\\2;\\3"
|
||||
RES
|
||||
"${GCOV_LINE}")
|
||||
|
||||
# Check if we should exclude lines using the Lcov syntax.
|
||||
string(REGEX MATCH "LCOV_EXCL_START" START_SKIP "${GCOV_LINE}")
|
||||
string(REGEX MATCH "LCOV_EXCL_END" END_SKIP "${GCOV_LINE}")
|
||||
string(REGEX MATCH "LCOV_EXCL_LINE" LINE_SKIP "${GCOV_LINE}")
|
||||
|
||||
set(RESET_SKIP 0)
|
||||
if (LINE_SKIP AND NOT DO_SKIP)
|
||||
set(DO_SKIP 1)
|
||||
set(RESET_SKIP 1)
|
||||
endif()
|
||||
|
||||
if (START_SKIP)
|
||||
set(DO_SKIP 1)
|
||||
message("${GCOV_LINE_COUNT}: Start skip")
|
||||
endif()
|
||||
|
||||
if (END_SKIP)
|
||||
set(DO_SKIP 0)
|
||||
endif()
|
||||
|
||||
list(LENGTH RES RES_COUNT)
|
||||
|
||||
if (RES_COUNT GREATER 2)
|
||||
list(GET RES 0 HITCOUNT)
|
||||
list(GET RES 1 LINE)
|
||||
list(GET RES 2 SOURCE)
|
||||
|
||||
string(STRIP ${HITCOUNT} HITCOUNT)
|
||||
string(STRIP ${LINE} LINE)
|
||||
|
||||
# Lines with 0 line numbers are metadata and can be ignored.
|
||||
if (NOT ${LINE} EQUAL 0)
|
||||
|
||||
if (DO_SKIP)
|
||||
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
|
||||
else()
|
||||
# Translate the hitcount into valid JSON values.
|
||||
if (${HITCOUNT} STREQUAL "#####" OR ${HITCOUNT} STREQUAL "=====")
|
||||
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}0, ")
|
||||
elseif (${HITCOUNT} STREQUAL "-")
|
||||
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
|
||||
else()
|
||||
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}${HITCOUNT}, ")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Failed to properly parse line (RES_COUNT = ${RES_COUNT}) ${GCOV_FILE}:${GCOV_LINE_COUNT}\n-->${GCOV_LINE}")
|
||||
endif()
|
||||
|
||||
if (RESET_SKIP)
|
||||
set(DO_SKIP 0)
|
||||
endif()
|
||||
math(EXPR GCOV_LINE_COUNT "${GCOV_LINE_COUNT}+1")
|
||||
endforeach()
|
||||
|
||||
message("${GCOV_LINE_COUNT} of ${LINE_COUNT} lines read!")
|
||||
|
||||
# Advanced way of removing the trailing comma in the JSON array.
|
||||
# "[1, 2, 3, " -> "[1, 2, 3"
|
||||
string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
|
||||
|
||||
# Append the trailing ] to complete the JSON array.
|
||||
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
|
||||
|
||||
# Generate the final JSON for this file.
|
||||
message("Generate JSON for file: ${GCOV_SRC_REL_PATH}...")
|
||||
string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
|
||||
|
||||
set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
|
||||
endforeach()
|
||||
|
||||
# Loop through all files we couldn't find any coverage for
|
||||
# as well, and generate JSON for those as well with 0% coverage.
|
||||
foreach(NOT_COVERED_SRC ${COVERAGE_SRCS_REMAINING})
|
||||
|
||||
# Set variables for json replacement
|
||||
set(GCOV_SRC_PATH ${NOT_COVERED_SRC})
|
||||
file(MD5 "${GCOV_SRC_PATH}" GCOV_CONTENTS_MD5)
|
||||
file(RELATIVE_PATH GCOV_SRC_REL_PATH "${PROJECT_ROOT}" "${GCOV_SRC_PATH}")
|
||||
|
||||
# Loads the source file as a list of lines.
|
||||
file(STRINGS ${NOT_COVERED_SRC} SRC_LINES)
|
||||
|
||||
set(GCOV_FILE_COVERAGE "[")
|
||||
set(GCOV_FILE_SOURCE "")
|
||||
|
||||
foreach (SOURCE ${SRC_LINES})
|
||||
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}null, ")
|
||||
|
||||
string(REPLACE "\\" "\\\\" SOURCE "${SOURCE}")
|
||||
string(REGEX REPLACE "\"" "\\\\\"" SOURCE "${SOURCE}")
|
||||
string(REPLACE "\t" "\\\\t" SOURCE "${SOURCE}")
|
||||
string(REPLACE "\r" "\\\\r" SOURCE "${SOURCE}")
|
||||
set(GCOV_FILE_SOURCE "${GCOV_FILE_SOURCE}${SOURCE}\\n")
|
||||
endforeach()
|
||||
|
||||
# Remove trailing comma, and complete JSON array with ]
|
||||
string(REGEX REPLACE ",[ ]*$" "" GCOV_FILE_COVERAGE ${GCOV_FILE_COVERAGE})
|
||||
set(GCOV_FILE_COVERAGE "${GCOV_FILE_COVERAGE}]")
|
||||
|
||||
# Generate the final JSON for this file.
|
||||
message("Generate JSON for non-gcov file: ${NOT_COVERED_SRC}...")
|
||||
string(CONFIGURE ${SRC_FILE_TEMPLATE} FILE_JSON)
|
||||
set(JSON_GCOV_FILES "${JSON_GCOV_FILES}${FILE_JSON}, ")
|
||||
endforeach()
|
||||
|
||||
# Get rid of trailing comma.
|
||||
string(REGEX REPLACE ",[ ]*$" "" JSON_GCOV_FILES ${JSON_GCOV_FILES})
|
||||
set(JSON_GCOV_FILES "${JSON_GCOV_FILES}]")
|
||||
|
||||
# Generate the final complete JSON!
|
||||
message("Generate final JSON...")
|
||||
string(CONFIGURE ${JSON_TEMPLATE} JSON)
|
||||
|
||||
file(WRITE "${COVERALLS_OUTPUT_FILE}" "${JSON}")
|
||||
message("###########################################################################")
|
||||
message("Generated coveralls JSON containing coverage data:")
|
||||
message("${COVERALLS_OUTPUT_FILE}")
|
||||
message("###########################################################################")
|
||||
|
31
.cmake/Modules/FindLibcsptr.cmake
Normal file
31
.cmake/Modules/FindLibcsptr.cmake
Normal file
|
@ -0,0 +1,31 @@
|
|||
# Copyright (C) 2015 Franklin "Snaipe" Mathieu.
|
||||
# Redistribution and use of this file is allowed according to the terms of the MIT license.
|
||||
# For details see the LICENSE file distributed with Criterion.
|
||||
|
||||
# - Find libcsptr
|
||||
# Find the native libcsptr headers and libraries.
|
||||
#
|
||||
# CSPTR_INCLUDE_DIRS - where to find smart_ptr.h, etc.
|
||||
# CSPTR_LIBRARIES - List of libraries when using libcsptr.
|
||||
# CSPTR_FOUND - True if libcsptr has been found.
|
||||
|
||||
# Look for the header file.
|
||||
FIND_PATH(CSPTR_INCLUDE_DIR csptr/smart_ptr.h PATH_SUFFIXES csptr)
|
||||
|
||||
# Look for the library.
|
||||
FIND_LIBRARY(CSPTR_LIBRARY NAMES csptr)
|
||||
|
||||
# Handle the QUIETLY and REQUIRED arguments and set CSPTR_FOUND to TRUE if all listed variables are TRUE.
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(CSPTR DEFAULT_MSG CSPTR_LIBRARY CSPTR_INCLUDE_DIR)
|
||||
|
||||
# Copy the results to the output variables.
|
||||
IF(CSPTR_FOUND)
|
||||
SET(CSPTR_LIBRARIES ${CSPTR_LIBRARY})
|
||||
SET(CSPTR_INCLUDE_DIRS ${CSPTR_INCLUDE_DIR})
|
||||
ELSE(CSPTR_FOUND)
|
||||
SET(CSPTR_LIBRARIES)
|
||||
SET(CSPTR_INCLUDE_DIRS)
|
||||
ENDIF(CSPTR_FOUND)
|
||||
|
||||
MARK_AS_ADVANCED(CSPTR_INCLUDE_DIRS CSPTR_LIBRARIES)
|
62
.cmake/Modules/FindLibintl.cmake
Normal file
62
.cmake/Modules/FindLibintl.cmake
Normal file
|
@ -0,0 +1,62 @@
|
|||
# Try to find Libintl functionality
|
||||
# Once done this will define
|
||||
#
|
||||
# LIBINTL_FOUND - system has Libintl
|
||||
# LIBINTL_INCLUDE_DIR - Libintl include directory
|
||||
# LIBINTL_LIBRARIES - Libraries needed to use Libintl
|
||||
#
|
||||
# TODO: This will enable translations only if Gettext functionality is
|
||||
# present in libc. Must have more robust system for release, where Gettext
|
||||
# functionality can also reside in standalone Gettext library, or the one
|
||||
# embedded within kdelibs (cf. gettext.m4 from Gettext source).
|
||||
|
||||
# Copyright (c) 2006, Chusslove Illich, <caslav.ilic@gmx.net>
|
||||
# Copyright (c) 2007, Alexander Neundorf, <neundorf@kde.org>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
|
||||
|
||||
SET(LIBINTL_SEARCH_PATHS
|
||||
/usr/local/opt/
|
||||
/usr/local/
|
||||
/usr/
|
||||
)
|
||||
|
||||
if(LIBINTL_INCLUDE_DIR AND LIBINTL_LIB_FOUND)
|
||||
set(Libintl_FIND_QUIETLY TRUE)
|
||||
endif(LIBINTL_INCLUDE_DIR AND LIBINTL_LIB_FOUND)
|
||||
|
||||
find_path(LIBINTL_INCLUDE_DIR libintl.h
|
||||
HINTS
|
||||
PATH_SUFFIXES gettext/include
|
||||
PATHS ${LIBINTL_SEARCH_PATHS}
|
||||
)
|
||||
|
||||
set(LIBINTL_LIB_FOUND FALSE)
|
||||
|
||||
if(LIBINTL_INCLUDE_DIR)
|
||||
include(CheckSymbolExists)
|
||||
check_symbol_exists(dgettext libintl.h LIBINTL_LIBC_HAS_DGETTEXT)
|
||||
|
||||
if (LIBINTL_LIBC_HAS_DGETTEXT)
|
||||
set(LIBINTL_LIBRARIES)
|
||||
set(LIBINTL_LIB_FOUND TRUE)
|
||||
else (LIBINTL_LIBC_HAS_DGETTEXT)
|
||||
find_library(LIBINTL_LIBRARIES
|
||||
NAMES intl
|
||||
HINTS
|
||||
PATH_SUFFIXES gettext/lib
|
||||
PATHS ${LIBINTL_SEARCH_PATHS}
|
||||
)
|
||||
if(LIBINTL_LIBRARIES)
|
||||
set(LIBINTL_LIB_FOUND TRUE)
|
||||
endif(LIBINTL_LIBRARIES)
|
||||
endif (LIBINTL_LIBC_HAS_DGETTEXT)
|
||||
|
||||
endif(LIBINTL_INCLUDE_DIR)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Libintl DEFAULT_MSG LIBINTL_INCLUDE_DIR LIBINTL_LIB_FOUND)
|
||||
|
||||
mark_as_advanced(LIBINTL_INCLUDE_DIR LIBINTL_LIBRARIES LIBINTL_LIBC_HAS_DGETTEXT LIBINTL_LIB_FOUND)
|
37
.cmake/Modules/FindPCRE.cmake
Normal file
37
.cmake/Modules/FindPCRE.cmake
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Copyright (C) 2007-2009 LuaDist.
|
||||
# Created by Peter Kapec <kapecp@gmail.com>
|
||||
# Redistribution and use of this file is allowed according to the terms of the MIT license.
|
||||
# For details see the COPYRIGHT file distributed with LuaDist.
|
||||
# Note:
|
||||
# Searching headers and libraries is very simple and is NOT as powerful as scripts
|
||||
# distributed with CMake, because LuaDist defines directories to search for.
|
||||
# Everyone is encouraged to contact the author with improvements. Maybe this file
|
||||
# becomes part of CMake distribution sometimes.
|
||||
|
||||
# - Find pcre
|
||||
# Find the native PCRE headers and libraries.
|
||||
#
|
||||
# PCRE_INCLUDE_DIRS - where to find pcre.h, etc.
|
||||
# PCRE_LIBRARIES - List of libraries when using pcre.
|
||||
# PCRE_FOUND - True if pcre found.
|
||||
|
||||
# Look for the header file.
|
||||
FIND_PATH(PCRE_INCLUDE_DIR NAMES pcre.h)
|
||||
|
||||
# Look for the library.
|
||||
FIND_LIBRARY(PCRE_LIBRARY NAMES pcre)
|
||||
|
||||
# Handle the QUIETLY and REQUIRED arguments and set PCRE_FOUND to TRUE if all listed variables are TRUE.
|
||||
INCLUDE(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_INCLUDE_DIR)
|
||||
|
||||
# Copy the results to the output variables.
|
||||
IF(PCRE_FOUND)
|
||||
SET(PCRE_LIBRARIES ${PCRE_LIBRARY})
|
||||
SET(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR})
|
||||
ELSE(PCRE_FOUND)
|
||||
SET(PCRE_LIBRARIES)
|
||||
SET(PCRE_INCLUDE_DIRS)
|
||||
ENDIF(PCRE_FOUND)
|
||||
|
||||
MARK_AS_ADVANCED(PCRE_INCLUDE_DIRS PCRE_LIBRARIES)
|
285
.cmake/Modules/GettextTranslate.cmake
Normal file
285
.cmake/Modules/GettextTranslate.cmake
Normal file
|
@ -0,0 +1,285 @@
|
|||
# Copyright (c) 2012, Jarryd Beck
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
# Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
# This module creates build rules for updating translation files made
|
||||
# with gettext
|
||||
# In your top level CMakeLists.txt, do
|
||||
# include(GettextTranslate)
|
||||
# then in any po directory where you want things to be translated, write
|
||||
# GettextTranslate()
|
||||
#
|
||||
# This module also finds the gettext binaries. If these are in a non-standard
|
||||
# location, you can define the following variables to provide paths to search
|
||||
# in
|
||||
# GettextTranslate_BINARIES --- a path in which to look for every program
|
||||
# GettextTranslate_XGETTEXT --- the xgettext program
|
||||
# GettextTranslate_MSGINIT --- the msginit program
|
||||
# GettextTranslate_MSGFILTER --- the msgfilter program
|
||||
# GettextTranslate_MSGCONV --- the msgconv program
|
||||
# GettextTranslate_MSGMERGE --- the msgmerge program
|
||||
# GettextTranslate_MSGFMT --- the msgfmt program
|
||||
# these are searched first before $PATH, so set this if you have your own
|
||||
# version that overrides the system version
|
||||
#
|
||||
# it reads variables from Makevars, one of the most important being DOMAIN
|
||||
# it reads the languages to generate from LINGUAS
|
||||
#
|
||||
# it adds the following targets
|
||||
# update-po
|
||||
# update-gmo
|
||||
# ${DOMAIN}-pot.update
|
||||
# generate-${DOMAIN}-${lang}-po
|
||||
# generate-${DOMAIN}-${lang}-gmo
|
||||
#
|
||||
# where ${DOMAIN} is the DOMAIN variable read from Makevars
|
||||
# and ${lang} is each language mentioned in LINGUAS
|
||||
#
|
||||
# if you want update-gmo to be added to the "all" target, then define the
|
||||
# variable GettextTranslate_ALL before including this file
|
||||
#
|
||||
# by default, the gmo files are built in the source directory. If you want
|
||||
# them to be built in the binary directory, then define the variable
|
||||
# GettextTranslate_GMO_BINARY
|
||||
|
||||
|
||||
|
||||
# add the update-po and update-gmo targets, the actual files that need to
|
||||
# depend on this will be added as we go
|
||||
|
||||
if (DEFINED GettextTranslate_ALL)
|
||||
set(_addToALL "ALL")
|
||||
endif()
|
||||
|
||||
add_custom_target(update-po)
|
||||
add_custom_target(update-gmo ${_addToALL})
|
||||
|
||||
#look for all the programs
|
||||
#xgettext, msginit, msgfilter, msgconv, msgmerge, msgfmt
|
||||
|
||||
function(REQUIRE_BINARY binname varname)
|
||||
if (defined ${${varname}-NOTFOUND})
|
||||
message(FATAL_ERROR "Could not find " binname)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
find_program(GettextTranslate_XGETTEXT_EXECUTABLE xgettext
|
||||
HINTS ${GettextTranslate_XGETTEXT} ${GettextTranslate_BINARIES}
|
||||
)
|
||||
REQUIRE_BINARY(xgettext GettextTranslate_XGETTEXT_EXECUTABLE)
|
||||
|
||||
find_program(GettextTranslate_MSGINIT_EXECUTABLE msginit
|
||||
HINTS ${GettextTranslate_MSGINIT} ${GettextTranslate_BINARIES}
|
||||
)
|
||||
REQUIRE_BINARY(msginit GettextTranslate_MSGINIT_EXECUTABLE)
|
||||
|
||||
find_program(GettextTranslate_MSGFILTER_EXECUTABLE msgfilter
|
||||
HINTS ${GettextTranslate_MSGFILTER} ${GettextTranslate_BINARIES}
|
||||
)
|
||||
REQUIRE_BINARY(msgfilter GettextTranslate_MSGFILTER_EXECUTABLE)
|
||||
|
||||
find_program(GettextTranslate_MSGCONV_EXECUTABLE msgconv
|
||||
HINTS ${GettextTranslate_MSGCONV} ${GettextTranslate_BINARIES}
|
||||
)
|
||||
REQUIRE_BINARY(msgconv GettextTranslate_MSGCONV_EXECUTABLE)
|
||||
|
||||
find_program(GettextTranslate_MSGMERGE_EXECUTABLE msgmerge
|
||||
HINTS ${GettextTranslate_MSGMERGE} ${GettextTranslate_BINARIES}
|
||||
)
|
||||
REQUIRE_BINARY(msgmerge GettextTranslate_MSGMERGE_EXECUTABLE)
|
||||
|
||||
find_program(GettextTranslate_MSGFMT_EXECUTABLE msgfmt
|
||||
HINTS ${GettextTranslate_MSGFMT} ${GettextTranslate_BINARIES}
|
||||
)
|
||||
REQUIRE_BINARY(msgfmt GettextTranslate_MSGFMT_EXECUTABLE)
|
||||
|
||||
mark_as_advanced(
|
||||
GettextTranslate_MSGCONV_EXECUTABLE
|
||||
GettextTranslate_MSGFILTER_EXECUTABLE
|
||||
GettextTranslate_MSGFMT_EXECUTABLE
|
||||
GettextTranslate_MSGINIT_EXECUTABLE
|
||||
GettextTranslate_MSGMERGE_EXECUTABLE
|
||||
GettextTranslate_XGETTEXT_EXECUTABLE
|
||||
)
|
||||
|
||||
macro(GettextTranslate)
|
||||
|
||||
if(GettextTranslate_GMO_BINARY)
|
||||
set (GMO_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
else()
|
||||
set (GMO_BUILD_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endif()
|
||||
|
||||
if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in)
|
||||
message(FATAL_ERROR "There is no POTFILES.in in
|
||||
${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
endif()
|
||||
|
||||
if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Makevars)
|
||||
message(FATAL_ERROR "There is no Makevars in ${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
endif()
|
||||
|
||||
file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/Makevars makevars
|
||||
REGEX "^[^=]+=(.*)$"
|
||||
)
|
||||
|
||||
foreach(makevar ${makevars})
|
||||
string(REGEX REPLACE "^([^= ]+) =[ ]?(.*)$" "\\1" MAKEVAR_KEY ${makevar})
|
||||
string(REGEX REPLACE "^([^= ]+) =[ ]?(.*)$" "\\2"
|
||||
MAKEVAR_${MAKEVAR_KEY} ${makevar})
|
||||
endforeach()
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/POTFILES
|
||||
COPYONLY
|
||||
)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/LINGUAS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/LINGUAS
|
||||
COPYONLY
|
||||
)
|
||||
|
||||
#set the directory to not clean
|
||||
#set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
# PROPERTY CLEAN_NO_CUSTOM true)
|
||||
|
||||
file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in potfiles
|
||||
REGEX "^[^#].*"
|
||||
)
|
||||
|
||||
foreach(potfile ${potfiles})
|
||||
list(APPEND source_translatable
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${MAKEVAR_top_builddir}/${potfile})
|
||||
endforeach()
|
||||
|
||||
set(TEMPLATE_FILE ${MAKEVAR_DOMAIN}.pot)
|
||||
set(TEMPLATE_FILE_ABS ${CMAKE_CURRENT_SOURCE_DIR}/${TEMPLATE_FILE})
|
||||
string(REGEX MATCHALL "[^ ]+" XGETTEXT_OPTS ${MAKEVAR_XGETTEXT_OPTIONS})
|
||||
#add_custom_target(${MAKEVAR_DOMAIN}.pot-update DEPENDS
|
||||
# ${TEMPLATE_FILE_ABS}
|
||||
#)
|
||||
|
||||
add_custom_target(${MAKEVAR_DOMAIN}.pot-update
|
||||
COMMAND ${GettextTranslate_XGETTEXT_EXECUTABLE} ${XGETTEXT_OPTS}
|
||||
-o ${TEMPLATE_FILE_ABS}
|
||||
--default-domain=${MAKEVAR_DOMAIN}
|
||||
--add-comments=TRANSLATORS:
|
||||
--copyright-holder=${MAKEVAR_COPYRIGHT_HOLDER}
|
||||
--msgid-bugs-address="${MAKEVAR_MSGID_BUGS_ADDRESS}"
|
||||
--directory=${MAKEVAR_top_builddir}
|
||||
--files-from=${CMAKE_CURRENT_BINARY_DIR}/POTFILES
|
||||
--package-version=${VERSION}
|
||||
--package-name=${CMAKE_PROJECT_NAME}
|
||||
DEPENDS ${source_translatable}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
#add_custom_command(OUTPUT ${TEMPLATE_FILE_ABS}
|
||||
# COMMAND ${GettextTranslate_XGETTEXT_EXECUTABLE} ${XGETTEXT_OPTS}
|
||||
# -o ${TEMPLATE_FILE_ABS}
|
||||
# --default-domain=${MAKEVAR_DOMAIN}
|
||||
# --add-comments=TRANSLATORS:
|
||||
# --copyright-holder=${MAKEVAR_COPYRIGHT_HOLDER}
|
||||
# --msgid-bugs-address="${MAKEVAR_MSGID_BUGS_ADDRESS}"
|
||||
# --directory=${MAKEVAR_top_builddir}
|
||||
# --files-from=${CMAKE_CURRENT_BINARY_DIR}/POTFILES
|
||||
# --package-version=${VERSION}
|
||||
# --package-name=${CMAKE_PROJECT_NAME}
|
||||
# DEPENDS ${source_translatable}
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/POTFILES.in
|
||||
# WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
#)
|
||||
|
||||
#add_dependencies(update-po ${MAKEVAR_DOMAIN}.pot-update)
|
||||
|
||||
file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/LINGUAS LINGUAS
|
||||
REGEX "^[^#].*")
|
||||
string(REGEX MATCHALL "[^ ]+" languages ${LINGUAS})
|
||||
|
||||
foreach(lang ${languages})
|
||||
set(PO_FILE_NAME "${CMAKE_CURRENT_SOURCE_DIR}/${lang}.po")
|
||||
set(GMO_FILE_NAME "${GMO_BUILD_DIR}/${lang}.gmo")
|
||||
set(PO_TARGET "generate-${MAKEVAR_DOMAIN}-${lang}-po")
|
||||
set(GMO_TARGET "generate-${MAKEVAR_DOMAIN}-${lang}-gmo")
|
||||
list(APPEND po_files ${PO_TARGET})
|
||||
list(APPEND gmo_files ${GMO_TARGET})
|
||||
|
||||
if(${lang} MATCHES "en@(.*)quot")
|
||||
|
||||
add_custom_command(OUTPUT ${lang}.insert-header
|
||||
COMMAND
|
||||
sed -e "'/^#/d'" -e 's/HEADER/${lang}.header/g'
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/insert-header.sin > ${lang}.insert-header
|
||||
)
|
||||
|
||||
#generate the en@quot files
|
||||
add_custom_target(${PO_TARGET}
|
||||
COMMAND
|
||||
${GettextTranslate_MSGINIT_EXECUTABLE} -i ${TEMPLATE_FILE_ABS}
|
||||
--no-translator -l ${lang}
|
||||
-o - 2>/dev/null
|
||||
| sed -f ${CMAKE_CURRENT_BINARY_DIR}/${lang}.insert-header
|
||||
| ${GettextTranslate_MSGCONV_EXECUTABLE} -t UTF-8
|
||||
| ${GettextTranslate_MSGFILTER_EXECUTABLE} sed -f
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/`echo ${lang}
|
||||
| sed -e 's/.*@//'`.sed 2>/dev/null >
|
||||
${PO_FILE_NAME}
|
||||
DEPENDS ${lang}.insert-header ${TEMPLATE_FILE_ABS}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
else()
|
||||
|
||||
add_custom_target(${PO_TARGET}
|
||||
COMMAND ${GettextTranslate_MSGMERGE_EXECUTABLE} --lang=${lang}
|
||||
${PO_FILE_NAME} ${TEMPLATE_FILE_ABS}
|
||||
-o ${PO_FILE_NAME}.new
|
||||
COMMAND mv ${PO_FILE_NAME}.new ${PO_FILE_NAME}
|
||||
DEPENDS ${TEMPLATE_FILE_ABS}
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
add_custom_target(${GMO_TARGET}
|
||||
COMMAND ${GettextTranslate_MSGFMT_EXECUTABLE} -c --statistics --verbose
|
||||
-o ${GMO_FILE_NAME} ${PO_FILE_NAME}
|
||||
DEPENDS ${PO_TARGET}
|
||||
)
|
||||
|
||||
add_dependencies(${PO_TARGET} ${MAKEVAR_DOMAIN}.pot-update)
|
||||
|
||||
install(FILES ${GMO_FILE_NAME} DESTINATION
|
||||
${LOCALEDIR}/${lang}/LC_MESSAGES
|
||||
RENAME ${MAKEVAR_DOMAIN}.mo
|
||||
)
|
||||
|
||||
endforeach()
|
||||
|
||||
add_dependencies(update-po ${po_files})
|
||||
add_dependencies(update-gmo ${gmo_files})
|
||||
|
||||
#string(REGEX MATCH "^[^=]+=(.*)$" parsed_variables ${makevars})
|
||||
|
||||
endmacro()
|
23
.cmake/Modules/uninstall.cmake
Normal file
23
.cmake/Modules/uninstall.cmake
Normal file
|
@ -0,0 +1,23 @@
|
|||
set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt")
|
||||
|
||||
if(NOT EXISTS ${MANIFEST})
|
||||
message(FATAL_ERROR "Cannot find install manifest: '${MANIFEST}'")
|
||||
endif()
|
||||
|
||||
file(STRINGS ${MANIFEST} files)
|
||||
foreach(file ${files})
|
||||
if(EXISTS ${file})
|
||||
message(STATUS "Removing file: '${file}'")
|
||||
|
||||
exec_program(
|
||||
${CMAKE_COMMAND} ARGS "-E remove ${file}"
|
||||
OUTPUT_VARIABLE stdout
|
||||
RETURN_VALUE result
|
||||
)
|
||||
if(NOT "${result}" STREQUAL 0)
|
||||
message(FATAL_ERROR "Failed to remove file: '${file}'.")
|
||||
endif()
|
||||
else()
|
||||
MESSAGE(STATUS "File '${file}' does not exist.")
|
||||
endif()
|
||||
endforeach(file)
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -14,11 +14,12 @@
|
|||
!HEADER
|
||||
!ChangeLog
|
||||
|
||||
!Makefile.am
|
||||
!configure.ac
|
||||
!autogen.sh
|
||||
!CMakeLists.txt
|
||||
!.cmake/*
|
||||
|
||||
!src/config.h.in
|
||||
src/config.h
|
||||
|
||||
build
|
||||
*~
|
||||
*.swp
|
||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
|||
[submodule "dependencies/csptr"]
|
||||
path = dependencies/csptr
|
||||
url = https://github.com/Snaipe/c-smart-pointers.git
|
27
.travis.yml
27
.travis.yml
|
@ -1,15 +1,24 @@
|
|||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
- gcc
|
||||
sudo: false
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- check
|
||||
- gettext
|
||||
- cmake
|
||||
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 gettext autopoint
|
||||
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.9 90
|
||||
- sudo pip install cpp-coveralls
|
||||
- export LOCAL_INSTALL="$HOME"
|
||||
- ".ci/install-libcsptr.sh"
|
||||
- export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$HOME/lib
|
||||
- export CFLAGS="-g -O0"
|
||||
script:
|
||||
- ./autogen.sh && ./configure --enable-gcov CFLAGS="-g -O0" && make && make -C samples check
|
||||
- mkdir -p build && cd $_ && cmake -DCOVERALLS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=$HOME .. && make && make test
|
||||
after_success:
|
||||
- coveralls --gcov gcov-4.9 --exclude samples --exclude dependencies --gcov-options '\-lp' -b .
|
||||
- make coveralls
|
||||
after_failure:
|
||||
- cat $(find samples -iname '*.log') /dev/null
|
||||
- cat Testing/Temporary/LastTest.log
|
||||
env:
|
||||
global:
|
||||
secure: bzZcWjdqoTgceC40kEBucx7NuWYJPk+rxgF3UJJDXi+ijQAFYPv70p5eVsGR6rfc+XgqXCxcUFQtuL4ZVt7QEfVk1ZOJITNeHbKIeKaEYS4nX8mFf+CBeEm9bJGZ04KiQJdJu5mzzAHvXbW7roGXDGWe1Bjnk5wwA+dNUCa7H04=
|
||||
|
|
132
CMakeLists.txt
Normal file
132
CMakeLists.txt
Normal file
|
@ -0,0 +1,132 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
project(Criterion C)
|
||||
|
||||
# Project setup & environment variables
|
||||
|
||||
enable_testing()
|
||||
add_subdirectory(samples)
|
||||
|
||||
set(PROJECT_VERSION "1.3.0")
|
||||
set(LOCALEDIR ${CMAKE_INSTALL_PREFIX}/share/locale)
|
||||
set(GettextTranslate_ALL)
|
||||
set(GettextTranslate_GMO_BINARY)
|
||||
set(MODULE_DIR "${CMAKE_SOURCE_DIR}/.cmake/Modules")
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${MODULE_DIR})
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Werror -g -std=gnu99")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-no-undefined")
|
||||
|
||||
# Setup coveralls
|
||||
|
||||
option(COVERALLS "Turn on coveralls support" OFF)
|
||||
option(COVERALLS_UPLOAD "Upload the generated coveralls json" ON)
|
||||
|
||||
if (COVERALLS)
|
||||
include(Coveralls)
|
||||
coveralls_turn_on_coverage()
|
||||
endif()
|
||||
|
||||
# Find dependencies
|
||||
|
||||
find_package(Gettext)
|
||||
find_package(Libintl)
|
||||
if (GETTEXT_FOUND AND LIBINTL_LIB_FOUND)
|
||||
include(GettextTranslate)
|
||||
add_subdirectory(po)
|
||||
set(ENABLE_NLS 1)
|
||||
endif ()
|
||||
|
||||
include(CheckLibraryExists)
|
||||
CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" HAVE_CLOCK_GETTIME)
|
||||
|
||||
find_package(PCRE)
|
||||
find_package(Libcsptr REQUIRED)
|
||||
|
||||
# List sources and headers
|
||||
|
||||
set(SOURCE_FILES
|
||||
src/abort.c
|
||||
src/abort.h
|
||||
src/event.c
|
||||
src/event.h
|
||||
src/report.c
|
||||
src/report.h
|
||||
src/runner.c
|
||||
src/runner.h
|
||||
src/process.c
|
||||
src/process.h
|
||||
src/stats.c
|
||||
src/stats.h
|
||||
src/log/logging.c
|
||||
src/log/tap.c
|
||||
src/log/normal.c
|
||||
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
|
||||
)
|
||||
|
||||
if (PCRE_FOUND)
|
||||
set (SOURCE_FILES ${SOURCE_FILES}
|
||||
src/extmatch.c
|
||||
src/extmatch.h
|
||||
)
|
||||
set(HAVE_PCRE 1)
|
||||
endif ()
|
||||
|
||||
set(INTERFACE_FILES
|
||||
include/criterion/assert.h
|
||||
include/criterion/abort.h
|
||||
include/criterion/common.h
|
||||
include/criterion/criterion.h
|
||||
include/criterion/event.h
|
||||
include/criterion/hooks.h
|
||||
include/criterion/logging.h
|
||||
include/criterion/types.h
|
||||
include/criterion/options.h
|
||||
include/criterion/ordered-set.h
|
||||
include/criterion/stats.h
|
||||
)
|
||||
|
||||
# Generate the configure file
|
||||
|
||||
configure_file(
|
||||
"${CMAKE_SOURCE_DIR}/src/config.h.in"
|
||||
"${CMAKE_SOURCE_DIR}/src/config.h"
|
||||
)
|
||||
|
||||
include_directories(include src ${CSPTR_INCLUDE_DIRS})
|
||||
add_library(criterion SHARED ${SOURCE_FILES} ${INTERFACE_FILES})
|
||||
target_link_libraries(criterion ${CSPTR_LIBRARIES})
|
||||
|
||||
if (HAVE_CLOCK_GETTIME)
|
||||
target_link_libraries(criterion rt)
|
||||
endif()
|
||||
|
||||
if (PCRE_FOUND)
|
||||
target_link_libraries(criterion ${PCRE_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if (LIBINTL_LIB_FOUND)
|
||||
target_link_libraries(criterion ${LIBINTL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if (COVERALLS)
|
||||
coveralls_setup("${SOURCE_FILES}" ${COVERALLS_UPLOAD})
|
||||
endif()
|
||||
|
||||
install(FILES ${INTERFACE_FILES} DESTINATION include/criterion)
|
||||
install(TARGETS criterion
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
|
||||
add_custom_target(uninstall
|
||||
"${CMAKE_COMMAND}" -P "${CMAKE_MODULE_PATH}/uninstall.cmake"
|
||||
)
|
|
@ -1,3 +1,12 @@
|
|||
2015-08-05 Franklin "Snaipe" Mathieu <franklinmathieu@gmail.com>
|
||||
|
||||
* criterion: version 1.3.0
|
||||
* Turned the library into a shared library.
|
||||
* Added extended globbing for --pattern (requires PCRE)
|
||||
* Switched to a CMake build system
|
||||
* Fixed windows builds & output
|
||||
* Added basic windows SEH-to-signal translator
|
||||
|
||||
2015-04-26 Franklin "Snaipe" Mathieu <franklinmathieu@gmail.com>
|
||||
|
||||
* criterion: version 1.2.1
|
||||
|
|
76
Makefile.am
76
Makefile.am
|
@ -1,76 +0,0 @@
|
|||
ACLOCAL_AMFLAGS = -I m4
|
||||
AM_CPPFLAGS = -DLOCALEDIR='"$(localedir)"'
|
||||
SUBDIRS = po dependencies/csptr samples
|
||||
|
||||
lib_LTLIBRARIES = libcriterion.la
|
||||
|
||||
WARNINGS = -Wall -Wextra \
|
||||
-Wno-unused-result
|
||||
|
||||
libcriterion_la_CFLAGS = \
|
||||
$(WARNINGS) \
|
||||
-std=gnu11 \
|
||||
-I$(top_srcdir)/src/ \
|
||||
-I$(top_srcdir)/include/ \
|
||||
-I$(top_srcdir)/dependencies/csptr/include/ \
|
||||
$(COVERAGE_CFLAGS)
|
||||
|
||||
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 = 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 \
|
||||
include/criterion/hooks.h \
|
||||
include/criterion/logging.h \
|
||||
include/criterion/types.h \
|
||||
include/criterion/options.h \
|
||||
include/criterion/ordered-set.h \
|
||||
include/criterion/stats.h
|
||||
|
||||
libcriterion_la_SOURCES = \
|
||||
src/abort.c \
|
||||
src/abort.h \
|
||||
src/event.c \
|
||||
src/event.h \
|
||||
src/report.c \
|
||||
src/report.h \
|
||||
src/runner.c \
|
||||
src/runner.h \
|
||||
src/process.c \
|
||||
src/process.h \
|
||||
src/stats.c \
|
||||
src/stats.h \
|
||||
src/log/logging.c \
|
||||
src/log/tap.c \
|
||||
src/log/normal.c \
|
||||
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)
|
||||
|
||||
package: all
|
||||
rm -Rf $(TARGET)
|
||||
mkdir -p $(TARGET)
|
||||
cp -Rf .libs $(TARGET)/lib/
|
||||
rm -f $(TARGET)/lib/libcriterion.la
|
||||
cp -f libcriterion.la $(TARGET)/lib
|
||||
cp -Rf include $(TARGET)
|
||||
tar -cvjf $(TARGET).tar.bz2 $(TARGET)
|
||||
|
||||
clean-local:
|
||||
rm -Rf $(TARGET) $(TARGET).tar.bz2
|
19
README.md
19
README.md
|
@ -1,5 +1,5 @@
|
|||
|
||||
Criterion
|
||||
<img src="doc/criterion-title.png" height="96" alt="Criterion Logo" />
|
||||
=========
|
||||
|
||||
[](https://travis-ci.org/Snaipe/Criterion)
|
||||
|
@ -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 MinGW or Cygwin).
|
||||
* [x] Runs on Linux, FreeBSD, Mac OS X, and Windows (Compiling with MinGW GCC).
|
||||
|
||||
## Downloads
|
||||
|
||||
* [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)
|
||||
* [Linux (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v1.3.0/criterion-1.3.0-linux-x86_64.tar.bz2)
|
||||
* [OS X (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v1.3.0/criterion-1.3.0-osx-x86_64.tar.bz2)
|
||||
* [FreeBSD (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v1.3.0/criterion-1.3.0-freebsd-x86_64.tar.bz2)
|
||||
* [Windows (x86_64)](https://github.com/Snaipe/Criterion/releases/download/v1.3.0/criterion-1.3.0-windows-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)
|
||||
|
||||
|
@ -99,8 +99,11 @@ 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 MinGW and Cygwin ports of GCC).
|
||||
most \*nix systems; Mac OS X Yosemite 10.10, FreeBSD 10.0, Windows 7 and 2K.
|
||||
|
||||
## Credits
|
||||
|
||||
Logo done by [Greehm](http://www.cargocollective.com/pbouigue)
|
||||
|
||||
[online-docs]: http://criterion.readthedocs.org/
|
||||
[pdf-docs]: http://readthedocs.org/projects/criterion/downloads/pdf/latest/
|
||||
|
|
54
appveyor.yml
54
appveyor.yml
|
@ -1,46 +1,15 @@
|
|||
version: 1.2.2_b{build}-{branch}
|
||||
version: 1.3.0_b{build}-{branch}
|
||||
|
||||
os: Windows Server 2012
|
||||
|
||||
init:
|
||||
- ps: (New-Object System.Net.WebClient).DownloadFile("https://cygwin.com/setup-x86.exe", "c:\setup-x86.exe")
|
||||
- git config --global core.autocrlf input
|
||||
- c:\setup-x86.exe -qnNdO -R %CYG_ROOT% -s %CYG_MIRROR% -l %CYG_CACHE% \
|
||||
-P autoconf \
|
||||
-P automake \
|
||||
-P gcc-core \
|
||||
-P mingw-runtime \
|
||||
-P mingw-binutils \
|
||||
-P mingw-gcc-core \
|
||||
-P mingw-pthreads \
|
||||
-P mingw-w32api \
|
||||
-P mingw64-i686-gcc-core \
|
||||
-P libtool \
|
||||
-P make \
|
||||
-P python3 \
|
||||
-P gettext-devel \
|
||||
-P gettext \
|
||||
-P expat \
|
||||
-P intltool \
|
||||
-P libiconv \
|
||||
-P pkg-config \
|
||||
-P check \
|
||||
-P git \
|
||||
-P wget \
|
||||
-P curl
|
||||
- 'SET PATH=%PATH%;C:\MinGW\msys\1.0\bin;C:\MinGW\bin;%APPVEYOR_BUILD_FOLDER%\bin;%APPVEYOR_BUILD_FOLDER%\build'
|
||||
|
||||
environment:
|
||||
global:
|
||||
CYG_ROOT: C:\cygwin
|
||||
CYG_MIRROR: http://cygwin.mirror.constant.com
|
||||
CYG_CACHE: C:\cygwin\var\cache\setup
|
||||
CYG_BASH: C:\cygwin\bin\bash
|
||||
COVERALLS_TOKEN:
|
||||
COVERALLS_REPO_TOKEN:
|
||||
secure: 5nuCg+faxFPeppoNNcSwVobswAVFUf8ut83vw8CX/4W2y0kZkGmwEfCUxSQWiQDU
|
||||
|
||||
cache:
|
||||
- '%CYG_CACHE%'
|
||||
|
||||
clone_depth: 5
|
||||
|
||||
matrix:
|
||||
|
@ -53,22 +22,19 @@ 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 CC=i686-w64-mingw32-gcc CFLAGS=\"-g -O0\" --enable-gcov"'
|
||||
- 'set LOCAL_INSTALL=%APPVEYOR_BUILD_FOLDER%'
|
||||
- 'bash -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0</dev/null; .ci/install-libcsptr.sh -G \"MSYS Makefiles\"; true"'
|
||||
- 'cd dependencies/libcsptr/build && make && make install && cd ../../../'
|
||||
- 'mkdir build && cd build && cmake -DCMAKE_PREFIX_PATH="%LOCAL_INSTALL%" -DCOVERALLS=ON -DCMAKE_BUILD_TYPE=Debug -G "MSYS Makefiles" ..'
|
||||
|
||||
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'"
|
||||
- 'make'
|
||||
|
||||
test_script:
|
||||
- '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER/samples; exec 0</dev/null; make check"'
|
||||
- 'make test || bash -lc "cat $APPVEYOR_BUILD_FOLDER/build/Testing/Temporary/LastTest.log"'
|
||||
|
||||
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'
|
||||
- 'make coveralls'
|
||||
|
||||
notifications:
|
||||
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/sh
|
||||
git submodule update --init --recursive
|
||||
autopoint
|
||||
autoreconf -i
|
48
configure.ac
48
configure.ac
|
@ -1,48 +0,0 @@
|
|||
AC_PREREQ([2.60])
|
||||
|
||||
AC_INIT([criterion], [1.2.2], [], [criterion], [franklinmathieu@gmail.com])
|
||||
AC_CONFIG_SRCDIR([src/runner.c])
|
||||
|
||||
LT_PREREQ([2.2.4])
|
||||
AC_CANONICAL_SYSTEM
|
||||
|
||||
AM_INIT_AUTOMAKE([dist-bzip2 no-dist-gzip foreign subdir-objects parallel-tests color-tests])
|
||||
LT_INIT([disable-shared])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
AC_PROG_CC
|
||||
AM_PROG_CC_C_O
|
||||
|
||||
AC_PROG_LIBTOOL
|
||||
AC_PROG_INSTALL
|
||||
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])],
|
||||
[COVERAGE_CFLAGS="-O0 -fprofile-arcs -ftest-coverage"
|
||||
COVERAGE_LDFLAGS="-lgcov"
|
||||
AC_SUBST([COVERAGE_CFLAGS])
|
||||
AC_SUBST([COVERAGE_LDFLAGS])
|
||||
],
|
||||
[])
|
||||
|
||||
AC_CONFIG_HEADERS([src/config.h])
|
||||
AC_CONFIG_FILES([Makefile samples/Makefile po/Makefile.in])
|
||||
|
||||
AC_CONFIG_SUBDIRS([dependencies/csptr])
|
||||
|
||||
AC_OUTPUT
|
1
dependencies/csptr
vendored
1
dependencies/csptr
vendored
|
@ -1 +0,0 @@
|
|||
Subproject commit 4f3e63aca586939ed734f4e76c4f7f7f8c07d247
|
|
@ -39,7 +39,7 @@ copyright = u'2015, Franklin "Snaipe" Mathieu'
|
|||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '1.2.2'
|
||||
version = '1.3.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = version
|
||||
|
||||
|
|
BIN
doc/criterion-title.png
Normal file
BIN
doc/criterion-title.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
|
@ -27,6 +27,5 @@ Features
|
|||
reported and tested.
|
||||
* Progress and statistics can be followed in real time with report hooks.
|
||||
* TAP output format can be enabled with an option.
|
||||
* Runs on Linux, FreeBSD, Mac OS X, and Windows (compiles only with Cygwin
|
||||
for the moment).
|
||||
* Runs on Linux, FreeBSD, Mac OS X, and Windows (Compiling with MinGW GCC).
|
||||
* xUnit framework structure
|
||||
|
|
|
@ -6,7 +6,7 @@ Prerequisites
|
|||
|
||||
Currently, this library only works under \*nix systems.
|
||||
|
||||
To compile the static library and its dependencies, GCC 4.9+ is needed.
|
||||
To compile the static library and its dependencies, GCC 4.6+ is needed.
|
||||
|
||||
To use the static library, any GNU-C compatible compiler will suffice
|
||||
(GCC, Clang/LLVM, ICC, MinGW-GCC, ...).
|
||||
|
@ -16,9 +16,10 @@ Installation
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
$ git clone https://github.com/Snaipe/Criterion.git
|
||||
$ cd Criterion
|
||||
$ ./autogen.sh && ./configure && make && sudo make install
|
||||
$ git clone https://github.com/Snaipe/Criterion.git && cd Criterion
|
||||
$ LOCAL_INSTALL=/usr .ci/install-libcsptr.sh
|
||||
$ mkdir build && cd $_ && cmake -DCMAKE_INSTALL_PATH=/usr ..
|
||||
$ make && sudo make install
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
|
|
@ -147,7 +147,34 @@ is received:
|
|||
*ptr = 42;
|
||||
}
|
||||
|
||||
This feature will of course not work on Windows.
|
||||
This feature will also work (to some extent) on Windows for the
|
||||
following signals on some exceptions:
|
||||
|
||||
======== =====================================================================
|
||||
Signal Triggered by
|
||||
======== =====================================================================
|
||||
SIGSEGV STATUS_ACCESS_VIOLATION, STATUS_DATATYPE_MISALIGNMENT,
|
||||
STATUS_ARRAY_BOUNDS_EXCEEDED, STATUS_GUARD_PAGE_VIOLATION,
|
||||
STATUS_IN_PAGE_ERROR, STATUS_NO_MEMORY, STATUS_INVALID_DISPOSITION,
|
||||
STATUS_STACK_OVERFLOW
|
||||
-------- ---------------------------------------------------------------------
|
||||
SIGILL STATUS_ILLEGAL_INSTRUCTION, STATUS_PRIVILEGED_INSTRUCTION,
|
||||
STATUS_NONCONTINUABLE_EXCEPTION
|
||||
-------- ---------------------------------------------------------------------
|
||||
SIGINT STATUS_CONTROL_C_EXIT
|
||||
-------- ---------------------------------------------------------------------
|
||||
SIGFPE STATUS_FLOAT_DENORMAL_OPERAND, STATUS_FLOAT_DIVIDE_BY_ZERO,
|
||||
STATUS_FLOAT_INEXACT_RESULT, STATUS_FLOAT_INVALID_OPERATION,
|
||||
STATUS_FLOAT_OVERFLOW, STATUS_FLOAT_STACK_CHECK,
|
||||
STATUS_FLOAT_UNDERFLOW, STATUS_INTEGER_DIVIDE_BY_ZERO,
|
||||
STATUS_INTEGER_OVERFLOW
|
||||
-------- ---------------------------------------------------------------------
|
||||
SIGALRM STATUS_TIMEOUT
|
||||
======== =====================================================================
|
||||
|
||||
See the `windows exception reference`_ for more details on each exception.
|
||||
|
||||
.. _windows exception reference: https://msdn.microsoft.com/en-us/library/windows/desktop/ms679356(v=vs.85).aspx
|
||||
|
||||
Configuration reference
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
.line_ = __LINE__, \
|
||||
__VA_ARGS__ \
|
||||
}; \
|
||||
SECTION_("criterion_tests") \
|
||||
SECTION_("cr_tst") \
|
||||
const struct criterion_test IDENTIFIER_(Category, Name, meta) = { \
|
||||
.name = #Name, \
|
||||
.category = #Category, \
|
||||
|
@ -38,7 +38,7 @@
|
|||
.line_ = 0, \
|
||||
__VA_ARGS__ \
|
||||
}; \
|
||||
SECTION_("crit_suites") \
|
||||
SECTION_("cr_sts") \
|
||||
const struct criterion_suite SUITE_IDENTIFIER_(Name, meta) = { \
|
||||
.name = #Name, \
|
||||
.data = &SUITE_IDENTIFIER_(Name, extra), \
|
||||
|
|
|
@ -48,9 +48,27 @@ typedef void (*f_report_hook)();
|
|||
# define HOOK_PROTOTYPE_ \
|
||||
void HOOK_IDENTIFIER_(impl)
|
||||
|
||||
// Section abbreviations
|
||||
# define HOOK_SECTION_PRE_ALL cr_pra
|
||||
# define HOOK_SECTION_PRE_SUITE cr_prs
|
||||
# define HOOK_SECTION_PRE_INIT cr_pri
|
||||
# define HOOK_SECTION_PRE_TEST cr_prt
|
||||
# define HOOK_SECTION_ASSERT cr_ast
|
||||
# define HOOK_SECTION_TEST_CRASH cr_tsc
|
||||
# define HOOK_SECTION_POST_TEST cr_pot
|
||||
# define HOOK_SECTION_POST_FINI cr_pof
|
||||
# define HOOK_SECTION_POST_SUITE cr_pos
|
||||
# define HOOK_SECTION_POST_ALL cr_poa
|
||||
|
||||
# define HOOK_SECTION(Kind) HOOK_SECTION_ ## Kind
|
||||
|
||||
# define HOOK_SECTION_STRINGIFY__(Sec) #Sec
|
||||
# define HOOK_SECTION_STRINGIFY_(Sec) HOOK_SECTION_STRINGIFY__(Sec)
|
||||
# define HOOK_SECTION_STRINGIFY(Kind) HOOK_SECTION_STRINGIFY_(HOOK_SECTION(Kind))
|
||||
|
||||
# define ReportHook(Kind) \
|
||||
HOOK_PROTOTYPE_(); \
|
||||
SECTION_("crit_" #Kind) \
|
||||
SECTION_(HOOK_SECTION_STRINGIFY(Kind)) \
|
||||
const f_report_hook HOOK_IDENTIFIER_(func) = HOOK_IDENTIFIER_(impl); \
|
||||
HOOK_PROTOTYPE_
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
# include <stdbool.h>
|
||||
# include <stddef.h>
|
||||
# include "common.h"
|
||||
|
||||
struct criterion_test_extra_data {
|
||||
int sentinel_;
|
||||
|
|
0
m4/.keep
0
m4/.keep
1
po/CMakeLists.txt
Normal file
1
po/CMakeLists.txt
Normal file
|
@ -0,0 +1 @@
|
|||
GettextTranslate()
|
|
@ -1,7 +1,7 @@
|
|||
# Makefile variables for PO directory in any package using GNU gettext.
|
||||
|
||||
# Usually the message domain is the same as the package name.
|
||||
DOMAIN = $(PACKAGE)
|
||||
DOMAIN = Criterion
|
||||
|
||||
# These two variables depend on the location of this directory.
|
||||
subdir = po
|
||||
|
|
12
po/fr.po
12
po/fr.po
|
@ -8,7 +8,7 @@ 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"
|
||||
"POT-Creation-Date: 2015-07-28 22:17+0200\n"
|
||||
"PO-Revision-Date: 2015-04-03 17:58+0200\n"
|
||||
"Last-Translator: <franklinmathieu@gmail.com>\n"
|
||||
"Language-Team: French\n"
|
||||
|
@ -70,8 +70,10 @@ 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"
|
||||
#, fuzzy, c-format
|
||||
msgid ""
|
||||
"%1$sWarning! The test `%2$s::%3$s` crashed during its setup or teardown."
|
||||
"%4$s\n"
|
||||
msgstr ""
|
||||
"%1$sAttention! Ce test a planté pendant son initialisation ou sa "
|
||||
"finalisation.%2$s\n"
|
||||
"%1$sAttention! Le test `%2$s::%3$s` a planté pendant son initialisation ou "
|
||||
"sa finalisation.%4$s\n"
|
||||
|
|
44
samples/CMakeLists.txt
Normal file
44
samples/CMakeLists.txt
Normal file
|
@ -0,0 +1,44 @@
|
|||
project(criterion_samples)
|
||||
|
||||
set(CMAKE_C_FLAGS "-std=c99 -Wall -Wextra -pedantic")
|
||||
|
||||
include_directories(../include)
|
||||
|
||||
set(SAMPLES
|
||||
signal
|
||||
report
|
||||
suites
|
||||
fixtures
|
||||
asserts
|
||||
more-suites
|
||||
long-messages
|
||||
description
|
||||
other-crashes
|
||||
simple
|
||||
)
|
||||
|
||||
set(SCRIPTS
|
||||
tap_test
|
||||
early_exit
|
||||
verbose
|
||||
list
|
||||
pattern
|
||||
fail_fast
|
||||
help
|
||||
)
|
||||
|
||||
foreach(sample ${SAMPLES})
|
||||
add_executable(${sample} ${sample}.c)
|
||||
target_link_libraries(${sample} criterion)
|
||||
add_test(${sample} ${sample})
|
||||
set_property(TEST ${sample} PROPERTY
|
||||
ENVIRONMENT "CRITERION_ALWAYS_SUCCEED=1"
|
||||
)
|
||||
endforeach()
|
||||
|
||||
foreach(script ${SCRIPTS})
|
||||
add_test(${script} sh ${CMAKE_CURRENT_LIST_DIR}/tests/${script}.sh)
|
||||
set_property(TEST ${script} PROPERTY
|
||||
ENVIRONMENT "CRITERION_ALWAYS_SUCCEED=1"
|
||||
)
|
||||
endforeach()
|
|
@ -8,8 +8,9 @@ Test(simple, caught, .signal = SIGSEGV) {
|
|||
*i = 42;
|
||||
}
|
||||
|
||||
Test(simple, wrong_signal, .signal = SIGSEGV) {
|
||||
raise(SIGINT);
|
||||
Test(simple, wrong_signal, .signal = SIGINT) {
|
||||
int *i = NULL;
|
||||
*i = 42;
|
||||
}
|
||||
|
||||
Test(simple, uncaught) {
|
||||
|
|
|
@ -1,2 +1,10 @@
|
|||
#!/bin/sh
|
||||
#!/bin/sh -e
|
||||
./simple --pattern '*/passing'
|
||||
./simple --pattern '!(*/passing)'
|
||||
./simple --pattern '[pf]a@(ss|il)ing'
|
||||
./simple --pattern '@(+(nest)ed))'
|
||||
./simple --pattern '?(*(a|b))'
|
||||
! ./simple --pattern '?(malformed'
|
||||
./simple --pattern '[!azerty]assing'
|
||||
./simple --pattern '|pipe'
|
||||
./simple --pattern '\!(escaped'
|
||||
|
|
11
src/config.h.in
Normal file
11
src/config.h.in
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef CONFIG_H_IN_
|
||||
# define CONFIG_H_IN_
|
||||
|
||||
#cmakedefine ENABLE_NLS @ENABLE_NLS@
|
||||
#cmakedefine HAVE_PCRE @HAVE_PCRE@
|
||||
|
||||
# define LOCALEDIR "${LOCALEDIR}"
|
||||
# define PACKAGE "${PROJECT_NAME}"
|
||||
# define VERSION "${PROJECT_VERSION}"
|
||||
|
||||
#endif /* !CONFIG_H_IN_ */
|
|
@ -45,8 +45,10 @@ struct event *read_event(FILE *f) {
|
|||
case ASSERT: {
|
||||
const size_t assert_size = sizeof (struct criterion_assert_stats);
|
||||
unsigned char *buf = malloc(assert_size);
|
||||
if (fread(buf, assert_size, 1, f) == 0)
|
||||
if (fread(buf, assert_size, 1, f) == 0) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return unique_ptr(struct event,
|
||||
.value = { .kind = kind, .data = buf },
|
||||
|
@ -54,8 +56,10 @@ struct event *read_event(FILE *f) {
|
|||
}
|
||||
case POST_TEST: {
|
||||
double *elapsed_time = malloc(sizeof (double));
|
||||
if (fread(elapsed_time, sizeof (double), 1, f) == 0)
|
||||
if (fread(elapsed_time, sizeof (double), 1, f) == 0) {
|
||||
free(elapsed_time);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return unique_ptr(struct event,
|
||||
.value = { .kind = kind, .data = elapsed_time },
|
||||
|
|
261
src/extmatch.c
Normal file
261
src/extmatch.c
Normal file
|
@ -0,0 +1,261 @@
|
|||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <pcre.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include "criterion/common.h"
|
||||
|
||||
struct context {
|
||||
int depth;
|
||||
char *dst;
|
||||
const char *src;
|
||||
char old, cur;
|
||||
int eos;
|
||||
const char **errmsg;
|
||||
jmp_buf *jmp;
|
||||
};
|
||||
|
||||
void transform_impl(struct context *ctx);
|
||||
|
||||
static inline void transform_rec(struct context *ctx) {
|
||||
struct context new_ctx = {
|
||||
.depth = ctx->depth + 1,
|
||||
.dst = ctx->dst,
|
||||
.src = ctx->src,
|
||||
.old = ctx->old,
|
||||
.eos = ctx->eos,
|
||||
.errmsg = ctx->errmsg,
|
||||
.jmp = ctx->jmp,
|
||||
};
|
||||
transform_impl(&new_ctx);
|
||||
ctx->dst = new_ctx.dst;
|
||||
ctx->src = new_ctx.src;
|
||||
ctx->old = new_ctx.old;
|
||||
ctx->eos = new_ctx.eos;
|
||||
}
|
||||
|
||||
static inline char read_char(struct context *ctx) {
|
||||
char c = *ctx->src;
|
||||
ctx->old = ctx->cur;
|
||||
ctx->cur = c;
|
||||
if (c == '\0')
|
||||
ctx->eos = 1;
|
||||
++ctx->src;
|
||||
return c;
|
||||
}
|
||||
|
||||
static inline char peek_char(struct context *ctx) {
|
||||
return *ctx->src;
|
||||
}
|
||||
|
||||
static inline void copy_char(struct context *ctx, char src) {
|
||||
*ctx->dst = src;
|
||||
++ctx->dst;
|
||||
}
|
||||
|
||||
static inline void dup_char(struct context *ctx) {
|
||||
copy_char(ctx, read_char(ctx));
|
||||
}
|
||||
|
||||
static inline void copy_str(struct context *ctx, const char *src) {
|
||||
size_t len = strlen(src);
|
||||
strncpy(ctx->dst, src, len);
|
||||
ctx->dst += len;
|
||||
}
|
||||
|
||||
#define PREPREFIX 0
|
||||
#define POSTPREFIX 1
|
||||
#define PRESUFFIX 2
|
||||
#define POSTSUFFIX 3
|
||||
#define ELSESTR 4
|
||||
|
||||
typedef struct {
|
||||
int (*validator)(struct context *ctx);
|
||||
char *str;
|
||||
} handler_arg;
|
||||
|
||||
static int active() { return 1; }
|
||||
static int inactive() { return 0; }
|
||||
|
||||
static int is_eos(struct context *ctx) {
|
||||
return peek_char(ctx) == '\0';
|
||||
}
|
||||
|
||||
static inline void handle_special(struct context *ctx, handler_arg strs[5]) {
|
||||
if (peek_char(ctx) == '(') {
|
||||
if ((strs[0].validator ?: inactive)(ctx))
|
||||
copy_str(ctx, strs[0].str);
|
||||
dup_char(ctx);
|
||||
if ((strs[1].validator ?: inactive)(ctx))
|
||||
copy_str(ctx, strs[1].str);
|
||||
|
||||
transform_rec(ctx);
|
||||
|
||||
if ((strs[2].validator ?: inactive)(ctx))
|
||||
copy_str(ctx,strs[2].str);
|
||||
copy_char(ctx, ')');
|
||||
if ((strs[3].validator ?: inactive)(ctx))
|
||||
copy_str(ctx, strs[3].str);
|
||||
} else if ((strs[4].validator ?: inactive)(ctx)) {
|
||||
copy_str(ctx, strs[4].str);
|
||||
}
|
||||
}
|
||||
|
||||
# define Handler(Name, ...) \
|
||||
static void Name(struct context *ctx, UNUSED char c) { \
|
||||
handle_special(ctx, (handler_arg[5]) { \
|
||||
__VA_ARGS__ \
|
||||
}); \
|
||||
}
|
||||
|
||||
# define _ active
|
||||
Handler(handle_plus, [POSTSUFFIX] = {_, "+"}, [ELSESTR] = {_, "+" });
|
||||
Handler(handle_star, [POSTSUFFIX] = {_, "*"}, [ELSESTR] = {_, ".*"});
|
||||
Handler(handle_wild, [POSTSUFFIX] = {_, "?"}, [ELSESTR] = {_, "." });
|
||||
Handler(handle_excl,
|
||||
[POSTPREFIX] = {_, "?!"},
|
||||
[PRESUFFIX] = {is_eos, "$" },
|
||||
[POSTSUFFIX] = {_, ".*"},
|
||||
[ELSESTR] = {_, "!" }
|
||||
);
|
||||
Handler(handle_at, [ELSESTR] = {_, "@"});
|
||||
# undef _
|
||||
|
||||
static void handle_rbra(struct context *ctx, UNUSED char c) {
|
||||
copy_char(ctx, c);
|
||||
if (peek_char(ctx) == '!') {
|
||||
read_char(ctx);
|
||||
copy_char(ctx, '^');
|
||||
}
|
||||
}
|
||||
|
||||
static void escape_char(struct context *ctx, char c) {
|
||||
copy_char(ctx, '\\');
|
||||
copy_char(ctx, c);
|
||||
}
|
||||
|
||||
static void escape_pipe(struct context *ctx, UNUSED char c) {
|
||||
if (ctx->depth == 0)
|
||||
copy_char(ctx, '\\');
|
||||
copy_char(ctx, '|');
|
||||
}
|
||||
|
||||
typedef void (*f_handler)(struct context *, char);
|
||||
|
||||
void transform_impl(struct context *ctx) {
|
||||
static f_handler handlers[] = {
|
||||
['+'] = handle_plus,
|
||||
['*'] = handle_star,
|
||||
['?'] = handle_wild,
|
||||
['!'] = handle_excl,
|
||||
['['] = handle_rbra,
|
||||
['@'] = handle_at,
|
||||
|
||||
['.'] = escape_char,
|
||||
['('] = escape_char,
|
||||
[')'] = escape_char,
|
||||
['|'] = escape_pipe,
|
||||
|
||||
[255] = NULL,
|
||||
};
|
||||
for (char c = read_char(ctx); !ctx->eos; c = read_char(ctx)) {
|
||||
f_handler handler = handlers[(unsigned char) c];
|
||||
|
||||
if (ctx->old == '\\')
|
||||
handler = copy_char;
|
||||
|
||||
if (c == ')' && ctx->depth > 0)
|
||||
return;
|
||||
|
||||
(handler ?: copy_char)(ctx, c);
|
||||
|
||||
if (ctx->eos)
|
||||
return;
|
||||
}
|
||||
if (ctx->depth > 0) {
|
||||
*ctx->errmsg = "mismatching parenthesis";
|
||||
longjmp(*ctx->jmp, -1); // abort operation
|
||||
}
|
||||
}
|
||||
|
||||
static int transform(const char *pattern, char *result, const char **errmsg) {
|
||||
jmp_buf jmp;
|
||||
struct context ctx = {
|
||||
.src = pattern,
|
||||
.dst = result,
|
||||
.errmsg = errmsg,
|
||||
.jmp = &jmp,
|
||||
};
|
||||
if (!setjmp(*ctx.jmp)) {
|
||||
copy_char(&ctx, '^');
|
||||
transform_impl(&ctx);
|
||||
copy_char(&ctx, '$');
|
||||
copy_char(&ctx, '\0');
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* let T be the transformation function,
|
||||
* let diff be the function that yields the greatest character difference
|
||||
* before and after its parameter has been transformed by T.
|
||||
*
|
||||
* T('*') = '.*'; diff('*') = 1
|
||||
* T('!()') = '(?!).*' or '(?!$).*'; diff('!()') = 4
|
||||
* T('@') = '' or '@'; diff('@') = 0
|
||||
* T('|') = '|' or '\|'; diff('|') = 1
|
||||
* T('.') = '\.'; diff('.') = 1
|
||||
* T('(') = '\('; diff('(') = 1
|
||||
* T(')') = '\)'; diff(')') = 1
|
||||
* for every other 1 character string s, we have T(s) = s; hence diff(s) = 0
|
||||
*
|
||||
* let num be the function that yields the number of occurences of a string.
|
||||
* let spec be the set {(s, num(s)) | ∀s}
|
||||
* ∀s, lenght(T(s)) = length(s) + Σ((c_i, n_i) ∈ spec, n_i * diff(c_i))
|
||||
*
|
||||
* let S = {'*', '!()', '|', '.', '(', ')'}.
|
||||
* since ∀s ∉ S, diff(s) = 0, we can simplify the above equation as:
|
||||
*
|
||||
* ∀s, lenght(T(s)) = length(s) + num('*') + num('|') + num('.')
|
||||
* + num('(') + num(')') + 4 * num('!()').
|
||||
*
|
||||
* We must now find the maximal length L such as ∀s, L >= length(T(s))
|
||||
*
|
||||
* It is immediately apparent that the largest string will depend on the number
|
||||
* of occurrences of '!()'. Hence, let u be a string that is a repeating
|
||||
* sequence of '!()' padded by '.' to a multiple of 3,
|
||||
*
|
||||
* let N = floor(length(u) / 3),
|
||||
* let Q = length(u) mod 3,
|
||||
* hence num('!()') = N.
|
||||
*
|
||||
* ∀s | lenght(s) = length(u),
|
||||
* length(T(s)) <= length(T(u))
|
||||
* <= length(u) | the original length
|
||||
* + 4 * N | the expansion of all '!()'
|
||||
* + Q * diff('.') | the expansion of Q '.'
|
||||
* <= 3 * N + Q + 4 * N + Q
|
||||
* <= 7 * N + 4
|
||||
* <= 7 * floor(length(u) / 3) + 4
|
||||
* <= 7 * floor(length(s) / 3) + 4
|
||||
*
|
||||
*/
|
||||
static inline size_t max_length(size_t len) {
|
||||
return 7 * len / 3 + 4;
|
||||
}
|
||||
|
||||
int extmatch(const char *pattern, const char *string, const char **errmsg) {
|
||||
char regex[max_length(strlen(pattern))];
|
||||
if (transform(pattern, regex, errmsg) != -1) {
|
||||
int erroffset;
|
||||
pcre *preg = pcre_compile(regex, 0, errmsg, &erroffset, NULL);
|
||||
if (preg) {
|
||||
int res = pcre_exec(preg, NULL, string, strlen(string), 0, 0, NULL, 0);
|
||||
pcre_free(preg);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return -10;
|
||||
}
|
6
src/extmatch.h
Normal file
6
src/extmatch.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#ifndef EXTMATCH_H_
|
||||
# define EXTMATCH_H_
|
||||
|
||||
int extmatch(const char *pattern, const char *string, const char **errmsg);
|
||||
|
||||
#endif /* !EXTMATCH_H_ */
|
|
@ -29,7 +29,11 @@
|
|||
#include "criterion/options.h"
|
||||
#include "i18n.h"
|
||||
|
||||
#define LOG_FORMAT "[%1$s%2$s%3$s] %4$s"
|
||||
#ifdef ENABLE_NLS
|
||||
# define LOG_FORMAT "[%1$s%2$s%3$s] %4$s"
|
||||
#else
|
||||
# define LOG_FORMAT "[%s%s%s] %s"
|
||||
#endif
|
||||
|
||||
const struct criterion_prefix_data g_criterion_logging_prefixes[] = {
|
||||
[CRITERION_LOGGING_PREFIX_DASHES] = { "----", CRIT_FG_BLUE },
|
||||
|
@ -38,7 +42,7 @@ const struct criterion_prefix_data g_criterion_logging_prefixes[] = {
|
|||
[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 }
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
void criterion_plog(enum criterion_logging_level level, const struct criterion_prefix_data *prefix, const char *msg, ...) {
|
||||
|
|
|
@ -33,24 +33,72 @@
|
|||
#include "timer.h"
|
||||
#include "config.h"
|
||||
#include "i18n.h"
|
||||
#include "posix-compat.h"
|
||||
|
||||
#define USED __attribute__ ((used))
|
||||
|
||||
#ifdef VANILLA_WIN32
|
||||
// fallback to strtok on windows since strtok_s is not available everywhere
|
||||
# define strtok_r(str, delim, saveptr) strtok(str, delim)
|
||||
#endif
|
||||
|
||||
typedef const char *const msg_t;
|
||||
|
||||
static msg_t msg_pre_all = "Criterion v%s\n";
|
||||
static msg_t msg_desc = " %s\n";
|
||||
|
||||
#ifdef ENABLE_NLS
|
||||
static msg_t msg_pre_init = "%1$s::%2$s\n";
|
||||
static msg_t msg_post_test_timed = "%1$s::%2$s: (%3$3.2fs)\n";
|
||||
static msg_t msg_post_test = "%1$s::%2$s\n";
|
||||
static msg_t msg_post_suite_test = "%1$s::%2$s: Test is disabled\n";
|
||||
static msg_t msg_post_suite_suite = "%1$s::%2$s: Suite is disabled\n";
|
||||
static msg_t msg_assert_fail = "%1$s%2$s%3$s:%4$s%5$d%6$s: Assertion failed: %7$s\n";
|
||||
static msg_t msg_test_crash_line = "%1$s%2$s%3$s:%4$s%5$u%6$s: Unexpected signal caught below this line!\n";
|
||||
static msg_t msg_test_crash = "%1$s::%2$s: CRASH!\n";
|
||||
static msg_t msg_test_other_crash = "%1$sWarning! The test `%2$s::%3$s` crashed during its setup or teardown.%4$s\n";
|
||||
static msg_t msg_pre_suite = "Running %1$s%2$lu%3$s test from %4$s%5$s%6$s:\n";
|
||||
static msg_t msg_pre_suite_pl = "Running %1$s%2$lu%3$s tests from %4$s%5$s%6$s:\n";
|
||||
static msg_t msg_post_all = "%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";
|
||||
#else
|
||||
static msg_t msg_pre_init = "%s::%s\n";
|
||||
static msg_t msg_post_test_timed = "%s::%s: (%3.2fs)\n";
|
||||
static msg_t msg_post_test = "%s::%s\n";
|
||||
static msg_t msg_post_suite_test = "%s::%s: Test is disabled\n";
|
||||
static msg_t msg_post_suite_suite = "%s::%s: Suite is disabled\n";
|
||||
static msg_t msg_assert_fail = "%s%s%s:%s%d%s: Assertion failed: %s\n";
|
||||
static msg_t msg_test_crash_line = "%s%s%s:%s%u%s: Unexpected signal caught below this line!\n";
|
||||
static msg_t msg_test_crash = "%s::%s: CRASH!\n";
|
||||
static msg_t msg_test_other_crash = "%sWarning! The test `%s::%s` crashed during its setup or teardown.%s\n";
|
||||
static msg_t msg_pre_suite = "Running %s%lu%s test from %s%s%s:\n";
|
||||
static msg_t msg_pre_suite_pl = "Running %s%lu%s tests from %s%s%s:\n";
|
||||
static msg_t msg_post_all = "%sSynthesis: Tested: %s%lu%s "
|
||||
"| Passing: %s%lu%s "
|
||||
"| Failing: %s%lu%s "
|
||||
"| Crashing: %s%lu%s "
|
||||
"%s\n";
|
||||
#endif
|
||||
|
||||
void normal_log_pre_all(UNUSED struct criterion_test_set *set) {
|
||||
criterion_pinfo(CRITERION_PREFIX_DASHES, _("Criterion v%s\n"), VERSION);
|
||||
criterion_pinfo(CRITERION_PREFIX_DASHES, _(msg_pre_all), VERSION);
|
||||
}
|
||||
|
||||
void normal_log_pre_init(struct criterion_test *test) {
|
||||
criterion_pinfo(CRITERION_PREFIX_RUN, _("%1$s::%2$s\n"),
|
||||
criterion_pinfo(CRITERION_PREFIX_RUN, _(msg_pre_init),
|
||||
test->category,
|
||||
test->name);
|
||||
|
||||
if (test->data->description)
|
||||
criterion_pinfo(CRITERION_PREFIX_RUN, _(" %s\n"),
|
||||
criterion_pinfo(CRITERION_PREFIX_RUN, _(msg_desc),
|
||||
test->data->description);
|
||||
}
|
||||
|
||||
void normal_log_post_test(struct criterion_test_stats *stats) {
|
||||
const char *format = can_measure_time() ? "%1$s::%2$s: (%3$3.2fs)\n"
|
||||
: "%1$s::%2$s\n";
|
||||
const char *format = can_measure_time() ? msg_post_test_timed : msg_post_test;
|
||||
|
||||
const enum criterion_logging_level level
|
||||
= stats->failed ? CRITERION_IMPORTANT : CRITERION_INFO;
|
||||
|
@ -73,15 +121,15 @@ 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)) {
|
||||
const char *format = ts->test->data->disabled
|
||||
? _("%1$s::%2$s: Test is disabled\n")
|
||||
: _("%1$s::%2$s: Suite is disabled\n");
|
||||
? _(msg_post_suite_test)
|
||||
: _(msg_post_suite_suite);
|
||||
|
||||
criterion_pinfo(CRITERION_PREFIX_SKIP, format,
|
||||
ts->test->category,
|
||||
ts->test->name);
|
||||
|
||||
if (ts->test->data->description)
|
||||
criterion_pinfo(CRITERION_PREFIX_DASHES, " %s\n",
|
||||
criterion_pinfo(CRITERION_PREFIX_DASHES, msg_desc,
|
||||
ts->test->data->description);
|
||||
}
|
||||
}
|
||||
|
@ -91,11 +139,7 @@ void normal_log_post_all(struct criterion_global_stats *stats) {
|
|||
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"),
|
||||
_(msg_post_all),
|
||||
FG_BOLD,
|
||||
FG_BLUE, (unsigned long) tested, FG_BOLD,
|
||||
FG_GREEN, (unsigned long) stats->tests_passed, FG_BOLD,
|
||||
|
@ -108,43 +152,49 @@ void normal_log_assert(struct criterion_assert_stats *stats) {
|
|||
if (!stats->passed) {
|
||||
char *dup = strdup(*stats->message ? stats->message
|
||||
: stats->condition);
|
||||
|
||||
#ifdef VANILLA_WIN32
|
||||
char *line = strtok(dup, "\n");
|
||||
#else
|
||||
char *saveptr = NULL;
|
||||
char *line = strtok_r(dup, "\n", &saveptr);
|
||||
#endif
|
||||
|
||||
criterion_pimportant(CRITERION_PREFIX_DASHES,
|
||||
_("%1$s%2$s%3$s:%4$s%5$d%6$s: Assertion failed: %7$s\n"),
|
||||
_(msg_assert_fail),
|
||||
FG_BOLD, stats->file, RESET,
|
||||
FG_RED, stats->line, RESET,
|
||||
line);
|
||||
|
||||
#ifdef VANILLA_WIN32
|
||||
while ((line = strtok(NULL, "\n")))
|
||||
#else
|
||||
while ((line = strtok_r(NULL, "\n", &saveptr)))
|
||||
criterion_pimportant(CRITERION_PREFIX_DASHES, _(" %s\n"), line);
|
||||
#endif
|
||||
criterion_pimportant(CRITERION_PREFIX_DASHES, _(msg_desc), line);
|
||||
free(dup);
|
||||
}
|
||||
}
|
||||
|
||||
void normal_log_test_crash(struct criterion_test_stats *stats) {
|
||||
criterion_pimportant(CRITERION_PREFIX_DASHES,
|
||||
_("%1$s%2$s%3$s:%4$s%5$u%6$s: "
|
||||
"Unexpected signal caught below this line!\n"),
|
||||
_(msg_test_crash_line),
|
||||
FG_BOLD, stats->file, RESET,
|
||||
FG_RED, stats->progress, RESET);
|
||||
criterion_pimportant(CRITERION_PREFIX_FAIL, _("%1$s::%2$s: CRASH!\n"),
|
||||
criterion_pimportant(CRITERION_PREFIX_FAIL, _(msg_test_crash),
|
||||
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);
|
||||
_(msg_test_other_crash),
|
||||
FG_BOLD, stats->test->category, stats->test->name, RESET);
|
||||
}
|
||||
|
||||
void normal_log_pre_suite(struct criterion_suite_set *set) {
|
||||
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),
|
||||
_s(msg_pre_suite, msg_pre_suite_pl, set->tests->size),
|
||||
FG_BLUE, (unsigned long) set->tests->size, RESET,
|
||||
FG_GOLD, set->suite.name, RESET);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "criterion/ordered-set.h"
|
||||
#include "timer.h"
|
||||
#include "config.h"
|
||||
#include "posix-compat.h"
|
||||
|
||||
void tap_log_pre_all(struct criterion_test_set *set) {
|
||||
size_t enabled_count = 0;
|
||||
|
@ -81,13 +82,22 @@ void tap_log_post_test(struct criterion_test_stats *stats) {
|
|||
stats->elapsed_time);
|
||||
for (struct criterion_assert_stats *asrt = stats->asserts; asrt; asrt = asrt->next) {
|
||||
if (!asrt->passed) {
|
||||
char *dup = strdup(*asrt->message ? asrt->message : asrt->condition), *saveptr = NULL;
|
||||
char *dup = strdup(*asrt->message ? asrt->message : asrt->condition);
|
||||
#ifdef VANILLA_WIN32
|
||||
char *line = strtok(dup, "\n");
|
||||
#else
|
||||
char *saveptr = NULL;
|
||||
char *line = strtok_r(dup, "\n", &saveptr);
|
||||
#endif
|
||||
criterion_important(" %s:%u: Assertion failed: %s\n",
|
||||
asrt->file,
|
||||
asrt->line,
|
||||
line);
|
||||
#ifdef VANILLA_WIN32
|
||||
while ((line = strtok(NULL, "\n")))
|
||||
#else
|
||||
while ((line = strtok_r(NULL, "\n", &saveptr)))
|
||||
#endif
|
||||
criterion_important(" %s\n", line);
|
||||
free(dup);
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
|
||||
# define VERSION_MSG "Tests compiled with Criterion v" VERSION "\n"
|
||||
|
||||
#ifdef HAVE_FNMATCH
|
||||
#ifdef HAVE_PCRE
|
||||
# define PATTERN_USAGE \
|
||||
" --pattern [PATTERN]: run tests matching the " \
|
||||
"given pattern\n"
|
||||
|
@ -124,7 +124,7 @@ 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
|
||||
#ifdef HAVE_PCRE
|
||||
{"pattern", required_argument, 0, 'p'},
|
||||
#endif
|
||||
{"always-succeed", no_argument, 0, 'y'},
|
||||
|
@ -146,7 +146,7 @@ int main(int argc, char *argv[]) {
|
|||
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
|
||||
#ifdef HAVE_PCRE
|
||||
opt->pattern = getenv("CRITERION_TEST_PATTERN");
|
||||
#endif
|
||||
|
||||
|
@ -162,7 +162,7 @@ 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
|
||||
#ifdef HAVE_PCRE
|
||||
case 'p': criterion_options.pattern = optarg; break;
|
||||
#endif
|
||||
case 't': use_tap = true; break;
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#include <assert.h>
|
||||
#include "posix-compat.h"
|
||||
#include "process.h"
|
||||
|
||||
#ifdef VANILLA_WIN32
|
||||
# define VC_EXTRALEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# undef _WIN32_WINNT
|
||||
# define _WIN32_WINNT 0x0502
|
||||
# include <windows.h>
|
||||
# include <io.h>
|
||||
# include <fcntl.h>
|
||||
|
@ -25,6 +28,11 @@
|
|||
# define WRITE_PROCESS_(Proc, What, Size) \
|
||||
WriteProcessMemory(Proc, &What, &What, Size, NULL);
|
||||
|
||||
# include <signal.h>
|
||||
# ifndef SIGALRM
|
||||
# define SIGALRM 14
|
||||
# endif
|
||||
|
||||
#else
|
||||
# include <unistd.h>
|
||||
# include <sys/wait.h>
|
||||
|
@ -53,19 +61,64 @@ struct pipe_handle {
|
|||
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;
|
||||
struct full_context {
|
||||
struct criterion_test test;
|
||||
struct criterion_test_extra_data test_data;
|
||||
struct criterion_suite suite;
|
||||
struct criterion_test_extra_data suite_data;
|
||||
f_worker_func func;
|
||||
struct pipe_handle pipe;
|
||||
int resumed;
|
||||
};
|
||||
|
||||
static TCHAR g_mapping_name[] = TEXT("WinCriterionWorker");
|
||||
#define MAPPING_SIZE sizeof (struct full_context)
|
||||
|
||||
static struct full_context local_ctx;
|
||||
#endif
|
||||
|
||||
int resume_child(void) {
|
||||
if (g_worker_context.test) {
|
||||
run_worker(&g_worker_context);
|
||||
return 1;
|
||||
}
|
||||
#ifdef VANILLA_WIN32
|
||||
HANDLE sharedMem = OpenFileMapping(
|
||||
FILE_MAP_ALL_ACCESS,
|
||||
FALSE,
|
||||
g_mapping_name);
|
||||
|
||||
if (sharedMem == NULL)
|
||||
return 0;
|
||||
|
||||
struct full_context *ctx = (struct full_context *) MapViewOfFile(sharedMem,
|
||||
FILE_MAP_ALL_ACCESS,
|
||||
0,
|
||||
0,
|
||||
MAPPING_SIZE);
|
||||
|
||||
if (ctx == NULL)
|
||||
exit(-1);
|
||||
|
||||
local_ctx = *ctx;
|
||||
g_worker_context = (struct worker_context) {
|
||||
&local_ctx.test,
|
||||
&local_ctx.suite,
|
||||
local_ctx.func,
|
||||
&local_ctx.pipe
|
||||
};
|
||||
|
||||
local_ctx.test.data = &local_ctx.test_data;
|
||||
local_ctx.suite.data = &local_ctx.suite_data;
|
||||
|
||||
ctx->resumed = 1;
|
||||
|
||||
UnmapViewOfFile(ctx);
|
||||
CloseHandle(sharedMem);
|
||||
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
|
||||
|
||||
run_worker(&g_worker_context);
|
||||
return 1;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
s_proc_handle *fork_process() {
|
||||
|
@ -83,37 +136,48 @@ s_proc_handle *fork_process() {
|
|||
return (void *) -1;
|
||||
|
||||
// Copy context over
|
||||
f_worker_func child_func = g_worker_context.func;
|
||||
HANDLE sharedMem = CreateFileMapping(
|
||||
INVALID_HANDLE_VALUE,
|
||||
NULL,
|
||||
PAGE_READWRITE,
|
||||
0,
|
||||
MAPPING_SIZE,
|
||||
g_mapping_name);
|
||||
|
||||
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;
|
||||
if (sharedMem == NULL)
|
||||
return (void *) -1;
|
||||
|
||||
g_worker_context = (struct worker_context) {
|
||||
&child_test,
|
||||
&child_suite,
|
||||
child_func,
|
||||
&child_pipe
|
||||
};
|
||||
struct full_context *ctx = (struct full_context *) MapViewOfFile(sharedMem,
|
||||
FILE_MAP_ALL_ACCESS,
|
||||
0,
|
||||
0,
|
||||
MAPPING_SIZE);
|
||||
|
||||
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));
|
||||
if (ctx == NULL) {
|
||||
CloseHandle(sharedMem);
|
||||
return (void *) -1;
|
||||
}
|
||||
|
||||
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));
|
||||
*ctx = (struct full_context) {
|
||||
.test = *g_worker_context.test,
|
||||
.test_data = *g_worker_context.test->data,
|
||||
.suite = *g_worker_context.suite,
|
||||
.func = g_worker_context.func,
|
||||
.pipe = *g_worker_context.pipe,
|
||||
.resumed = 0,
|
||||
};
|
||||
|
||||
if (g_worker_context.suite->data)
|
||||
ctx->suite_data = *g_worker_context.suite->data;
|
||||
|
||||
ResumeThread(info.hThread);
|
||||
CloseHandle(info.hThread);
|
||||
|
||||
while (!ctx->resumed); // wait until the child has initialized itself
|
||||
|
||||
UnmapViewOfFile(ctx);
|
||||
CloseHandle(sharedMem);
|
||||
|
||||
return unique_ptr(s_proc_handle, { info.hProcess });
|
||||
#else
|
||||
pid_t pid = fork();
|
||||
|
@ -131,7 +195,40 @@ void wait_process(s_proc_handle *handle, int *status) {
|
|||
DWORD exit_code;
|
||||
GetExitCodeProcess(handle->handle, &exit_code);
|
||||
CloseHandle(handle->handle);
|
||||
*status = exit_code << 8;
|
||||
|
||||
unsigned int sig = 0;
|
||||
switch (exit_code) {
|
||||
case STATUS_FLOAT_DENORMAL_OPERAND:
|
||||
case STATUS_FLOAT_DIVIDE_BY_ZERO:
|
||||
case STATUS_FLOAT_INEXACT_RESULT:
|
||||
case STATUS_FLOAT_INVALID_OPERATION:
|
||||
case STATUS_FLOAT_OVERFLOW:
|
||||
case STATUS_FLOAT_STACK_CHECK:
|
||||
case STATUS_FLOAT_UNDERFLOW:
|
||||
case STATUS_INTEGER_DIVIDE_BY_ZERO:
|
||||
case STATUS_INTEGER_OVERFLOW: sig = SIGFPE; break;
|
||||
|
||||
case STATUS_ILLEGAL_INSTRUCTION:
|
||||
case STATUS_PRIVILEGED_INSTRUCTION:
|
||||
case STATUS_NONCONTINUABLE_EXCEPTION: sig = SIGILL; break;
|
||||
|
||||
case STATUS_TIMEOUT: sig = SIGALRM; break;
|
||||
|
||||
case STATUS_ACCESS_VIOLATION:
|
||||
case STATUS_DATATYPE_MISALIGNMENT:
|
||||
case STATUS_ARRAY_BOUNDS_EXCEEDED:
|
||||
case STATUS_GUARD_PAGE_VIOLATION:
|
||||
case STATUS_IN_PAGE_ERROR:
|
||||
case STATUS_NO_MEMORY:
|
||||
case STATUS_INVALID_DISPOSITION:
|
||||
case STATUS_STACK_OVERFLOW: sig = SIGSEGV; break;
|
||||
|
||||
case STATUS_CONTROL_C_EXIT: sig = SIGINT; break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
*status = sig ? sig : exit_code << 8;
|
||||
#else
|
||||
waitpid(handle->pid, status, 0);
|
||||
#endif
|
||||
|
@ -206,3 +303,37 @@ bool is_current_process(s_proc_handle *proc) {
|
|||
return proc->pid == getpid();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef VANILLA_WIN32
|
||||
void *get_win_section_start(const char *section) {
|
||||
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER) GetModuleHandle(NULL);
|
||||
PIMAGE_NT_HEADERS ntHeader = ntHeader = (PIMAGE_NT_HEADERS) ((DWORD)(dosHeader) + (dosHeader->e_lfanew));
|
||||
|
||||
assert(dosHeader->e_magic == IMAGE_DOS_SIGNATURE);
|
||||
assert(ntHeader->Signature == IMAGE_NT_SIGNATURE);
|
||||
|
||||
PIMAGE_SECTION_HEADER pSecHeader = IMAGE_FIRST_SECTION(ntHeader);
|
||||
for(int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++, pSecHeader++) {
|
||||
if (!strncmp((char*) pSecHeader->Name, section, 8)) {
|
||||
return (char*) dosHeader + pSecHeader->VirtualAddress;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *get_win_section_end(const char *section) {
|
||||
PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER) GetModuleHandle(NULL);
|
||||
PIMAGE_NT_HEADERS ntHeader = ntHeader = (PIMAGE_NT_HEADERS) ((DWORD)(dosHeader) + (dosHeader->e_lfanew));
|
||||
|
||||
assert(dosHeader->e_magic == IMAGE_DOS_SIGNATURE);
|
||||
assert(ntHeader->Signature == IMAGE_NT_SIGNATURE);
|
||||
|
||||
PIMAGE_SECTION_HEADER pSecHeader = IMAGE_FIRST_SECTION(ntHeader);
|
||||
for(int i = 0; i < ntHeader->FileHeader.NumberOfSections; i++, pSecHeader++) {
|
||||
if (!strncmp((char*) pSecHeader->Name, section, 8)) {
|
||||
return (char*) dosHeader + (size_t) pSecHeader->VirtualAddress + pSecHeader->SizeOfRawData;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -53,4 +53,14 @@ void wait_process(s_proc_handle *handle, int *status);
|
|||
s_proc_handle *get_current_process();
|
||||
bool is_current_process(s_proc_handle *proc);
|
||||
|
||||
# ifdef VANILLA_WIN32
|
||||
void *get_win_section_start(const char *section);
|
||||
void *get_win_section_end(const char *section);
|
||||
# define GET_SECTION_START(Name) get_win_section_start(#Name)
|
||||
# define GET_SECTION_END(Name) get_win_section_end(#Name)
|
||||
# else
|
||||
# define GET_SECTION_START(Name) SECTION_START(Name)
|
||||
# define GET_SECTION_END(Name) SECTION_END(Name)
|
||||
# endif
|
||||
|
||||
#endif /* !POSIX_COMPAT_H_ */
|
||||
|
|
104
src/report.c
104
src/report.c
|
@ -31,83 +31,37 @@
|
|||
#include "criterion/ordered-set.h"
|
||||
#include "report.h"
|
||||
#include "config.h"
|
||||
#include "posix-compat.h"
|
||||
|
||||
#ifdef HAVE_FNMATCH
|
||||
#include <fnmatch.h>
|
||||
#endif
|
||||
|
||||
#define IMPL_CALL_REPORT_HOOKS(Kind) \
|
||||
IMPL_SECTION_LIMITS(f_report_hook, crit_ ## Kind); \
|
||||
void call_report_hooks_##Kind(void *data) { \
|
||||
for (f_report_hook *hook = SECTION_START(crit_ ## Kind); \
|
||||
hook < SECTION_END(crit_ ## Kind); \
|
||||
++hook) { \
|
||||
(*hook)(data); \
|
||||
} \
|
||||
#define IMPL_CALL_REPORT_HOOKS(Kind) \
|
||||
IMPL_SECTION_LIMITS(f_report_hook, HOOK_SECTION(Kind)); \
|
||||
void call_report_hooks_##Kind(void *data) { \
|
||||
for (f_report_hook *hook = GET_SECTION_START(HOOK_SECTION(Kind)); \
|
||||
hook < (f_report_hook*) GET_SECTION_END(HOOK_SECTION(Kind)); \
|
||||
++hook) { \
|
||||
(*hook)(data); \
|
||||
} \
|
||||
}
|
||||
|
||||
#define IMPL_REPORT_HOOK(Type) \
|
||||
IMPL_CALL_REPORT_HOOKS(Type); \
|
||||
ReportHook(Type)
|
||||
IMPL_CALL_REPORT_HOOKS(PRE_ALL);
|
||||
IMPL_CALL_REPORT_HOOKS(PRE_SUITE);
|
||||
IMPL_CALL_REPORT_HOOKS(PRE_INIT);
|
||||
IMPL_CALL_REPORT_HOOKS(PRE_TEST);
|
||||
IMPL_CALL_REPORT_HOOKS(ASSERT);
|
||||
IMPL_CALL_REPORT_HOOKS(TEST_CRASH);
|
||||
IMPL_CALL_REPORT_HOOKS(POST_TEST);
|
||||
IMPL_CALL_REPORT_HOOKS(POST_FINI);
|
||||
IMPL_CALL_REPORT_HOOKS(POST_SUITE);
|
||||
IMPL_CALL_REPORT_HOOKS(POST_ALL);
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void nothing() {}
|
||||
ReportHook(PRE_ALL)() {}
|
||||
ReportHook(PRE_SUITE)() {}
|
||||
ReportHook(PRE_INIT)() {}
|
||||
ReportHook(PRE_TEST)() {}
|
||||
ReportHook(ASSERT)() {}
|
||||
ReportHook(TEST_CRASH)() {}
|
||||
ReportHook(POST_TEST)() {}
|
||||
ReportHook(POST_FINI)() {}
|
||||
ReportHook(POST_SUITE)() {}
|
||||
ReportHook(POST_ALL)() {}
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
||||
IMPL_REPORT_HOOK(PRE_SUITE)(struct criterion_suite_set *set) {
|
||||
log(pre_suite, set);
|
||||
}
|
||||
|
||||
IMPL_REPORT_HOOK(PRE_INIT)(struct criterion_test *test) {
|
||||
log(pre_init, test);
|
||||
}
|
||||
|
||||
IMPL_REPORT_HOOK(PRE_TEST)(struct criterion_test *test) {
|
||||
log(pre_test, test);
|
||||
}
|
||||
|
||||
IMPL_REPORT_HOOK(ASSERT)(struct criterion_assert_stats *stats) {
|
||||
log(assert, stats);
|
||||
}
|
||||
|
||||
IMPL_REPORT_HOOK(TEST_CRASH)(struct criterion_test_stats *stats) {
|
||||
log(test_crash, stats);
|
||||
}
|
||||
|
||||
IMPL_REPORT_HOOK(POST_TEST)(struct criterion_test_stats *stats) {
|
||||
log(post_test, stats);
|
||||
}
|
||||
|
||||
IMPL_REPORT_HOOK(POST_FINI)(struct criterion_test_stats *stats) {
|
||||
log(post_fini, stats);
|
||||
}
|
||||
|
||||
IMPL_REPORT_HOOK(POST_SUITE)(struct criterion_suite_stats *stats) {
|
||||
log(post_suite, stats);
|
||||
}
|
||||
|
||||
IMPL_REPORT_HOOK(POST_ALL)(struct criterion_global_stats *stats) {
|
||||
log(post_all, stats);
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@
|
|||
|
||||
# define report(Kind, Data) call_report_hooks_##Kind(Data)
|
||||
|
||||
# define DECL_CALL_REPORT_HOOKS(Kind) \
|
||||
DECL_SECTION_LIMITS(f_report_hook, crit_ ## Kind); \
|
||||
# define DECL_CALL_REPORT_HOOKS(Kind) \
|
||||
DECL_SECTION_LIMITS(f_report_hook, HOOK_SECTION(Kind)); \
|
||||
void call_report_hooks_##Kind(void *data)
|
||||
|
||||
DECL_CALL_REPORT_HOOKS(PRE_ALL);
|
||||
|
|
84
src/runner.c
84
src/runner.c
|
@ -27,6 +27,7 @@
|
|||
#include "criterion/criterion.h"
|
||||
#include "criterion/options.h"
|
||||
#include "criterion/ordered-set.h"
|
||||
#include "criterion/logging.h"
|
||||
#include "stats.h"
|
||||
#include "runner.h"
|
||||
#include "report.h"
|
||||
|
@ -35,14 +36,22 @@
|
|||
#include "timer.h"
|
||||
#include "posix-compat.h"
|
||||
#include "abort.h"
|
||||
#include "config.h"
|
||||
|
||||
IMPL_SECTION_LIMITS(struct criterion_test, criterion_tests);
|
||||
IMPL_SECTION_LIMITS(struct criterion_suite, crit_suites);
|
||||
#ifdef HAVE_PCRE
|
||||
#include "extmatch.h"
|
||||
#endif
|
||||
|
||||
IMPL_SECTION_LIMITS(struct criterion_test, cr_tst);
|
||||
IMPL_SECTION_LIMITS(struct criterion_suite, cr_sts);
|
||||
|
||||
// This is here to make the test suite & test sections non-empty
|
||||
TestSuite();
|
||||
Test(,) {};
|
||||
|
||||
__attribute__ ((always_inline))
|
||||
static inline void nothing() {}
|
||||
|
||||
int cmp_suite(void *a, void *b) {
|
||||
struct criterion_suite *s1 = a, *s2 = b;
|
||||
return strcmp(s1->name, s2->name);
|
||||
|
@ -67,6 +76,9 @@ struct criterion_test_set *criterion_init(void) {
|
|||
struct criterion_ordered_set *suites = new_ordered_set(cmp_suite, dtor_suite_set);
|
||||
|
||||
FOREACH_SUITE_SEC(s) {
|
||||
if (!s->name)
|
||||
break;
|
||||
|
||||
struct criterion_suite_set css = {
|
||||
.suite = *s,
|
||||
};
|
||||
|
@ -75,6 +87,9 @@ struct criterion_test_set *criterion_init(void) {
|
|||
|
||||
size_t nb_tests = 0;
|
||||
FOREACH_TEST_SEC(test) {
|
||||
if (!test->category)
|
||||
break;
|
||||
|
||||
if (!*test->category)
|
||||
continue;
|
||||
|
||||
|
@ -109,6 +124,7 @@ static void map_tests(struct criterion_test_set *set,
|
|||
continue;
|
||||
|
||||
report(PRE_SUITE, s);
|
||||
log(pre_suite, s);
|
||||
|
||||
smart struct criterion_suite_stats *suite_stats = suite_stats_init(&s->suite);
|
||||
|
||||
|
@ -124,12 +140,10 @@ static void map_tests(struct criterion_test_set *set,
|
|||
}
|
||||
|
||||
report(POST_SUITE, suite_stats);
|
||||
log(post_suite, suite_stats);
|
||||
}
|
||||
}
|
||||
|
||||
__attribute__ ((always_inline))
|
||||
static inline void nothing() {}
|
||||
|
||||
static void run_test_child(struct criterion_test *test,
|
||||
struct criterion_suite *suite) {
|
||||
|
||||
|
@ -197,15 +211,28 @@ static void run_test(struct criterion_global_stats *stats,
|
|||
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);
|
||||
test_started = true;
|
||||
break;
|
||||
case ASSERT: report(ASSERT, ev->data); break;
|
||||
case POST_TEST: report(POST_TEST, test_stats);
|
||||
normal_finish = true;
|
||||
break;
|
||||
case POST_FINI: report(POST_FINI, test_stats); break;
|
||||
case PRE_INIT:
|
||||
report(PRE_INIT, test);
|
||||
log(pre_init, test);
|
||||
break;
|
||||
case PRE_TEST:
|
||||
report(PRE_TEST, test);
|
||||
log(pre_test, test);
|
||||
test_started = true;
|
||||
break;
|
||||
case ASSERT:
|
||||
report(ASSERT, ev->data);
|
||||
log(assert, ev->data);
|
||||
break;
|
||||
case POST_TEST:
|
||||
report(POST_TEST, test_stats);
|
||||
log(post_test, test_stats);
|
||||
normal_finish = true;
|
||||
break;
|
||||
case POST_FINI:
|
||||
report(POST_FINI, test_stats);
|
||||
log(post_fini, test_stats);
|
||||
break;
|
||||
}
|
||||
sfree(ev);
|
||||
}
|
||||
|
@ -225,21 +252,49 @@ static void run_test(struct criterion_global_stats *stats,
|
|||
test_stats->signal = status.status;
|
||||
if (test->data->signal == 0) {
|
||||
push_event(TEST_CRASH);
|
||||
log(test_crash, test_stats);
|
||||
} else {
|
||||
double elapsed_time = 0;
|
||||
push_event(POST_TEST, .data = &elapsed_time);
|
||||
log(post_test, test_stats);
|
||||
push_event(POST_FINI);
|
||||
log(post_fini, test_stats);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_PCRE
|
||||
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) {
|
||||
const char *errmsg;
|
||||
int ret = extmatch(criterion_options.pattern, test->data->identifier_, &errmsg);
|
||||
if (ret == -10) {
|
||||
printf("pattern error: %s\n", errmsg);
|
||||
exit(1);
|
||||
} else if (ret < 0) {
|
||||
test->data->disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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();
|
||||
#ifdef HAVE_PCRE
|
||||
if (criterion_options.pattern)
|
||||
disable_unmatching(set);
|
||||
#endif
|
||||
|
||||
report(PRE_ALL, set);
|
||||
log(pre_all, set);
|
||||
|
||||
smart struct criterion_global_stats *stats = stats_init();
|
||||
map_tests(set, stats, run_test);
|
||||
|
@ -248,6 +303,7 @@ static int criterion_run_all_tests_impl(void) {
|
|||
return -1;
|
||||
|
||||
report(POST_ALL, stats);
|
||||
log(post_all, stats);
|
||||
return stats->tests_failed == 0;
|
||||
}
|
||||
|
||||
|
|
13
src/runner.h
13
src/runner.h
|
@ -25,20 +25,21 @@
|
|||
# define CRITERION_RUNNER_H_
|
||||
|
||||
# include "criterion/types.h"
|
||||
# include "posix-compat.h"
|
||||
|
||||
DECL_SECTION_LIMITS(struct criterion_test, criterion_tests);
|
||||
DECL_SECTION_LIMITS(struct criterion_suite, crit_suites);
|
||||
DECL_SECTION_LIMITS(struct criterion_test, cr_tst);
|
||||
DECL_SECTION_LIMITS(struct criterion_suite, cr_sts);
|
||||
|
||||
struct criterion_test_set *criterion_init(void);
|
||||
|
||||
# define FOREACH_TEST_SEC(Test) \
|
||||
for (struct criterion_test *Test = SECTION_START(criterion_tests); \
|
||||
Test < SECTION_END(criterion_tests); \
|
||||
for (struct criterion_test *Test = GET_SECTION_START(cr_tst); \
|
||||
Test < (struct criterion_test*) GET_SECTION_END(cr_tst); \
|
||||
++Test)
|
||||
|
||||
# define FOREACH_SUITE_SEC(Suite) \
|
||||
for (struct criterion_suite *Suite = SECTION_START(crit_suites); \
|
||||
Suite < SECTION_END(crit_suites); \
|
||||
for (struct criterion_suite *Suite = GET_SECTION_START(cr_sts); \
|
||||
Suite < (struct criterion_suite*) GET_SECTION_END(cr_sts); \
|
||||
++Suite)
|
||||
|
||||
#endif /* !CRITERION_RUNNER_H_ */
|
||||
|
|
Loading…
Add table
Reference in a new issue