[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:
Snaipe 2015-08-05 06:54:45 +02:00
commit 4e36f68ff3
50 changed files with 2060 additions and 379 deletions

View file

@ -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
View 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
)

View 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()

View 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()

View 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("###########################################################################")

View 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)

View 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)

View 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)

View 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()

View 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
View file

@ -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
View file

@ -1,3 +0,0 @@
[submodule "dependencies/csptr"]
path = dependencies/csptr
url = https://github.com/Snaipe/c-smart-pointers.git

View file

@ -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
View 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"
)

View file

@ -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

View file

@ -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

View file

@ -1,5 +1,5 @@
Criterion
<img src="doc/criterion-title.png" height="96" alt="Criterion Logo" />
=========
[![Build Status](https://travis-ci.org/Snaipe/Criterion.svg?branch=bleeding)](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/

View file

@ -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:

View file

@ -1,4 +0,0 @@
#!/bin/sh
git submodule update --init --recursive
autopoint
autoreconf -i

View file

@ -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 +0,0 @@
Subproject commit 4f3e63aca586939ed734f4e76c4f7f7f8c07d247

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

View file

@ -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

View file

@ -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
-----

View file

@ -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
~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -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), \

View file

@ -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_

View file

@ -26,6 +26,7 @@
# include <stdbool.h>
# include <stddef.h>
# include "common.h"
struct criterion_test_extra_data {
int sentinel_;

View file

1
po/CMakeLists.txt Normal file
View file

@ -0,0 +1 @@
GettextTranslate()

View file

@ -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

View file

@ -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
View 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()

View file

@ -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) {

View file

@ -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
View 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_ */

View file

@ -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
View 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
View 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_ */

View file

@ -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, ...) {

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;

View file

@ -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

View file

@ -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_ */

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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_ */