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() 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 #checks that the given hard-coded list contains all headers + sources in the given folder
function(CheckFileList LIST_VAR FOLDER) function(CheckFileList LIST_VAR FOLDER)
set(MESSAGE " should be added to the variable ${LIST_VAR}") 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}/CmdLineTests.cpp
${SELF_TEST_DIR}/CompilationTests.cpp ${SELF_TEST_DIR}/CompilationTests.cpp
${SELF_TEST_DIR}/ConditionTests.cpp ${SELF_TEST_DIR}/ConditionTests.cpp
${SELF_TEST_DIR}/DecompositionTests.cpp
${SELF_TEST_DIR}/EnumToString.cpp ${SELF_TEST_DIR}/EnumToString.cpp
${SELF_TEST_DIR}/ExceptionTests.cpp ${SELF_TEST_DIR}/ExceptionTests.cpp
${SELF_TEST_DIR}/MessageTests.cpp ${SELF_TEST_DIR}/MessageTests.cpp
@ -237,6 +242,10 @@ SOURCE_GROUP("Benchmarks" FILES ${BENCH_SOURCES})
# configure the executable # configure the executable
include_directories(${HEADER_DIR}) include_directories(${HEADER_DIR})
# 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(SelfTest ${TEST_SOURCES} ${IMPL_SOURCES} ${HEADERS})
add_executable(Benchmark ${BENCH_SOURCES} ${HEADERS}) add_executable(Benchmark ${BENCH_SOURCES} ${HEADERS})
@ -261,4 +270,7 @@ set_tests_properties(ListTests PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ test c
add_test(NAME ListTags COMMAND SelfTest --list-tags) add_test(NAME ListTags COMMAND SelfTest --list-tags)
set_tests_properties(ListTags PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ tags") set_tests_properties(ListTags PROPERTIES PASS_REGULAR_EXPRESSION "[0-9]+ tags")
endif() # !NO_SELFTEST
install(DIRECTORY "single_include/" DESTINATION "include/catch") install(DIRECTORY "single_include/" DESTINATION "include/catch")

View File

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

View File

@ -25,10 +25,24 @@
# # # #
# include(ParseAndAddCatchTests) # # include(ParseAndAddCatchTests) #
# ParseAndAddCatchTests(${PROJECT_NAME}) # # 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) 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 # This removes the contents between
# - block comments (i.e. /* ... */) # - block comments (i.e. /* ... */)
# - full line comments (i.e. // ... ) # - full line comments (i.e. // ... )
@ -47,9 +61,13 @@ endfunction()
# Worker function # Worker function
function(ParseFile SourceFile TestTarget) 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}) if(NOT EXISTS ${SourceFile})
message(WARNING "Cannot find source file: ${SourceFile}")
return() return()
endif() endif()
PrintDebugMessage("parsing ${SourceFile}")
file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME) file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME)
# Remove block and fullline comments # Remove block and fullline comments
@ -90,25 +108,42 @@ function(ParseFile SourceFile TestTarget)
else() else()
set(CTestName "${Name}") set(CTestName "${Name}")
endif() 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) if(TestStringsLength EQUAL 2)
list(GET TestStrings 1 Tags) list(GET TestStrings 1 Tags)
string(TOLOWER "${Tags}" 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}")
string(REPLACE "[" "" Tags "${Tags}") string(REPLACE "[" "" Tags "${Tags}")
endif() 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 the test and set its properties
add_test(NAME "\"${CTestName}\"" COMMAND ${TestTarget} ${Name} ${AdditionalCatchParameters}) add_test(NAME "\"${CTestName}\"" COMMAND ${TestTarget} ${Name} ${AdditionalCatchParameters})
set_tests_properties("\"${CTestName}\"" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran" set_tests_properties("\"${CTestName}\"" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran"
LABELS "${Tags}") LABELS "${Labels}")
endforeach() endforeach()
endfunction() endfunction()
# entry point # entry point
function(ParseAndAddCatchTests TestTarget) function(ParseAndAddCatchTests TestTarget)
PrintDebugMessage("Started parsing ${TestTarget}")
get_target_property(SourceFiles ${TestTarget} SOURCES) get_target_property(SourceFiles ${TestTarget} SOURCES)
PrintDebugMessage("Found the following sources: ${SourceFiles}")
foreach(SourceFile ${SourceFiles}) foreach(SourceFile ${SourceFiles})
ParseFile(${SourceFile} ${TestTarget}) ParseFile(${SourceFile} ${TestTarget})
endforeach() endforeach()
PrintDebugMessage("Finished parsing ${TestTarget}")
endfunction() endfunction()

View File

@ -17,7 +17,7 @@ Fine tuning:
Running: Running:
* [Command line](command-line.md) * [Command line](command-line.md)
* [Build systems](build-systems.md) * [CI and Build system integration](build-systems.md)
FAQ: FAQ:
* [Why are my tests slow to compile?](slow-compiles.md) * [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. 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`. 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 - Bloomberg
- NASA - 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 // 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 // Tear-down after a test case is run
} }
}; };

View File

@ -1,6 +1,32 @@
# Logging macros # 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 ## Streaming macros

View File

@ -1,6 +1,6 @@
# Open Source projects using Catch # 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? 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. 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 # 1.9.2
### Improvements and minor changes ### 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) 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 ### Special Tags

View File

@ -68,7 +68,7 @@ with expansion:
0 == 1 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: Let's change the factorial function to:

View File

@ -166,6 +166,32 @@ namespace Catch {
return returnCode; 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() { int run() {
if( m_configData.showHelp ) if( m_configData.showHelp )
return 0; return 0;

View File

@ -10,8 +10,14 @@
#ifndef __OBJC__ #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 // Standard C/C++ main entry point
int main (int argc, char * argv[]) { int main (int argc, char * argv[]) {
#endif
int result = Catch::Session().run( argc, argv ); int result = Catch::Session().run( argc, argv );
return ( result < 0xff ? result : 0xff ); return ( result < 0xff ? result : 0xff );
} }

View File

@ -142,8 +142,9 @@ namespace Catch {
m_totals.assertions.failed++; m_totals.assertions.failed++;
} }
if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) // We have no use for the return value (whether messages should be cleared), because messages were made scoped
m_messages.clear(); // and should be let to clear themselves out.
static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
// Reset working state // Reset working state
m_lastAssertionInfo = AssertionInfo( std::string(), m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); m_lastAssertionInfo = AssertionInfo( std::string(), m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );

View File

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

View File

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

@ -9,7 +9,6 @@
TEST_CASE( "Character pretty printing" ){ TEST_CASE( "Character pretty printing" ){
//
SECTION("Specifically escaped"){ SECTION("Specifically escaped"){
char tab = '\t'; char tab = '\t';
char newline = '\n'; char newline = '\n';

View File

@ -36,6 +36,13 @@ exeNameParser = re.compile(r'''
# This is a hack until something more reasonable is figured out # This is a hack until something more reasonable is figured out
specialCaseParser = re.compile(r'file\((\d+)\)') 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: if len(sys.argv) == 2:
cmdPath = sys.argv[1] cmdPath = sys.argv[1]
else: else:
@ -45,9 +52,9 @@ overallResult = 0
def diffFiles(fileA, fileB): def diffFiles(fileA, fileB):
with open(fileA, 'r') as file: with open(fileA, 'r') as file:
aLines = file.readlines() aLines = [line.rstrip() for line in file.readlines()]
with open(fileB, 'r') as file: with open(fileB, 'r') as file:
bLines = file.readlines() bLines = [line.rstrip() for line in file.readlines()]
shortenedFilenameA = fileA.rsplit(os.sep, 1)[-1] shortenedFilenameA = fileA.rsplit(os.sep, 1)[-1]
shortenedFilenameB = fileB.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 = durationsParser.sub(' time="{duration}"', line)
line = timestampsParser.sub(' timestamp="{iso8601-timestamp}"', line) line = timestampsParser.sub(' timestamp="{iso8601-timestamp}"', line)
line = specialCaseParser.sub('file:\g<1>', line) line = specialCaseParser.sub('file:\g<1>', line)
line = errnoParser.sub('errno', line)
return line return line
@ -119,7 +127,7 @@ def approve(baseName, args):
if os.path.exists(baselinesPath): if os.path.exists(baselinesPath):
diffResult = diffFiles(baselinesPath, filteredResultsPath) diffResult = diffFiles(baselinesPath, filteredResultsPath)
if diffResult: if diffResult:
print(''.join(diffResult)) print('\n'.join(diffResult))
print(" \n****************************\n \033[91mResults differed") print(" \n****************************\n \033[91mResults differed")
if len(diffResult) > overallResult: if len(diffResult) > overallResult:
overallResult = len(diffResult) 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 # 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 pull Microsoft master', shell=True)
subprocess.call('git push', shell=True)
# Create a new branch for the update # Create a new branch for the update
subprocess.call('git checkout -b catch-{}'.format(ver_string), shell=True) subprocess.call('git checkout -b catch-{}'.format(ver_string), shell=True)