Merge branch 'master' into dev-modernize

Hopefully nothing went too wrong.
This commit is contained in:
Martin Hořeňovský 2017-06-05 17:19:42 +02:00
commit 7f6773bb4d
27 changed files with 264 additions and 52 deletions

View File

@ -19,6 +19,10 @@ else()
endif()
if(USE_WMAIN)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ENTRY:wmainCRTStartup")
endif()
#checks that the given hard-coded list contains all headers + sources in the given folder
function(CheckFileList LIST_VAR FOLDER)
set(MESSAGE " should be added to the variable ${LIST_VAR}")
@ -55,6 +59,7 @@ set(TEST_SOURCES
${SELF_TEST_DIR}/CmdLineTests.cpp
${SELF_TEST_DIR}/CompilationTests.cpp
${SELF_TEST_DIR}/ConditionTests.cpp
${SELF_TEST_DIR}/DecompositionTests.cpp
${SELF_TEST_DIR}/EnumToString.cpp
${SELF_TEST_DIR}/ExceptionTests.cpp
${SELF_TEST_DIR}/MessageTests.cpp
@ -237,28 +242,35 @@ SOURCE_GROUP("Benchmarks" FILES ${BENCH_SOURCES})
# configure the executable
include_directories(${HEADER_DIR})
add_executable(SelfTest ${TEST_SOURCES} ${IMPL_SOURCES} ${HEADERS})
add_executable(Benchmark ${BENCH_SOURCES} ${HEADERS})
# Add desired warnings
if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" )
target_compile_options( SelfTest PRIVATE -Wall -Wextra )
target_compile_options( Benchmark PRIVATE -Wall -Wextra )
endif()
if ( CMAKE_CXX_COMPILER_ID MATCHES "MSVC" )
target_compile_options( SelfTest PRIVATE /W4 /w44265 /WX )
target_compile_options( Benchmark PRIVATE /W4 )
endif()
# Projects consuming Catch via ExternalProject_Add might want to use install step
# without building all of our selftests.
if (NOT NO_SELFTEST)
add_executable(SelfTest ${TEST_SOURCES} ${IMPL_SOURCES} ${HEADERS})
add_executable(Benchmark ${BENCH_SOURCES} ${HEADERS})
# Add desired warnings
if ( CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU" )
target_compile_options( SelfTest PRIVATE -Wall -Wextra )
target_compile_options( Benchmark PRIVATE -Wall -Wextra )
endif()
if ( CMAKE_CXX_COMPILER_ID MATCHES "MSVC" )
target_compile_options( SelfTest PRIVATE /W4 /w44265 /WX )
target_compile_options( Benchmark PRIVATE /W4 )
endif()
# configure unit tests via CTest
enable_testing()
add_test(NAME RunTests COMMAND SelfTest)
# configure unit tests via CTest
enable_testing()
add_test(NAME RunTests COMMAND SelfTest)
add_test(NAME ListTests COMMAND SelfTest --list-tests)
set_tests_properties(ListTests PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ test cases")
add_test(NAME ListTests COMMAND SelfTest --list-tests)
set_tests_properties(ListTests PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ test cases")
add_test(NAME ListTags COMMAND SelfTest --list-tags)
set_tests_properties(ListTags PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ tags")
endif() # !NO_SELFTEST
add_test(NAME ListTags COMMAND SelfTest --list-tags)
set_tests_properties(ListTags PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ tags")
install(DIRECTORY "single_include/" DESTINATION "include/catch")

View File

@ -8,7 +8,13 @@ os:
environment:
matrix:
- additional_flags: "/permissive- /std:c++latest"
wmain: 0
- additional_flags: ""
wmain: 0
- additional_flags: "/D_UNICODE /DUNICODE"
wmain: 1
matrix:
exclude:
@ -41,7 +47,7 @@ configuration:
#Cmake will autodetect the compiler, but we set the arch
before_build:
- set CXXFLAGS=%additional_flags%
- cmake -H. -BBuild -A%PLATFORM%
- cmake -H. -BBuild -A%PLATFORM% -DUSE_WMAIN=%wmain%
# build with MSBuild
build:

View File

@ -25,13 +25,27 @@
# #
# include(ParseAndAddCatchTests) #
# ParseAndAddCatchTests(${PROJECT_NAME}) #
# #
# The following variables affect the behavior of the script: #
# #
# PARSE_CATCH_TESTS_VERBOSE (Default OFF) #
# -- enables debug messages #
# #
#==================================================================================================#
cmake_minimum_required(VERSION 2.8.8)
option(PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF)
function(PrintDebugMessage)
if(PARSE_CATCH_TESTS_VERBOSE)
message(STATUS "ParseAndAddCatchTests: ${ARGV}")
endif()
endfunction()
# This removes the contents between
# - block comments (i.e. /* ... */)
# - full line comments (i.e. // ... )
# - block comments (i.e. /* ... */)
# - full line comments (i.e. // ... )
# contents have been read into '${CppCode}'.
# !keep partial line comments
function(RemoveComments CppCode)
@ -47,9 +61,13 @@ endfunction()
# Worker function
function(ParseFile SourceFile TestTarget)
# According to CMake docs EXISTS behavior is well-defined only for full paths.
get_filename_component(SourceFile ${SourceFile} ABSOLUTE)
if(NOT EXISTS ${SourceFile})
message(WARNING "Cannot find source file: ${SourceFile}")
return()
endif()
PrintDebugMessage("parsing ${SourceFile}")
file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME)
# Remove block and fullline comments
@ -90,25 +108,42 @@ function(ParseFile SourceFile TestTarget)
else()
set(CTestName "${Name}")
endif()
set(CTestName "${TestTarget}:${CTestName}")
# add target to labels to enable running all tests added from this target
set(Labels ${TestTarget})
if(TestStringsLength EQUAL 2)
list(GET TestStrings 1 Tags)
string(TOLOWER "${Tags}" Tags)
# remove target from labels if the test is hidden
if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*")
list(REMOVE_ITEM Labels ${TestTarget})
endif()
string(REPLACE "]" ";" Tags "${Tags}")
string(REPLACE "[" "" Tags "${Tags}")
endif()
list(APPEND Labels ${Tags})
PrintDebugMessage("Adding test \"${CTestName}\"")
if(Labels)
PrintDebugMessage("Setting labels to ${Labels}")
endif()
# Add the test and set its properties
add_test(NAME "\"${CTestName}\"" COMMAND ${TestTarget} ${Name} ${AdditionalCatchParameters})
set_tests_properties("\"${CTestName}\"" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran"
LABELS "${Tags}")
LABELS "${Labels}")
endforeach()
endfunction()
# entry point
function(ParseAndAddCatchTests TestTarget)
PrintDebugMessage("Started parsing ${TestTarget}")
get_target_property(SourceFiles ${TestTarget} SOURCES)
PrintDebugMessage("Found the following sources: ${SourceFiles}")
foreach(SourceFile ${SourceFiles})
ParseFile(${SourceFile} ${TestTarget})
endforeach()
endfunction()
PrintDebugMessage("Finished parsing ${TestTarget}")
endfunction()

View File

@ -17,7 +17,7 @@ Fine tuning:
Running:
* [Command line](command-line.md)
* [Build systems](build-systems.md)
* [CI and Build system integration](build-systems.md)
FAQ:
* [Why are my tests slow to compile?](slow-compiles.md)

View File

@ -1,4 +1,4 @@
# Integration with build systems
# CI and build system integration
Build Systems may refer to low-level tools, like CMake, or larger systems that run on servers, like Jenkins or TeamCity. This page will talk about both.
@ -134,6 +134,10 @@ TEST_CASE("Test3", "[a][b][c]") {
```
would be registered as 3 tests, `Test1`, `Test2` and `Test3`, and ctest 4 labels would be created, `a`, `b`, `c` and `unit`.
### CodeCoverage module (GCOV, LCOV...)
If you are using GCOV tool to get testing coverage of your code, and are not sure how to integrate it with CMake and Catch, there should be an external example over at https://github.com/fkromer/catch_cmake_coverage
---
[Home](Readme.md)
[Home](Readme.md)

View File

@ -13,4 +13,4 @@ fact then please let us know - either directly, via a PR or
- Bloomberg
- NASA
- [Inscopix Inc.](https://www.inscopix.com/)

View File

@ -31,7 +31,7 @@ struct MyListener : Catch::TestEventListenerBase {
// Perform some setup before a test case is run
}
virtual void testCaseEnded( TestCaseStats const& testCaseStats ) override {
virtual void testCaseEnded( Catch::TestCaseStats const& testCaseStats ) override {
// Tear-down after a test case is run
}
};
@ -70,4 +70,4 @@ just look in the source code to see what fields are available.
---
[Home](Readme.md)
[Home](Readme.md)

View File

@ -1,6 +1,32 @@
# Logging macros
Additional messages can be logged during a test case.
Additional messages can be logged during a test case. Note that the messages are scoped and thus will not be reported if failure occurs in scope preceding the message declaration. An example:
```cpp
TEST_CASE("Foo") {
INFO("Test case start");
for (int i = 0; i < 2; ++i) {
INFO("The number is " << i);
CHECK(i == 0);
}
}
TEST_CASE("Bar") {
INFO("Test case start");
for (int i = 0; i < 2; ++i) {
INFO("The number is " << i);
CHECK(i == i);
}
CHECK(false);
}
```
When the `CHECK` fails in the "Foo" test case, then two messages will be printed.
```
Test case start
The number is 1
```
When the last `CHECK` fails in the "Bar" test case, then only one message will be printed: `Test case start`.
## Streaming macros

View File

@ -1,6 +1,6 @@
# Open Source projects using Catch
Catch is great for open source. With it's [liberal license](../LICENSE_1_0.txt) and single-header, dependency-free, distribution
Catch is great for open source. With it's [liberal license](../LICENSE.txt) and single-header, dependency-free, distribution
it's easy to just drop the header into your project and start writing tests - what's not to like?
As a result Catch is now being used in many Open Source projects, including some quite well known ones.

View File

@ -1,3 +1,18 @@
# 1.9.4
### Fixes
* `CATCH_FAIL` macro no longer causes compilation error without variadic macro support
* `INFO` messages are no longer cleared after being reported once
### Improvements and minor changes
* Catch now uses `wmain` when compiled under Windows and `UNICODE` is defined.
* Note that Catch still officially supports only ASCII
# 1.9.3
### Fixes
* Completed the fix for (lack of) uint64_t in earlier Visual Studios
# 1.9.2
### Improvements and minor changes

44
docs/release-process.md Normal file
View File

@ -0,0 +1,44 @@
# How to release
When enough changes have accumulated, it is time to release new version of Catch. This document describes the proces in doing so, that no steps are forgotten. Note that all referenced scripts can be found in the `scripts/` directory.
## Approval testing
Catch's releases are primarily validated against output from previous release, stored in `projects/SelfTest/Baselines`. To validate current sources, build the SelfTest binary and pass it to the `approvalTests.py` script: `approvalTests.py <path/to/SelfTest>`.
There should be no differences, as Approval tests should be updated when changes to Catch are made, but if there are, then they need to be manually reviewed and either approved (using `approve.py`) or Catch requires other fixes.
## Incrementing version number
Catch uses a variant of [semantic versioning](http://semver.org/), with breaking API changes (and thus major version increments) being very rare. Thus, the release will usually increment the patch version, when it only contains couple of bugfixes, or minor version, when it contains new functionality, or larger changes in implementation of current functionality.
After deciding which part of version number should be incremented, you can use one of the `*Release.py` scripts to perform the required changes to Catch.
## Generate updated single-include header
After updating version number, regenerate single-include header using `generateSingleHeader.py`.
## Release notes
Once a release is ready, release notes need to be written. They should summarize changes done since last release. For rough idea of expected notes see previous releases. Once written, release notes should be placed in `docs/release-notes.md`.
## Commit and push update to GitHub
After version number is incremented, single-include header is regenerated and release notes are updated, changes should be commited and pushed to GitHub.
## Release on GitHub
After pushing changes to GitHub, GitHub release *needs* to be created. Tag version and release title should be same as the new version, description should contain the release notes for the current release. Single header version of `catch.hpp` *needs* to be attached as a binary, as that is where the official download link links to. Preferably it should use linux line endings.
## vcpkg update
As a last step, optionally update Microsoft's package manager [vcpkg](https://github.com/Microsoft/vcpkg) with Catch's new version. `updateVcpkgPackage.py` can do a lot of neccessary work for you, but it assumes that you have your fork of vcpkg checked out in a directory next to the directory, where you have checked out Catch.
It creates a branch and commits neccessary changes, that you then should review, synchronize and open a PR against.

View File

@ -28,7 +28,7 @@ The tag expression, ```"[widget]"``` selects A, B & D. ```"[gadget]"``` selects
For more detail on command line selection see [the command line docs](command-line.md#specifying-which-tests-to-run)
Tag names are not case sensitive.
Tag names are not case sensitive and can contain any ASCII characters. This means that tags `[tag with spaces]` and `[I said "good day"]` are both allowed tags and can be filtered on. Escapes are not supported however and `[\]]` is not a valid tag.
### Special Tags

View File

@ -68,7 +68,7 @@ with expansion:
0 == 1
```
Note that we get the actual return value of Factorial(0) printed for us (0) - even though we used a natural expression with the == operator. That let's us immediately see what the problem is.
Note that we get the actual return value of Factorial(0) printed for us (0) - even though we used a natural expression with the == operator. That lets us immediately see what the problem is.
Let's change the factorial function to:

View File

@ -166,6 +166,32 @@ namespace Catch {
return returnCode;
}
#if defined(WIN32) && defined(UNICODE)
int run( int argc, wchar_t const* const* const argv ) {
char **utf8Argv = new char *[ argc ];
for ( int i = 0; i < argc; ++i ) {
int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL );
utf8Argv[ i ] = new char[ bufSize ];
WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL );
}
int returnCode = applyCommandLine( argc, utf8Argv );
if( returnCode == 0 )
returnCode = run();
for ( int i = 0; i < argc; ++i )
delete [] utf8Argv[ i ];
delete [] utf8Argv;
return returnCode;
}
#endif
int run() {
if( m_configData.showHelp )
return 0;

View File

@ -33,8 +33,8 @@ namespace Catch {
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& );
template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& );
private:
DecomposedExpression& operator = (DecomposedExpression const&);
private:
DecomposedExpression& operator = (DecomposedExpression const&);
};
struct AssertionInfo

View File

@ -10,9 +10,15 @@
#ifndef __OBJC__
#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
// Standard C/C++ Win32 Unicode wmain entry point
extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
#else
// Standard C/C++ main entry point
int main (int argc, char * argv[]) {
int result = Catch::Session().run( argc, argv );
#endif
int result = Catch::Session().run( argc, argv );
return ( result < 0xff ? result : 0xff );
}

View File

@ -28,7 +28,7 @@ namespace Catch {
namespace Catch {
struct FatalConditionHandler {
void reset() {}
void reset() {}
};
}
@ -106,7 +106,7 @@ namespace Catch {
namespace Catch {
struct FatalConditionHandler {
void reset() {}
void reset() {}
};
}

View File

@ -104,7 +104,7 @@ namespace Catch {
namespace Matchers {
namespace Impl {
namespace NSStringMatchers {
struct StringHolder : MatcherBase<NSString*>{
StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}

View File

@ -142,8 +142,9 @@ namespace Catch {
m_totals.assertions.failed++;
}
if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) )
m_messages.clear();
// We have no use for the return value (whether messages should be cleared), because messages were made scoped
// and should be let to clear themselves out.
static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
// Reset working state
m_lastAssertionInfo = AssertionInfo( std::string(), m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
@ -296,7 +297,7 @@ namespace Catch {
// This just means the test was aborted due to failure
}
catch(...) {
// Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
// Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
// are reported without translation at the point of origin.
if (m_shouldReportUnexpected) {
makeUnexpectedResultBuilder().useActiveException();

View File

@ -10,7 +10,6 @@
#include "catch_stream.h"
#include "catch_compiler_capabilities.h"
#include "catch_suppress_warnings.h"
#include <sstream>
#include <string>
@ -230,6 +229,5 @@ namespace Catch {
};
}
#include "catch_reenable_warnings.h"
#endif // TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED

View File

@ -29,7 +29,7 @@ namespace Catch {
// + 1 for null terminator
const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
char buffer[maxDoubleSize];
// Save previous errno, to prevent sprintf from overwriting it
ErrnoGuard guard;
#ifdef _MSC_VER

View File

@ -143,6 +143,7 @@ namespace Catch {
}
virtual void testCaseStarting( TestCaseInfo const& testInfo ) override {
m_testTimer.start();
StreamingReporterBase::testCaseStarting( testInfo );
stream << "##teamcity[testStarted name='"
<< escape( testInfo.name ) << "']\n";
@ -159,7 +160,8 @@ namespace Catch {
<< escape( testCaseStats.testInfo.name )
<< "' out='" << escape( testCaseStats.stdErr ) << "']\n";
stream << "##teamcity[testFinished name='"
<< escape( testCaseStats.testInfo.name ) << "']\n";
<< escape( testCaseStats.testInfo.name ) << "' duration='"
<< m_testTimer.getElapsedMilliseconds() << "']\n";
}
private:
@ -198,6 +200,7 @@ namespace Catch {
}
private:
bool m_headerPrintedForThisSection = false;
Timer m_testTimer;
};
#ifdef CATCH_IMPL

View File

@ -0,0 +1,28 @@
/*
* Created by Martin on 27/5/2017.
* Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#include <iostream>
struct truthy {
truthy(bool b):m_value(b){}
operator bool() const {
return false;
}
bool m_value;
};
std::ostream& operator<<(std::ostream& o, truthy) {
o << "Hey, its truthy!";
return o;
}
#include "catch.hpp"
TEST_CASE( "Reconstruction should be based on stringification: #914" , "[Decomposition][failing][.]") {
CHECK(truthy(false));
}

View File

@ -401,5 +401,5 @@ static int f() {
TEST_CASE( "#835 -- errno should not be touched by Catch", "[!shouldfail]" ) {
errno = 1;
CHECK(f() == 0);
REQUIRE(errno == 1); // Check that f() doesn't touch errno.
REQUIRE(errno == 1); // Check that f() doesn't touch errno.
}

View File

@ -9,7 +9,6 @@
TEST_CASE( "Character pretty printing" ){
//
SECTION("Specifically escaped"){
char tab = '\t';
char newline = '\n';
@ -36,7 +35,7 @@ TEST_CASE( "Character pretty printing" ){
char c = static_cast<char>(i);
REQUIRE(c == i);
}
}
}
}

View File

@ -36,6 +36,13 @@ exeNameParser = re.compile(r'''
# This is a hack until something more reasonable is figured out
specialCaseParser = re.compile(r'file\((\d+)\)')
# errno macro expands into various names depending on platform, so we need to fix them up as well
errnoParser = re.compile(r'''
\(\*__errno_location\ \(\)\)
|
\(\*__error\(\)\)
''', re.VERBOSE)
if len(sys.argv) == 2:
cmdPath = sys.argv[1]
else:
@ -45,9 +52,9 @@ overallResult = 0
def diffFiles(fileA, fileB):
with open(fileA, 'r') as file:
aLines = file.readlines()
aLines = [line.rstrip() for line in file.readlines()]
with open(fileB, 'r') as file:
bLines = file.readlines()
bLines = [line.rstrip() for line in file.readlines()]
shortenedFilenameA = fileA.rsplit(os.sep, 1)[-1]
shortenedFilenameB = fileB.rsplit(os.sep, 1)[-1]
@ -90,6 +97,7 @@ def filterLine(line):
line = durationsParser.sub(' time="{duration}"', line)
line = timestampsParser.sub(' timestamp="{iso8601-timestamp}"', line)
line = specialCaseParser.sub('file:\g<1>', line)
line = errnoParser.sub('errno', line)
return line
@ -119,7 +127,7 @@ def approve(baseName, args):
if os.path.exists(baselinesPath):
diffResult = diffFiles(baselinesPath, filteredResultsPath)
if diffResult:
print(''.join(diffResult))
print('\n'.join(diffResult))
print(" \n****************************\n \033[91mResults differed")
if len(diffResult) > overallResult:
overallResult = len(diffResult)

View File

@ -96,6 +96,7 @@ def git_push(path_to_repo):
# Update repo to current master, so we don't work off old version of the portsfile
subprocess.call('git pull Microsoft master', shell=True)
subprocess.call('git push', shell=True)
# Create a new branch for the update
subprocess.call('git checkout -b catch-{}'.format(ver_string), shell=True)