From 062cd07342edc2b003555e90dd2cee0514b9f64a Mon Sep 17 00:00:00 2001 From: Bardur Arantsson Date: Mon, 23 Feb 2015 09:11:55 +0100 Subject: Add BanditCpp 1.1.4 test harness --- vendor/bandit/.gitignore | 8 + vendor/bandit/.travis.yml | 12 + vendor/bandit/.vimrc | 0 vendor/bandit/CMakeLists.txt | 47 + vendor/bandit/LICENSE.md | 21 + vendor/bandit/README.md | 63 + vendor/bandit/bandit/adapters/adapter.h | 12 + vendor/bandit/bandit/adapters/adapters.h | 16 + vendor/bandit/bandit/adapters/snowhouse.h | 22 + vendor/bandit/bandit/assertion_exception.h | 39 + .../assertion_frameworks/snowhouse/.gitignore | 6 + .../assertion_frameworks/snowhouse/CMakeLists.txt | 17 + .../assertion_frameworks/snowhouse/README.md | 338 +++ .../snowhouse/example/main.cpp | 21 + .../snowhouse/snowhouse/assert.h | 116 + .../snowhouse/snowhouse/assertionexception.h | 48 + .../snowhouse/snowhouse/assertmacro.h | 15 + .../snowhouse/snowhouse/constraints/constraints.h | 21 + .../snowhouse/constraints/containsconstraint.h | 80 + .../snowhouse/constraints/endswithconstraint.h | 53 + .../snowhouse/constraints/equalsconstraint.h | 74 + .../constraints/equalscontainerconstraint.h | 75 + .../constraints/equalswithdeltaconstraint.h | 51 + .../constraints/expressions/andexpression.h | 46 + .../snowhouse/constraints/expressions/expression.h | 38 + .../constraints/expressions/expression_fwd.h | 15 + .../constraints/expressions/notexpression.h | 44 + .../constraints/expressions/orexpression.h | 46 + .../snowhouse/constraints/fulfillsconstraint.h | 51 + .../snowhouse/constraints/haslengthconstraint.h | 60 + .../constraints/isgreaterthanconstraint.h | 55 + .../snowhouse/constraints/islessthanconstraint.h | 54 + .../snowhouse/constraints/startswithconstraint.h | 52 + .../snowhouse/snowhouse/exceptions.h | 81 + .../snowhouse/snowhouse/fluent/constraintadapter.h | 39 + .../snowhouse/snowhouse/fluent/constraintlist.h | 91 + .../snowhouse/snowhouse/fluent/expressionbuilder.h | 323 +++ .../snowhouse/snowhouse/fluent/fluent.h | 38 + .../snowhouse/fluent/operators/andoperator.h | 54 + .../fluent/operators/collections/alloperator.h | 35 + .../fluent/operators/collections/atleastoperator.h | 41 + .../fluent/operators/collections/atmostoperator.h | 39 + .../collections/collectionconstraintevaluator.h | 113 + .../operators/collections/collectionoperator.h | 24 + .../fluent/operators/collections/exactlyoperator.h | 39 + .../fluent/operators/collections/noneoperator.h | 33 + .../fluent/operators/constraintoperator.h | 79 + .../snowhouse/fluent/operators/notoperator.h | 53 + .../snowhouse/fluent/operators/oroperator.h | 55 + .../snowhouse/snowhouse/snowhouse.h | 24 + .../snowhouse/snowhouse/stringize.h | 90 + .../snowhouse/snowhouse/stringizers.h | 60 + vendor/bandit/bandit/bandit.h | 34 + vendor/bandit/bandit/context.h | 97 + vendor/bandit/bandit/external/optionparser.h | 2825 ++++++++++++++++++++ .../failure_formatters/default_failure_formatter.h | 30 + .../bandit/failure_formatters/failure_formatter.h | 13 + .../bandit/failure_formatters/failure_formatters.h | 16 + .../visual_studio_failure_formatter.h | 36 + vendor/bandit/bandit/grammar.h | 145 + vendor/bandit/bandit/listener.h | 27 + vendor/bandit/bandit/options.h | 105 + vendor/bandit/bandit/registration/registrar.h | 19 + vendor/bandit/bandit/registration/registration.h | 7 + vendor/bandit/bandit/registration/spec_registry.h | 17 + vendor/bandit/bandit/reporters/colorizer.h | 99 + vendor/bandit/bandit/reporters/dots_reporter.h | 69 + vendor/bandit/bandit/reporters/progress_reporter.h | 116 + vendor/bandit/bandit/reporters/reporters.h | 28 + .../bandit/bandit/reporters/single_line_reporter.h | 86 + vendor/bandit/bandit/reporters/spec_reporter.h | 126 + vendor/bandit/bandit/reporters/test_run_summary.h | 90 + vendor/bandit/bandit/reporters/xunit_reporter.h | 109 + .../bandit/bandit/run_policies/always_run_policy.h | 16 + .../bandit/bandit/run_policies/bandit_run_policy.h | 155 ++ .../bandit/bandit/run_policies/never_run_policy.h | 14 + vendor/bandit/bandit/run_policies/run_policies.h | 9 + vendor/bandit/bandit/run_policies/run_policy.h | 27 + vendor/bandit/bandit/runner.h | 98 + .../bandit/skip_policies/always_include_policy.h | 16 + .../bandit/skip_policies/always_skip_policy.h | 15 + .../skip_policies/name_contains_skip_policy.h | 28 + vendor/bandit/bandit/skip_policies/skip_policies.h | 9 + vendor/bandit/bandit/skip_policies/skip_policy.h | 29 + vendor/bandit/bandit/test_run_error.h | 12 + vendor/bandit/cross_compile.sh | 47 + .../bandit/specs/before_each_after_each.spec.cpp | 78 + vendor/bandit/specs/context.spec.cpp | 44 + vendor/bandit/specs/describe.spec.cpp | 108 + .../failure_formatters/default_formatter.spec.cpp | 21 + .../visual_studio_failure_formatter.spec.cpp | 22 + vendor/bandit/specs/fakes/fake_context.h | 54 + vendor/bandit/specs/fakes/fake_reporter.h | 78 + vendor/bandit/specs/fakes/fakes.h | 8 + vendor/bandit/specs/fakes/logging_fake.h | 32 + vendor/bandit/specs/fuzzbox.spec.cpp | 77 + vendor/bandit/specs/it.spec.cpp | 168 ++ vendor/bandit/specs/main.cpp | 6 + vendor/bandit/specs/options.spec.cpp | 109 + vendor/bandit/specs/reporters/colorizer.spec.cpp | 45 + .../bandit/specs/reporters/dots_reporter.spec.cpp | 202 ++ .../specs/reporters/single_line_reporter.spec.cpp | 201 ++ .../bandit/specs/reporters/xunit_reporter.spec.cpp | 161 ++ vendor/bandit/specs/run.spec.cpp | 77 + .../specs/run_policies/bandit_run_policy.spec.cpp | 226 ++ vendor/bandit/specs/specs.h | 10 + vendor/bandit/specs/synopsis.spec.cpp | 54 + vendor/bandit/specs/util/argv_helper.h | 62 + vendor/bandit/specs/util/util.h | 6 + 109 files changed, 9345 insertions(+) create mode 100644 vendor/bandit/.gitignore create mode 100644 vendor/bandit/.travis.yml create mode 100644 vendor/bandit/.vimrc create mode 100644 vendor/bandit/CMakeLists.txt create mode 100644 vendor/bandit/LICENSE.md create mode 100644 vendor/bandit/README.md create mode 100644 vendor/bandit/bandit/adapters/adapter.h create mode 100644 vendor/bandit/bandit/adapters/adapters.h create mode 100644 vendor/bandit/bandit/adapters/snowhouse.h create mode 100644 vendor/bandit/bandit/assertion_exception.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/.gitignore create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/CMakeLists.txt create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/README.md create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/example/main.cpp create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assert.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertionexception.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertmacro.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/constraints.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/containsconstraint.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/endswithconstraint.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalsconstraint.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalscontainerconstraint.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalswithdeltaconstraint.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/andexpression.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression_fwd.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/notexpression.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/orexpression.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/fulfillsconstraint.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/haslengthconstraint.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/isgreaterthanconstraint.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/islessthanconstraint.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/startswithconstraint.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/exceptions.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintadapter.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintlist.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/expressionbuilder.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/fluent.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/andoperator.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/alloperator.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atleastoperator.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atmostoperator.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionconstraintevaluator.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionoperator.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/exactlyoperator.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/noneoperator.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/constraintoperator.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/notoperator.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/oroperator.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/snowhouse.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringize.h create mode 100644 vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringizers.h create mode 100644 vendor/bandit/bandit/bandit.h create mode 100644 vendor/bandit/bandit/context.h create mode 100644 vendor/bandit/bandit/external/optionparser.h create mode 100644 vendor/bandit/bandit/failure_formatters/default_failure_formatter.h create mode 100644 vendor/bandit/bandit/failure_formatters/failure_formatter.h create mode 100644 vendor/bandit/bandit/failure_formatters/failure_formatters.h create mode 100644 vendor/bandit/bandit/failure_formatters/visual_studio_failure_formatter.h create mode 100644 vendor/bandit/bandit/grammar.h create mode 100644 vendor/bandit/bandit/listener.h create mode 100644 vendor/bandit/bandit/options.h create mode 100644 vendor/bandit/bandit/registration/registrar.h create mode 100644 vendor/bandit/bandit/registration/registration.h create mode 100644 vendor/bandit/bandit/registration/spec_registry.h create mode 100644 vendor/bandit/bandit/reporters/colorizer.h create mode 100644 vendor/bandit/bandit/reporters/dots_reporter.h create mode 100644 vendor/bandit/bandit/reporters/progress_reporter.h create mode 100644 vendor/bandit/bandit/reporters/reporters.h create mode 100644 vendor/bandit/bandit/reporters/single_line_reporter.h create mode 100644 vendor/bandit/bandit/reporters/spec_reporter.h create mode 100644 vendor/bandit/bandit/reporters/test_run_summary.h create mode 100644 vendor/bandit/bandit/reporters/xunit_reporter.h create mode 100644 vendor/bandit/bandit/run_policies/always_run_policy.h create mode 100644 vendor/bandit/bandit/run_policies/bandit_run_policy.h create mode 100644 vendor/bandit/bandit/run_policies/never_run_policy.h create mode 100644 vendor/bandit/bandit/run_policies/run_policies.h create mode 100644 vendor/bandit/bandit/run_policies/run_policy.h create mode 100644 vendor/bandit/bandit/runner.h create mode 100644 vendor/bandit/bandit/skip_policies/always_include_policy.h create mode 100644 vendor/bandit/bandit/skip_policies/always_skip_policy.h create mode 100644 vendor/bandit/bandit/skip_policies/name_contains_skip_policy.h create mode 100644 vendor/bandit/bandit/skip_policies/skip_policies.h create mode 100644 vendor/bandit/bandit/skip_policies/skip_policy.h create mode 100644 vendor/bandit/bandit/test_run_error.h create mode 100755 vendor/bandit/cross_compile.sh create mode 100644 vendor/bandit/specs/before_each_after_each.spec.cpp create mode 100644 vendor/bandit/specs/context.spec.cpp create mode 100644 vendor/bandit/specs/describe.spec.cpp create mode 100644 vendor/bandit/specs/failure_formatters/default_formatter.spec.cpp create mode 100644 vendor/bandit/specs/failure_formatters/visual_studio_failure_formatter.spec.cpp create mode 100644 vendor/bandit/specs/fakes/fake_context.h create mode 100644 vendor/bandit/specs/fakes/fake_reporter.h create mode 100644 vendor/bandit/specs/fakes/fakes.h create mode 100644 vendor/bandit/specs/fakes/logging_fake.h create mode 100644 vendor/bandit/specs/fuzzbox.spec.cpp create mode 100644 vendor/bandit/specs/it.spec.cpp create mode 100644 vendor/bandit/specs/main.cpp create mode 100644 vendor/bandit/specs/options.spec.cpp create mode 100644 vendor/bandit/specs/reporters/colorizer.spec.cpp create mode 100644 vendor/bandit/specs/reporters/dots_reporter.spec.cpp create mode 100644 vendor/bandit/specs/reporters/single_line_reporter.spec.cpp create mode 100644 vendor/bandit/specs/reporters/xunit_reporter.spec.cpp create mode 100644 vendor/bandit/specs/run.spec.cpp create mode 100644 vendor/bandit/specs/run_policies/bandit_run_policy.spec.cpp create mode 100644 vendor/bandit/specs/specs.h create mode 100644 vendor/bandit/specs/synopsis.spec.cpp create mode 100644 vendor/bandit/specs/util/argv_helper.h create mode 100644 vendor/bandit/specs/util/util.h (limited to 'vendor/bandit') diff --git a/vendor/bandit/.gitignore b/vendor/bandit/.gitignore new file mode 100644 index 00000000..9cbee0b8 --- /dev/null +++ b/vendor/bandit/.gitignore @@ -0,0 +1,8 @@ +CMakeCache.txt +CMakeFiles +Makefile +bin +*.cmake +builds/ +tags +cotire diff --git a/vendor/bandit/.travis.yml b/vendor/bandit/.travis.yml new file mode 100644 index 00000000..0e6f0d61 --- /dev/null +++ b/vendor/bandit/.travis.yml @@ -0,0 +1,12 @@ +language: cpp +compiler: + - gcc + - clang +before_script: + - BUILD_DIR=`pwd`/builds + - mkdir -p ${BUILD_DIR} + - cd ${BUILD_DIR} + - cmake .. +script: + - cd ${BUILD_DIR} + - make diff --git a/vendor/bandit/.vimrc b/vendor/bandit/.vimrc new file mode 100644 index 00000000..e69de29b diff --git a/vendor/bandit/CMakeLists.txt b/vendor/bandit/CMakeLists.txt new file mode 100644 index 00000000..49ff0565 --- /dev/null +++ b/vendor/bandit/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 2.8) +project(bandit) + +set (CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") +include(cotire) + +include_directories("${PROJECT_SOURCE_DIR}") + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ./bin) + +if (MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /MP ") +else() + # Assume GCC-style arguments + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wfatal-errors -Wall -W -Werror -Wfloat-equal -Wundef -Wendif-labels") + + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + endif() + endif() + + if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + endif() +endif() + +# +# If we're on Mac OS we assume we have libc++, otherwise we assume +# we don't need it. (TODO: make this check more sofisticated) +# +if (CMAKE_HOST_APPLE AND (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") +endif() + +FILE(GLOB BanditSpecSourceFiles specs/*.cpp specs/**/*.cpp) +add_executable(bandit-specs ${BanditSpecSourceFiles} ) +set_target_properties(bandit-specs PROPERTIES COTIRE_CXX_PREFIX_HEADER_INIT "specs/specs.h") +set_target_properties(bandit-specs PROPERTIES COTIRE_ADD_UNIT_BUILD FALSE) +cotire(bandit-specs) + +add_custom_command(TARGET bandit-specs + POST_BUILD + COMMAND bandit-specs --no-color --reporter=dots + WORKING_DIRECTORY ./bin) diff --git a/vendor/bandit/LICENSE.md b/vendor/bandit/LICENSE.md new file mode 100644 index 00000000..9d61348c --- /dev/null +++ b/vendor/bandit/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Joakim Karlsson + +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. diff --git a/vendor/bandit/README.md b/vendor/bandit/README.md new file mode 100644 index 00000000..0c95d420 --- /dev/null +++ b/vendor/bandit/README.md @@ -0,0 +1,63 @@ +bandit +====== +[![Build Status](https://travis-ci.org/joakimkarlsson/bandit.png)](https://travis-ci.org/joakimkarlsson/bandit) + +Human friendly unit testing for C++11 + +Bandit is a framework for C++11 that wants to make working with unit tests a pleasant +experience. + +For more information, go to [the bandit website](http://banditcpp.org). + +Bandit is released under the [MIT license](LICENSE.md) + +#An example + +This is a complete test application written in bandit: + +```cpp +#include +using namespace bandit; + +// Tell bandit there are tests here. +go_bandit([](){ + + // We're describing how a fuzzbox works. + describe("fuzzbox:", [](){ + guitar_ptr guitar; + fuzzbox_ptr fuzzbox; + + // Make sure each test has a fresh setup with + // a guitar with a fuzzbox connected to it. + before_each([&](){ + guitar = guitar_ptr(new struct guitar()); + fuzzbox = fuzzbox_ptr(new struct fuzzbox()); + guitar->add_effect(*fuzzbox); + }); + + it("starts in clean mode", [&](){ + AssertThat(guitar->sound(), Equals(sounds::clean)); + }); + + // Describe what happens when we turn on the fuzzbox. + describe("in distorted mode", [&](){ + + // Turn on the fuzzbox. + before_each([&](){ + fuzzbox->flip(); + }); + + it("sounds distorted", [&](){ + AssertThat(guitar->sound(), Equals(sounds::distorted)); + }); + }); + }); + +}); + +int main(int argc, char* argv[]) +{ + // Run the tests. + return bandit::run(argc, argv); +} +``` diff --git a/vendor/bandit/bandit/adapters/adapter.h b/vendor/bandit/bandit/adapters/adapter.h new file mode 100644 index 00000000..809212a1 --- /dev/null +++ b/vendor/bandit/bandit/adapters/adapter.h @@ -0,0 +1,12 @@ +#ifndef BANDIT_ADAPTER_H +#define BANDIT_ADAPTER_H + +namespace bandit { namespace adapters { + + struct assertion_adapter + { + virtual void adapt_exceptions(detail::voidfunc_t) = 0; + }; +}} + +#endif diff --git a/vendor/bandit/bandit/adapters/adapters.h b/vendor/bandit/bandit/adapters/adapters.h new file mode 100644 index 00000000..fbfddaea --- /dev/null +++ b/vendor/bandit/bandit/adapters/adapters.h @@ -0,0 +1,16 @@ +#ifndef BANDIT_ADAPTERS_H +#define BANDIT_ADAPTERS_H + +#include +#include + +namespace bandit { namespace detail { + + inline bandit::adapters::assertion_adapter& registered_adapter() + { + static bandit::adapters::snowhouse_adapter adapter; + return adapter; + } +}} + +#endif diff --git a/vendor/bandit/bandit/adapters/snowhouse.h b/vendor/bandit/bandit/adapters/snowhouse.h new file mode 100644 index 00000000..f0776662 --- /dev/null +++ b/vendor/bandit/bandit/adapters/snowhouse.h @@ -0,0 +1,22 @@ +#ifndef BANDIT_ADAPTERS_SNOWHOUSE_H +#define BANDIT_ADAPTERS_SNOWHOUSE_H + +namespace bandit { namespace adapters { + + struct snowhouse_adapter : public assertion_adapter + { + void adapt_exceptions(detail::voidfunc_t func) + { + try + { + func(); + } + catch(const snowhouse::AssertionException& ex) + { + throw bandit::detail::assertion_exception(ex.GetMessage(), ex.GetFilename(), ex.GetLineNumber()); + } + } + }; + +}} +#endif diff --git a/vendor/bandit/bandit/assertion_exception.h b/vendor/bandit/bandit/assertion_exception.h new file mode 100644 index 00000000..33d7474f --- /dev/null +++ b/vendor/bandit/bandit/assertion_exception.h @@ -0,0 +1,39 @@ +#ifndef BANDIT_ASSERTION_EXCEPTION_H +#define BANDIT_ASSERTION_EXCEPTION_H + +namespace bandit { namespace detail { + + struct assertion_exception : public std::runtime_error + { + assertion_exception(const std::string& message, + const std::string& file_name, const unsigned int line_number) + : std::runtime_error(message), file_name_(file_name), line_number_(line_number) + {} + + assertion_exception(const std::string& message) + : std::runtime_error(message), line_number_(0) + {} + + // + // To make gcc < 4.7 happy. + // + virtual ~assertion_exception() throw() + {} + + const std::string& file_name() const + { + return file_name_; + } + + unsigned int line_number() const + { + return line_number_; + } + + private: + std::string file_name_; + unsigned int line_number_; + }; +}} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/.gitignore b/vendor/bandit/bandit/assertion_frameworks/snowhouse/.gitignore new file mode 100644 index 00000000..bf3ce9c6 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/.gitignore @@ -0,0 +1,6 @@ +#cmake files +CMakeCache.txt +CMakeFiles/ +Makefile +bin/ +cmake_install.cmake diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/CMakeLists.txt b/vendor/bandit/bandit/assertion_frameworks/snowhouse/CMakeLists.txt new file mode 100644 index 00000000..edfabc0d --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 2.8) + +project(snowhouse) + +include_directories("${PROJECT_SOURCE_DIR}") + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ./bin) + +set(CMAKE_CXX_FLAGS "-Wfatal-errors -Wall -W -Werror -Wfloat-equal -Wundef -Wendif-labels -Wshadow -pedantic-errors") + +FILE(GLOB SnowhouseSpecSourceFiles example/*.cpp) +add_executable(snowhouse-tests ${SnowhouseSpecSourceFiles}) + +add_custom_command(TARGET snowhouse-tests + POST_BUILD + COMMAND snowhouse-tests + WORKING_DIRECTORY ./bin) diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/README.md b/vendor/bandit/bandit/assertion_frameworks/snowhouse/README.md new file mode 100644 index 00000000..67d849b6 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/README.md @@ -0,0 +1,338 @@ +snowhouse +========= + +An assertion library for C++ + +Snowhouse is a stand alone assertion framework for C++. It was originally +developed as part of [Igloo](http://github.com/joakimkarlsson/igloo) and has +been extracted to be usable in other contexts. + +## Usage + +```C++ +#include +using namespace snowhouse; + +int main() +{ + std::cout << "Testing that 23 is 23" << std::endl; + AssertThat(23, Is().EqualTo(23)); + + try + { + AssertThat(12, Is().LessThan(11).And().GreaterThan(99)); + } + catch(const AssertionException& ex) + { + std::cout << "Apparently this failed:" << std::endl; + std::cout << ex.GetMessage() << std::endl; + } + + return 0; +} +``` + +### Assertions + +Snowhouse uses a constraint based assertion model that is heavily inspired by the +model used in [NUnit](http://nunit.org/). An assertion in Snowhouse is written +using the following format: + +```cpp +AssertThat(actual_value, ); +``` + +where <constraint expression> is an expression that actual_value is evaluated against when the test is executed. + +Constraint expressions come in two basic forms: composite and fluent expressions + +#### Composite Expressions + +With composite expressions, you can create compact, powerful expressions that combine a set of predefined constraints with ones that you provide yourself. + +Example: + +```cpp +AssertThat(length, IsGreaterThan(4) && !Equals(10)); +``` + +Composite expressions can be any combination of constraints and the standard logical C++ operators. + +You can also add your own constraints to be used within composite expressions. + +####Fluent Expressions + +With fluent expressions, you can create assertions that better convey the intent of a test without exposing implementation-specific details. Fluent expressions aim to help you create tests that are not just by developers for developers, but rather can be read and understood by domain experts. + +Fluent expressions also has the ability to make assertions on the elements in a conteiner in a way you cannot achieve with composite expressions. + +Example: + +```cpp +AssertThat(length, Is().GreaterThan(4).And().Not().EqualTo(10)); +``` + +### Basic Constraints + +####Equality Constraint + +Used to verify equality between actual and expected. + +```cpp +AssertThat(x, Equals(12)); +AssertThat(x, Is().EqualTo(12)); +``` + +####EqualityWithDelta Constraint + +Used to verify equality between actual and expected, allowing the two to differ by a delta. + +```cpp +AssertThat(2.49, EqualsWithDelta(2.5, 0.1)); +AssertThat(2.49, Is().EqualToWithDelta(2.5, 0.1)); +``` + +####GreaterThan Constraint + +Used to verify that actual is greater than a value. + +```cpp +AssertThat(x, IsGreaterThan(4)); +AssertThat(x, Is().GreaterThan(4)); +``` + + +####LessThan Constraint + +Used to verify that actual is less than a value. + +```cpp +AssertThat(x, IsLessThan(3)); +AssertThat(x, Is().LessThan(3)); +``` + +### String Constraints + +String assertions in Snowhouse are used to verify the values of STL strings (std::string). + +####Equality Constraints + +Used to verify that actual is equal to an expected value. + +```cpp +Assert:That(actual_str, Equals("foo")); +AssertThat(actual_str, Is().EqualTo("foo")); +``` + +####Contains Constraint + +Used to verify that a string contains a substring. + +```cpp +AssertThat(actual_str, Contains("foo")); +AssertThat(actual_str, Is().Containing("foo")); +``` + +####EndsWith Constraint + +Used to verify that a string ends with an expected substring. + +```cpp +AssertThat(actual_str, EndsWith("foo")); +AssertThat(actual_str, Is().EndingWith("foo")); +``` + +####StartsWith Constraint + +Used to verify that a string starts with an expected substring. + +```cpp +AssertThat(actual_str, StartsWith("foo")); +AssertThat(actual_str, Is().StartingWith("foo")); +``` + +####HasLength Constraint + +Used to verify that a string is of the expected length. + +```cpp +AssertThat(actual_str, HasLength(5)); +AssertThat(actual_str, Is().OfLength(5)); +``` + +###Constraints on Multi Line Strings + +If you have a string that contains multiple lines, you can use the collection constraints to make assertions on the content of that string. This may be handy if you have a string that, for instance, represents the resulting content of a file or a network transmission. + +Snowhouse can handle both windows (CR+LF) and unix (LF) line endings + +```cpp +std::string lines = "First line\r\nSecond line\r\nThird line"; +AssertThat(lines, Has().Exactly(1).StartingWith("Second")); +``` + +###Container Constraints + +The following constraints can be applied to containers in the standard template library: + +####Contains Constraint + +Used to verify that a container contains an expected value. + +```cpp +AssertThat(container, Contains(12)); +AssertThat(container, Is().Containing(12)); +``` + +####HasLength Constraint + +Used to verify that a container has the expected length. + +```cpp +AssertThat(container, HasLength(3)); +AssertThat(container, Is().OfLength(3)); +``` + +####IsEmpty Constraint + +Used to verify that a container is empty. + +```cpp +AssertThat(contatiner, IsEmpty()); +AssertThat(container, Is().Empty()); +``` + +####All + +Used to verify that all elements of a STL sequence container matches an expectation. + +```cpp +AssertThat(container, Has().All().LessThan(5).Or().EqualTo(66)); +``` + +####AtLeast + +Used to verify that at least a specified amount of elements in a STL sequence container matches an expectation. + +```cpp +AssertThat(container, Has().AtLeast(3).StartingWith("foo")); +``` + +####AtMost + +Used to verify that at most a specified amount of elements in a STL sequence container matches an expectation. + +```cpp +Assert:That(container, Has().AtMost(2).Not().Containing("failed")); +``` + +####Exactly + +Used to verify that a STL sequence container has exactly a specified amount of elements that matches an expectation. + +```cpp +AssertThat(container, Has().Exactly(3).GreaterThan(10).And().LessThan(20)); +``` + +####EqualsContainer + +Used to verify that two STL sequence containers are equal. + +```cpp +AssertThat(container1, EqualsContainer(container2)); +AssertThat(container1, Is().EqualToContainer(container2)); +``` + +#####Predicate functions + +You can supply a predicate function or a functor to EqualsContainer to customize how to compare the elements in the two containers. + +With a predicate function: + +```cpp +static bool are_my_types_equal(const my_type& lhs, const my_type& rhs) +{ + return lhs.my_val_ == rhs.my_val_; +} + +AssertThat(container1, EqualsContainer(container2, are_my_types_equal)); +``` + +With a functor as predicate: + +```cpp +struct within_delta +{ + within_delta(int delta) : delta_(delta) {} + + bool operator()(const my_type& lhs, const my_type& rhs) const + { + return abs(lhs.my_val_ - rhs.my_val_) <= delta_; + } + +private: + int delta_; +}; + +AssertThat(container1, Is().EqualToContainer(container1, within_delta(1)); +``` + +###Exceptions + +Exception constraints can be used to verify that your code throws the correct exceptions. + +####AssertThrows + +AssertThrows succeeds if the exception thrown by the call is of the supplied type (or one of its subtypes). + +```cpp +AssertThrows(std::logic_error, myObject.a_method(42)); +``` + +####Making Assertions on the Thrown Exceptions + +If AssertThrows succeeds, it will store the thrown exception so that you can make more detailed assertions on it. + +```cpp +AssertThrows(std::logic_error, myObject.a_method(42)); +AssertThat(LastException().what(), Is().Containing("logic failure")); +``` + +The LastException<> is available in the scope of the call to AssertThrows. An exception is not available between specs in order to avoid the result of one spec contaminating another. + +###Custom Constraints + +You can add your own constraints to Snowhouse to create more expressive specifications. + +####Fulfills Constraints + +By defining the following matcher + +```cpp +struct IsEvenNumber +{ + bool Matches(const int actual) const + { + return (actual % 2) == 0; + } + + friend std::ostream& operator<<(std::ostream& stm, const IsEvenNumber& ); +}; + +std::ostream& operator<<(std::ostream& stm, const IsEvenNumber& ) +{ + stm << "An even number"; + return stm; +} +``` + +You can create the following constraints in Snowhouse: + +```cpp +AssertThat(42, Fulfills(IsEvenNumber())); +AssertThat(42, Is().Fulfilling(IsEvenNumber())); +``` + +Your custom matcher should implement a method called Matches() that takes a parameter of the type you expect and returns true if the passed parameter fulfills the constraint. + +To get more expressive failure messages, you should also implement the streaming operator as in the example above. diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/main.cpp b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/main.cpp new file mode 100644 index 00000000..4153ec7f --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/example/main.cpp @@ -0,0 +1,21 @@ +#include + +using namespace snowhouse; + +int main() +{ + std::cout << "Testing that 23 is 23" << std::endl; + Assert::That(23, Is().EqualTo(23)); + + try + { + AssertThat(12, Is().LessThan(11).And().GreaterThan(99)); + } + catch(const AssertionException& ex) + { + std::cout << "Apparently this failed:" << std::endl; + std::cout << ex.GetMessage() << std::endl; + } + + return 0; +} diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assert.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assert.h new file mode 100644 index 00000000..e25bf501 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assert.h @@ -0,0 +1,116 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_ASSERT_H +#define IGLOO_ASSERT_H + +#include "stringize.h" +#include "stringizers.h" + +namespace snowhouse { + + class Assert + { + public: + + template + static void That(const ActualType& actual, ExpressionBuilder expression) + { + const char* no_file = ""; + int line_number = 0; + + Assert::That(actual, expression, no_file, line_number); + } + + template + static void That(const ActualType& actual, ExpressionBuilder expression, const char* file_name, int line_number) + { + try + { + ResultStack result; + OperatorStack operators; + expression.Evaluate(result, operators, actual); + + while (!operators.empty()) + { + ConstraintOperator* op = operators.top(); + op->PerformOperation(result); + operators.pop(); + } + + if (result.empty()) + { + throw InvalidExpressionException("The expression did not yield any result"); + } + + if (!result.top()) + { + throw AssertionException(CreateErrorText(expression, actual), file_name, line_number); + } + } + catch (const InvalidExpressionException& e) + { + throw AssertionException("Malformed expression: \"" + snowhouse::Stringize(expression) + "\"\n" + e.Message()); + } + } + + template + static void That(const char* actual, ExpressionBuilder expression) + { + return That(std::string(actual), expression); + } + + template + static void That(const ActualType& actual, const ExpressionType& expression) + { + const char* no_file = ""; + int no_line = 0; + That(actual, expression, no_file, no_line); + } + + template + static void That(const ActualType& actual, const ExpressionType& expression, const char* file_name, int line_number) + { + if (!expression(actual)) + { + throw AssertionException(CreateErrorText(expression, actual), file_name, line_number); + } + } + + template + static void That(const char* actual, const ExpressionType& expression) + { + return That(std::string(actual), expression); + } + + static void That(bool actual) + { + if (!actual) + { + throw AssertionException("Expected: true\nActual: false"); + } + } + + static void Failure(const std::string& message) + { + throw AssertionException(message); + } + + private: + template + static std::string CreateErrorText(const ExpectedType& expected, const ActualType& actual) + { + std::ostringstream str; + + str << "Expected: " << snowhouse::Stringize(expected) << std::endl; + str << "Actual: " << snowhouse::Stringize(actual) << std::endl; + + return str.str(); + } + }; +} + +#endif // IGLOO_ASSERT_H diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertionexception.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertionexception.h new file mode 100644 index 00000000..77890a7e --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertionexception.h @@ -0,0 +1,48 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_ASSERTIONEXCEPTION_H +#define IGLOO_ASSERTIONEXCEPTION_H + +namespace snowhouse { + class AssertionException : public std::exception + { + public: + AssertionException(const std::string& message) + : m_message(message), m_fileName(""), m_line(0) + {} + + AssertionException(const std::string& message, const std::string& fileName, unsigned int line) + : m_message(message), m_fileName(fileName), m_line(line) + {} + + virtual ~AssertionException() throw() + { + } + + std::string GetMessage() const + { + return m_message; + } + + std::string GetFilename() const + { + return m_fileName; + } + + unsigned int GetLineNumber() const + { + return m_line; + } + + private: + std::string m_message; + std::string m_fileName; + unsigned int m_line; + }; +} + +#endif // IGLOO_ASSERTIONEXCEPTION_H diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertmacro.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertmacro.h new file mode 100644 index 00000000..6ebdfe1f --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/assertmacro.h @@ -0,0 +1,15 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_ASSERTMACRO_H +#define IGLOO_ASSERTMACRO_H + +#include "assert.h" + +#define AssertThat(p1,p2)\ + Assert::That((p1), (p2), __FILE__, __LINE__);\ + +#endif // IGLOO_ASSERTMACRO_H diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/constraints.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/constraints.h new file mode 100644 index 00000000..ea7a53d1 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/constraints.h @@ -0,0 +1,21 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_CONSTRAINTS_H +#define IGLOO_CONSTRAINTS_H + +#include "containsconstraint.h" +#include "endswithconstraint.h" +#include "equalsconstraint.h" +#include "haslengthconstraint.h" +#include "isgreaterthanconstraint.h" +#include "islessthanconstraint.h" +#include "startswithconstraint.h" +#include "fulfillsconstraint.h" +#include "equalswithdeltaconstraint.h" +#include "equalscontainerconstraint.h" + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/containsconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/containsconstraint.h new file mode 100644 index 00000000..f20d6800 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/containsconstraint.h @@ -0,0 +1,80 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_CONTAINSCONSTRAINT_H +#define IGLOO_CONTAINSCONSTRAINT_H + +#include + +#include "./expressions/expression.h" + +namespace snowhouse { + + template + struct find_in_container_traits + { + template + static bool find(const ContainerType& container, const ExpectedType& expected) + { + return std::find(container.begin(), container.end(), expected) != container.end(); + } + }; + + template + struct find_in_container_traits > + { + template + static bool find(const std::map& container, const ExpectedType& expected) + { + return container.find(expected) != container.end(); + } + }; + + template + struct ContainsConstraint : Expression< ContainsConstraint > + { + ContainsConstraint(const ExpectedType& expected) + : m_expected(expected) {} + + template + bool operator()(const ActualType& actual) const + { + return find_in_container_traits::find(actual, m_expected); + } + + bool operator()(const std::string& actual) const + { + return actual.find(m_expected) != actual.npos; + } + + ExpectedType m_expected; + }; + + template< typename ExpectedType > + inline ContainsConstraint Contains(const ExpectedType& expected) + { + return ContainsConstraint(expected); + } + + inline ContainsConstraint Contains(const char* expected) + { + return ContainsConstraint(expected); + } + + template< typename ExpectedType > + struct Stringizer< ContainsConstraint< ExpectedType > > + { + static std::string ToString(const ContainsConstraint& constraint) + { + std::ostringstream builder; + builder << "contains " << snowhouse::Stringize(constraint.m_expected); + + return builder.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/endswithconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/endswithconstraint.h new file mode 100644 index 00000000..c867e203 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/endswithconstraint.h @@ -0,0 +1,53 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_ENDSWITHCONSTRAINT_H +#define IGLOO_ENDSWITHCONSTRAINT_H + +#include "./expressions/expression.h" + +namespace snowhouse { + + template + struct EndsWithConstraint : Expression< EndsWithConstraint > + { + EndsWithConstraint(const ExpectedType& expected) + : m_expected(expected) {} + + bool operator()(const std::string& actual) const + { + size_t expectedPos = actual.length() - m_expected.length(); + return actual.find(m_expected) == expectedPos; + } + + ExpectedType m_expected; + }; + + template< typename ExpectedType > + inline EndsWithConstraint EndsWith(const ExpectedType& expected) + { + return EndsWithConstraint(expected); + } + + inline EndsWithConstraint EndsWith(const char* expected) + { + return EndsWithConstraint(expected); + } + + template< typename ExpectedType > + struct Stringizer< EndsWithConstraint< ExpectedType > > + { + static std::string ToString(const EndsWithConstraint& constraint) + { + std::ostringstream builder; + builder << "ends with " << snowhouse::Stringize(constraint.m_expected); + + return builder.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalsconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalsconstraint.h new file mode 100644 index 00000000..cbdc4054 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalsconstraint.h @@ -0,0 +1,74 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_EQUALSCONSTRAINT_H +#define IGLOO_EQUALSCONSTRAINT_H + +#include "./expressions/expression.h" + +namespace snowhouse { + + template< typename ExpectedType > + struct EqualsConstraint : Expression< EqualsConstraint > + { + EqualsConstraint(const ExpectedType& expected) + : m_expected(expected) + { + } + + template + bool operator()(const ActualType& actual) const + { + return (m_expected == actual); + } + + ExpectedType m_expected; + }; + + template< typename ExpectedType > + inline EqualsConstraint Equals(const ExpectedType& expected) + { + return EqualsConstraint(expected); + } + + inline EqualsConstraint Equals(const char* expected) + { + return EqualsConstraint(expected); + } + + inline EqualsConstraint IsFalse() + { + return EqualsConstraint(false); + } + + inline EqualsConstraint IsTrue() + { + return EqualsConstraint(true); + } + + template <> + struct Stringizer< EqualsConstraint< bool > > + { + static std::string ToString(const EqualsConstraint& constraint) + { + return constraint.m_expected ? "true" : "false"; + } + }; + + template< typename ExpectedType > + struct Stringizer< EqualsConstraint< ExpectedType > > + { + static std::string ToString(const EqualsConstraint& constraint) + { + std::ostringstream builder; + builder << "equal to " << snowhouse::Stringize(constraint.m_expected); + + return builder.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalscontainerconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalscontainerconstraint.h new file mode 100644 index 00000000..6bb5d797 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalscontainerconstraint.h @@ -0,0 +1,75 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_EQUALSCONTAINERCONSTRAINT_H +#define IGLOO_EQUALSCONTAINERCONSTRAINT_H + +namespace snowhouse { + + namespace constraint_internal { + template + inline bool default_comparer(const T& lhs, const T& rhs) + { + return lhs == rhs; + } + } + + template< typename ExpectedType, typename BinaryPredicate> + struct EqualsContainerConstraint : Expression< EqualsContainerConstraint > + { + EqualsContainerConstraint(const ExpectedType& expected, const BinaryPredicate predicate) + : expected_(expected), predicate_(predicate) + {} + + template + bool operator()(const ActualType& actual) const + { + typename ActualType::const_iterator actual_it; + typename ExpectedType::const_iterator expected_it; + + for(actual_it = actual.begin(), expected_it = expected_.begin(); actual_it != actual.end() && expected_it != expected_.end(); actual_it++, expected_it++) + { + if(!predicate_(*actual_it, *expected_it)) + { + return false; + } + } + + return actual.size() == expected_.size(); + } + + const ExpectedType expected_; + const BinaryPredicate predicate_; + + private: + EqualsContainerConstraint& operator=(const EqualsContainerConstraint&) { return *this; } + }; + + template< typename ExpectedType> + inline EqualsContainerConstraint EqualsContainer(const ExpectedType& expected) + { + return EqualsContainerConstraint(expected, constraint_internal::default_comparer); + } + + template< typename ExpectedType, typename BinaryPredicate > + inline EqualsContainerConstraint EqualsContainer(const ExpectedType& expected, const BinaryPredicate predicate) + { + return EqualsContainerConstraint(expected, predicate); + } + + template< typename ExpectedType, typename BinaryPredicate > + struct Stringizer< EqualsContainerConstraint > + { + static std::string ToString(const EqualsContainerConstraint& constraint) + { + std::ostringstream builder; + builder << snowhouse::Stringize(constraint.expected_); + return builder.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalswithdeltaconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalswithdeltaconstraint.h new file mode 100644 index 00000000..b54fb74e --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/equalswithdeltaconstraint.h @@ -0,0 +1,51 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_EQUALSWITHDELTACONSTRAINT_H +#define IGLOO_EQUALSWITHDELTACONSTRAINT_H + +#include "./expressions/expression.h" + +namespace snowhouse { + + template< typename ExpectedType, typename DeltaType > + struct EqualsWithDeltaConstraint : Expression< EqualsWithDeltaConstraint > + { + EqualsWithDeltaConstraint(const ExpectedType& expected, const DeltaType& delta) + : m_expected(expected), m_delta(delta) + { + } + + template + bool operator()(const ActualType& actual) const + { + return ((m_expected <= (actual + m_delta)) && (m_expected >= (actual - m_delta))); + } + + ExpectedType m_expected; + DeltaType m_delta; + }; + + template< typename ExpectedType, typename DeltaType > + inline EqualsWithDeltaConstraint EqualsWithDelta(const ExpectedType& expected, const DeltaType& delta) + { + return EqualsWithDeltaConstraint(expected, delta); + } + + template< typename ExpectedType, typename DeltaType > + struct Stringizer< EqualsWithDeltaConstraint< ExpectedType, DeltaType > > + { + static std::string ToString(const EqualsWithDeltaConstraint& constraint) + { + std::ostringstream builder; + builder << "equal to " << snowhouse::Stringize(constraint.m_expected) << " (+/- " << snowhouse::Stringize(constraint.m_delta) << ")"; + + return builder.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/andexpression.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/andexpression.h new file mode 100644 index 00000000..8b6b7d12 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/andexpression.h @@ -0,0 +1,46 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_ANDEXPRESSION_H +#define IGLOO_ANDEXPRESSION_H + +#include "./expression_fwd.h" + +namespace snowhouse { + + template< typename LeftExpression, typename RightExpression > + struct AndExpression : Expression< AndExpression > + { + AndExpression(const LeftExpression& left, const RightExpression& right) + : m_left(left) + , m_right(right) + { + } + + template< typename ActualType > + bool operator()(const ActualType& actual) const + { + return (m_left(actual) && m_right(actual)); + } + + LeftExpression m_left; + RightExpression m_right; + }; + + template< typename LeftExpression, typename RightExpression > + struct Stringizer< AndExpression > + { + static std::string ToString(const AndExpression& expression) + { + std::ostringstream builder; + builder << Stringize(expression.m_left) << " and " << Stringize(expression.m_right); + + return builder.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression.h new file mode 100644 index 00000000..fa894818 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression.h @@ -0,0 +1,38 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_EXPRESSION_H +#define IGLOO_EXPRESSION_H + +#include "./notexpression.h" +#include "./andexpression.h" +#include "./orexpression.h" + +namespace snowhouse { + + template + struct Expression + { + NotExpression operator!() const + { + return NotExpression(static_cast(*this)); + } + + template< typename Right > + AndExpression operator&&(const Right& right) const + { + return AndExpression(static_cast(*this), right); + } + + template< typename Right > + OrExpression operator||(const Right& right) const + { + return OrExpression(static_cast(*this), right); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression_fwd.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression_fwd.h new file mode 100644 index 00000000..c0e3706e --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/expression_fwd.h @@ -0,0 +1,15 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_EXPRESSION_FWD_H +#define IGLOO_EXPRESSION_FWD_H + +namespace snowhouse { + template + struct Expression; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/notexpression.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/notexpression.h new file mode 100644 index 00000000..4785f07b --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/notexpression.h @@ -0,0 +1,44 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_NOTEXPRESSION_H +#define IGLOO_NOTEXPRESSION_H + +#include "./expression_fwd.h" + +namespace snowhouse { + + template< typename ExpressionType > + struct NotExpression : Expression< NotExpression > + { + NotExpression(const ExpressionType& expression) + : m_expression(expression) + { + } + + template + bool operator()(const ActualType& actual) const + { + return !m_expression(actual); + } + + ExpressionType m_expression; + }; + + template< typename ExpressionType > + struct Stringizer< NotExpression > + { + static std::string ToString(const NotExpression& expression) + { + std::ostringstream builder; + builder << "not " << snowhouse::Stringize(expression.m_expression); + + return builder.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/orexpression.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/orexpression.h new file mode 100644 index 00000000..c1b6c12b --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/expressions/orexpression.h @@ -0,0 +1,46 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_OREXPRESSION_H +#define IGLOO_OREXPRESSION_H + +#include "./expression_fwd.h" + +namespace snowhouse { + + template< typename LeftExpression, typename RightExpression > + struct OrExpression : Expression< OrExpression > + { + OrExpression(const LeftExpression& left, const RightExpression& right) + : m_left(left) + , m_right(right) + { + } + + template< typename ActualType > + bool operator()(const ActualType& actual) const + { + return (m_left(actual) || m_right(actual)); + } + + LeftExpression m_left; + RightExpression m_right; + }; + + template< typename LeftExpression, typename RightExpression > + struct Stringizer< OrExpression > + { + static std::string ToString(const OrExpression& expression) + { + std::ostringstream builder; + builder << snowhouse::Stringize(expression.m_left) << " or " << snowhouse::Stringize(expression.m_right); + + return builder.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/fulfillsconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/fulfillsconstraint.h new file mode 100644 index 00000000..577056c1 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/fulfillsconstraint.h @@ -0,0 +1,51 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef FULFILLSCONSTRAINT_H +#define FULFILLSCONSTRAINT_H + +#include "./expressions/expression.h" + +namespace snowhouse { + + template< typename MatcherType > + struct FulfillsConstraint : Expression< FulfillsConstraint > + { + FulfillsConstraint(const MatcherType& matcher) + : m_matcher(matcher) + { + } + + template + bool operator()(const ActualType& actual) const + { + return m_matcher.Matches(actual); + } + + MatcherType m_matcher; + }; + + template< typename MatcherType > + inline FulfillsConstraint Fulfills(const MatcherType& matcher) + { + return FulfillsConstraint(matcher); + } + + template< typename MatcherType > + struct Stringizer< FulfillsConstraint< MatcherType > > + { + static std::string ToString(const FulfillsConstraint& constraint) + { + std::ostringstream builder; + builder << snowhouse::Stringize(constraint.m_matcher); + + return builder.str(); + } + }; + +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/haslengthconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/haslengthconstraint.h new file mode 100644 index 00000000..fb6e7cc9 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/haslengthconstraint.h @@ -0,0 +1,60 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_HASLENGTHCONSTRAINT_H +#define IGLOO_HASLENGTHCONSTRAINT_H + +#include "./expressions/expression.h" + +namespace snowhouse { + + template + struct HasLengthConstraint : Expression< HasLengthConstraint > + { + HasLengthConstraint(const ExpectedType& expected) + : m_expected(expected) {} + + template + bool operator()(const ActualType& actual) const + { + typedef typename ActualType::size_type SizeType; + SizeType expectedSize = static_cast(m_expected); + return (actual.size() == expectedSize); + } + + ExpectedType m_expected; + }; + + template< typename ExpectedType > + inline HasLengthConstraint HasLength(const ExpectedType& expected) + { + return HasLengthConstraint(expected); + } + + inline HasLengthConstraint IsEmpty() + { + return HasLength(0); + } + + inline HasLengthConstraint HasLength(const char* expected) + { + return HasLengthConstraint(expected); + } + + template< typename ExpectedType > + struct Stringizer< HasLengthConstraint< ExpectedType > > + { + static std::string ToString(const HasLengthConstraint& constraint) + { + std::ostringstream builder; + builder << "of length " << snowhouse::Stringize(constraint.m_expected); + + return builder.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/isgreaterthanconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/isgreaterthanconstraint.h new file mode 100644 index 00000000..e7ef01f8 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/isgreaterthanconstraint.h @@ -0,0 +1,55 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_ISGREATERTHANCONSTRAINT_H +#define IGLOO_ISGREATERTHANCONSTRAINT_H + +#include "./expressions/expression.h" + +namespace snowhouse { + + template< typename ExpectedType > + struct IsGreaterThanConstraint : Expression< IsGreaterThanConstraint > + { + IsGreaterThanConstraint(const ExpectedType& expected) + : m_expected(expected) + { + } + + template + bool operator()(const ActualType& actual) const + { + return (actual > m_expected); + } + + ExpectedType m_expected; + }; + + template< typename ExpectedType > + inline IsGreaterThanConstraint IsGreaterThan(const ExpectedType& expected) + { + return IsGreaterThanConstraint(expected); + } + + inline IsGreaterThanConstraint IsGreaterThan(const char* expected) + { + return IsGreaterThanConstraint(expected); + } + + template< typename ExpectedType > + struct Stringizer< IsGreaterThanConstraint< ExpectedType > > + { + static std::string ToString(const IsGreaterThanConstraint& constraint) + { + std::ostringstream builder; + builder << "greater than " << snowhouse::Stringize(constraint.m_expected); + + return builder.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/islessthanconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/islessthanconstraint.h new file mode 100644 index 00000000..7379dcf7 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/islessthanconstraint.h @@ -0,0 +1,54 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_ISLESSTHANCONSTRAINT_H +#define IGLOO_ISLESSTHANCONSTRAINT_H + +#include "./expressions/expression.h" + +namespace snowhouse { + + template< typename ExpectedType > + struct IsLessThanConstraint : Expression< IsLessThanConstraint > + { + IsLessThanConstraint(const ExpectedType& expected) + : m_expected(expected) + { + } + + template + bool operator()(const ActualType& actual) const + { + return (actual < m_expected); + } + + ExpectedType m_expected; + }; + + template< typename ExpectedType > + inline IsLessThanConstraint IsLessThan(const ExpectedType& expected) + { + return IsLessThanConstraint(expected); + } + + inline IsLessThanConstraint IsLessThan(const char* expected) + { + return IsLessThanConstraint(expected); + } + + template< typename ExpectedType > + struct Stringizer< IsLessThanConstraint< ExpectedType > > + { + static std::string ToString(const IsLessThanConstraint& constraint) + { + std::ostringstream builder; + builder << "less than " << snowhouse::Stringize(constraint.m_expected); + + return builder.str(); + } + }; +} +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/startswithconstraint.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/startswithconstraint.h new file mode 100644 index 00000000..ffdd1696 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/constraints/startswithconstraint.h @@ -0,0 +1,52 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_STARTSWITHCONSTRAINT_H +#define IGLOO_STARTSWITHCONSTRAINT_H + +#include "./expressions/expression.h" + +namespace snowhouse { + + template + struct StartsWithConstraint : Expression< StartsWithConstraint > + { + StartsWithConstraint(const ExpectedType& expected) + : m_expected(expected) {} + + bool operator()(const std::string& actual) const + { + return actual.find(m_expected) == 0; + } + + ExpectedType m_expected; + }; + + template< typename ExpectedType > + inline StartsWithConstraint StartsWith(const ExpectedType& expected) + { + return StartsWithConstraint(expected); + } + + inline StartsWithConstraint StartsWith(const char* expected) + { + return StartsWithConstraint(expected); + } + + template< typename ExpectedType > + struct Stringizer< StartsWithConstraint< ExpectedType > > + { + static std::string ToString(const StartsWithConstraint& constraint) + { + std::ostringstream builder; + builder << "starts with " << snowhouse::Stringize(constraint.m_expected); + + return builder.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/exceptions.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/exceptions.h new file mode 100644 index 00000000..03b879ef --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/exceptions.h @@ -0,0 +1,81 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_EXCEPTIONS_H +#define IGLOO_EXCEPTIONS_H + +#include "assert.h" + +namespace snowhouse { + + template + class ExceptionStorage + { + public: + static std::auto_ptr& last_exception() + { + static std::auto_ptr last; + return last; + } + + void compiler_thinks_i_am_unused() {} + + ~ExceptionStorage() + { + last_exception().reset(NULL); + } + }; + + template + inline ExceptionType& LastException() + { + if(ExceptionStorage::last_exception().get() == NULL) + { + Assert::Failure("No exception was stored"); + } + + return *(ExceptionStorage::last_exception().get()); + } +} + +#define IGLOO_CONCAT2(a, b) a##b +#define IGLOO_CONCAT(a, b) IGLOO_CONCAT2(a, b) + +#define AssertThrows(EXCEPTION_TYPE, METHOD) \ +ExceptionStorage IGLOO_CONCAT(IGLOO_storage_, __LINE__); IGLOO_CONCAT(IGLOO_storage_, __LINE__).compiler_thinks_i_am_unused(); \ +{ \ + bool wrong_exception = false; \ + bool no_exception = false; \ + try \ + { \ + METHOD; \ + no_exception = true; \ + } \ + catch (const EXCEPTION_TYPE& e) \ + { \ + ExceptionStorage::last_exception() = std::auto_ptr(new EXCEPTION_TYPE(e)); \ + } \ + catch(...) \ + { \ + wrong_exception = true; \ + } \ + if(no_exception) \ + { \ + std::ostringstream stm; \ + stm << "Expected " << #EXCEPTION_TYPE << ". No exception was thrown."; \ + Assert::Failure(stm.str()); \ + } \ + if(wrong_exception) \ + { \ + std::ostringstream stm; \ + stm << "Expected " << #EXCEPTION_TYPE << ". Wrong exception was thrown."; \ + Assert::Failure(stm.str()); \ + } \ +} + +#endif + + diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintadapter.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintadapter.h new file mode 100644 index 00000000..b1719288 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintadapter.h @@ -0,0 +1,39 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_CONSTRAINTADAPTER_H +#define IGLOO_CONSTRAINTADAPTER_H + +namespace snowhouse { + + template + struct ConstraintAdapter + { + ConstraintAdapter(const ConstraintType& constraint) : m_constraint(constraint) + { + } + + template + void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual) + { + result.push(m_constraint(actual)); + EvaluateConstraintList(list.m_tail, result, operators, actual); + } + + ConstraintType m_constraint; + }; + + template + struct Stringizer< ConstraintAdapter > + { + static std::string ToString(const ConstraintAdapter& constraintAdapter) + { + return snowhouse::Stringize(constraintAdapter.m_constraint); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintlist.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintlist.h new file mode 100644 index 00000000..1a62a60e --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/constraintlist.h @@ -0,0 +1,91 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_CONSTRAINT_H +#define IGLOO_CONSTRAINT_H + +namespace snowhouse { + + struct ConstraintOperator; + typedef std::stack ResultStack; + typedef std::stack OperatorStack; + + template + struct ConstraintList + { + typedef HT HeadType; + typedef TT TailType; + + ConstraintList(const HeadType& head, const TailType& tail) + : m_head(head), m_tail(tail) + { + } + + HeadType m_head; + TailType m_tail; + }; + + struct Nil + { + Nil() {} + Nil(const Nil&) {} + }; + + + // ---- These structs defines the resulting types of list concatenation operations + template + struct type_concat + { + typedef ConstraintList::t> t; + }; + + template struct type_concat { typedef L2 t; }; + + template inline L3 tr_concat(const Nil&, const Nil&) { return Nil(); } + + + // ---- These structs define the concatenation operations. + + template + struct ListConcat + { + static ResultList Concatenate(const LeftList& left, const RightList& right) + { + return ResultList(left.m_head, ListConcat::t>::Concatenate(left.m_tail, right)); + } + }; + + // Concatenating an empty list with a second list yields the second list + template + struct ListConcat + { + static ResultList Concatenate(const Nil&, const RightList& right) + { + return right; + } + + }; + + // Concatenating two empty lists yields an empty list + template + struct ListConcat + { + static ResultList Concatenate(const Nil&, const Nil&) + { + return Nil(); + } + }; + + // ---- The concatenation operation + + template + inline typename type_concat::t Concatenate(const L1& list1, const L2& list2) + { + return ListConcat::t>::Concatenate(list1, list2); + } +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/expressionbuilder.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/expressionbuilder.h new file mode 100644 index 00000000..20bf1358 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/expressionbuilder.h @@ -0,0 +1,323 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_EXPRESSIONBUILDER_H +#define IGLOO_EXPRESSIONBUILDER_H + +namespace snowhouse { + + // ---- Evaluation of list of constraints + + template + inline void EvaluateConstraintList(ConstraintListType& constraint_list, ResultStack& result, OperatorStack& operators, const ActualType& actual) + { + constraint_list.m_head.Evaluate(constraint_list, result, operators, actual); + } + + template + inline void EvaluateConstraintList(Nil&, ResultStack&, OperatorStack&, const ActualType&) {} + + + template + struct ExpressionBuilder + { + ExpressionBuilder(const ConstraintListType& list) : m_constraint_list(list) + { + } + + template + ExpressionBuilder >, Nil> >::t> + EqualTo(const ExpectedType& expected) + { + typedef ConstraintAdapter > ConstraintAdapterType; + typedef ExpressionBuilder< typename type_concat >::t > BuilderType; + + ConstraintAdapterType constraint(expected); + ConstraintList node(constraint, Nil()); + + return BuilderType(Concatenate(m_constraint_list, node)); + } + + template + ExpressionBuilder >, Nil> >::t> + EqualToWithDelta(const ExpectedType& expected, const DeltaType& delta) + { + typedef ConstraintAdapter > ConstraintAdapterType; + typedef ExpressionBuilder< typename type_concat >::t > BuilderType; + + ConstraintAdapterType constraint(EqualsWithDeltaConstraint(expected, delta)); + ConstraintList node(constraint, Nil()); + + return BuilderType(Concatenate(m_constraint_list, node)); + } + + template + ExpressionBuilder >, Nil> >::t> + Fulfilling(const MatcherType& matcher) + { + typedef ConstraintAdapter > ConstraintAdapterType; + typedef ExpressionBuilder< typename type_concat >::t > BuilderType; + + ConstraintAdapterType constraint(matcher); + ConstraintList node(constraint, Nil()); + + return BuilderType(Concatenate(m_constraint_list, node)); + } + + ExpressionBuilder >, Nil> >::t> + False() + { + return EqualTo(false); + } + + ExpressionBuilder >, Nil> >::t> + True() + { + return EqualTo(true); + } + + ExpressionBuilder >, Nil> >::t> + EqualTo(const char* expected) + { + return EqualTo(std::string(expected)); + } + + template + ExpressionBuilder >, Nil> >::t> + GreaterThan(const ExpectedType& expected) + { + typedef ConstraintAdapter > ConstraintAdapterType; + + typedef ExpressionBuilder< typename type_concat >::t > BuilderType; + ConstraintAdapterType constraint(expected); + ConstraintList node(constraint, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + template + ExpressionBuilder >, Nil> >::t> + LessThan(const ExpectedType& expected) + { + typedef ConstraintAdapter > ConstraintAdapterType; + + typedef ExpressionBuilder< typename type_concat >::t > BuilderType; + ConstraintAdapterType constraint(expected); + ConstraintList node(constraint, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + template + ExpressionBuilder >, Nil> >::t> + Containing(const ExpectedType& expected) + { + typedef ConstraintAdapter > ConstraintAdapterType; + + typedef ExpressionBuilder< typename type_concat >::t > BuilderType; + ConstraintAdapterType constraint(expected); + ConstraintList node(constraint, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + ExpressionBuilder >, Nil> >::t> + Containing(const char* expected) + { + return Containing(std::string(expected)); + } + + template + ExpressionBuilder >, Nil> >::t> + EndingWith(const ExpectedType& expected) + { + typedef ConstraintAdapter > ConstraintAdapterType; + typedef ExpressionBuilder< typename type_concat >::t > BuilderType; + + ConstraintAdapterType constraint(expected); + ConstraintList node(constraint, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + ExpressionBuilder >, Nil> >::t> + EndingWith(const char* expected) + { + return EndingWith(std::string(expected)); + } + + template + ExpressionBuilder >, Nil> >::t> + StartingWith(const ExpectedType& expected) + { + typedef ConstraintAdapter > ConstraintAdapterType; + + typedef ExpressionBuilder< typename type_concat >::t > BuilderType; + ConstraintAdapterType constraint(expected); + ConstraintList node(constraint, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + ExpressionBuilder >, Nil> >::t> + StartingWith(const char* expected) + { + return StartingWith(std::string(expected)); + } + + template + ExpressionBuilder >, Nil> >::t> + OfLength(const ExpectedType& expected) + { + typedef ConstraintAdapter > ConstraintAdapterType; + + typedef ExpressionBuilder< typename type_concat >::t > BuilderType; + ConstraintAdapterType constraint(expected); + ConstraintList node(constraint, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + ExpressionBuilder >, Nil> >::t> + Empty() + { + typedef ConstraintAdapter > ConstraintAdapterType; + + typedef ExpressionBuilder< typename type_concat >::t > BuilderType; + ConstraintAdapterType constraint(0); + ConstraintList node(constraint, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + template + ExpressionBuilder >, Nil> >::t> + EqualToContainer(const ExpectedType& expected) + { + typedef bool (*DefaultBinaryPredivateType)(const typename ExpectedType::value_type&, const typename ExpectedType::value_type&); + typedef ConstraintAdapter > ConstraintAdapterType; + + typedef ExpressionBuilder< typename type_concat >::t > BuilderType; + ConstraintAdapterType constraint(EqualsContainerConstraint(expected, constraint_internal::default_comparer)); + ConstraintList node(constraint, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + template + ExpressionBuilder >, Nil> >::t> + EqualToContainer(const ExpectedType& expected, const BinaryPredicate predicate) + { + typedef ConstraintAdapter > ConstraintAdapterType; + + typedef ExpressionBuilder< typename type_concat >::t > BuilderType; + ConstraintAdapterType constraint(EqualsContainerConstraint(expected, predicate)); + ConstraintList node(constraint, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + typedef ConstraintList AndOperatorNode; + typedef ConstraintList OrOperatorNode; + typedef ConstraintList NotOperatorNode; + typedef ConstraintList AllOperatorNode; + typedef ConstraintList AtLeastOperatorNode; + typedef ConstraintList ExactlyOperatorNode; + typedef ConstraintList AtMostOperatorNode; + typedef ConstraintList NoneOperatorNode; + + ExpressionBuilder::t> All() + { + typedef ExpressionBuilder::t> BuilderType; + AllOperator op; + AllOperatorNode node(op, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + ExpressionBuilder::t> AtLeast(unsigned int expected) + { + typedef ExpressionBuilder::t> BuilderType; + AtLeastOperator op(expected); + AtLeastOperatorNode node(op, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + ExpressionBuilder::t> Exactly(unsigned int expected) + { + typedef ExpressionBuilder::t> BuilderType; + ExactlyOperator op(expected); + ExactlyOperatorNode node(op, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + ExpressionBuilder::t> AtMost(unsigned int expected) + { + typedef ExpressionBuilder::t> BuilderType; + AtMostOperator op(expected); + AtMostOperatorNode node(op, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + ExpressionBuilder::t> None() + { + typedef ExpressionBuilder::t> BuilderType; + NoneOperator op; + NoneOperatorNode node(op, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + ExpressionBuilder::t> And() + { + typedef ExpressionBuilder::t> BuilderType; + AndOperator op; + AndOperatorNode node(op, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + ExpressionBuilder::t> Or() + { + typedef ExpressionBuilder::t> BuilderType; + OrOperator op; + OrOperatorNode node(op, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + ExpressionBuilder::t> Not() + { + typedef ExpressionBuilder::t> BuilderType; + NotOperator op; + NotOperatorNode node(op, Nil()); + return BuilderType(Concatenate(m_constraint_list, node)); + } + + template + void Evaluate(ResultStack& result, OperatorStack& operators, const ActualType& actual) + { + EvaluateConstraintList(m_constraint_list, result, operators, actual); + } + + ConstraintListType m_constraint_list; + }; + + template + inline void StringizeConstraintList(const T& list, std::ostringstream& stm) + { + if (stm.tellp() > 0) + stm << " "; + + stm << snowhouse::Stringize(list.m_head); + StringizeConstraintList(list.m_tail, stm); + } + + inline void StringizeConstraintList(const Nil&, std::ostringstream&) + { + } + + template + struct Stringizer< ExpressionBuilder > + { + static std::string ToString(const ExpressionBuilder& builder) + { + std::ostringstream stm; + StringizeConstraintList(builder.m_constraint_list, stm); + + return stm.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/fluent.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/fluent.h new file mode 100644 index 00000000..6bbd4b81 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/fluent.h @@ -0,0 +1,38 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_FLUENT_H +#define IGLOO_FLUENT_H + +#include "constraintlist.h" +#include "constraintadapter.h" +#include "operators/constraintoperator.h" +#include "operators/andoperator.h" +#include "operators/oroperator.h" +#include "operators/collections/collectionconstraintevaluator.h" +#include "operators/collections/alloperator.h" +#include "operators/collections/noneoperator.h" +#include "operators/collections/atleastoperator.h" +#include "operators/collections/exactlyoperator.h" +#include "operators/collections/atmostoperator.h" +#include "operators/notoperator.h" +#include "expressionbuilder.h" + +namespace snowhouse { + + inline ExpressionBuilder Is() + { + return ExpressionBuilder(Nil()); + } + + inline ExpressionBuilder Has() + { + return ExpressionBuilder(Nil()); + } + +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/andoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/andoperator.h new file mode 100644 index 00000000..39670632 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/andoperator.h @@ -0,0 +1,54 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_ANDOPERATOR_H +#define IGLOO_ANDOPERATOR_H + +namespace snowhouse { + + struct AndOperator : public ConstraintOperator + { + template + void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual) + { + EvaluateOperatorsWithLessOrEqualPrecedence(*this, operators, result); + + operators.push(this); + + EvaluateConstraintList(list.m_tail, result, operators, actual); + } + + void PerformOperation(ResultStack& result) + { + if(result.size() < 2) + { + throw InvalidExpressionException("The expression contains an and operator with too few operands"); + } + + bool right = result.top(); + result.pop(); + bool left = result.top(); + result.pop(); + + result.push(left && right); + } + + int Precedence() const + { + return 3; + } + }; + + template<> + struct Stringizer + { + static std::string ToString(const AndOperator&) + { + return "and"; + } + }; +} +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/alloperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/alloperator.h new file mode 100644 index 00000000..4157faf9 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/alloperator.h @@ -0,0 +1,35 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_ALLOPERATOR_H +#define IGLOO_ALLOPERATOR_H + +#include "collectionoperator.h" + +namespace snowhouse { + + struct AllOperator : public CollectionOperator + { + template + void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual) + { + unsigned int passed_elements = CollectionConstraintEvaluator::Evaluate(*this, list, result, operators, actual); + + result.push(passed_elements == actual.size()); + } + }; + + template<> + struct Stringizer + { + static std::string ToString(const AllOperator&) + { + return "all"; + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atleastoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atleastoperator.h new file mode 100644 index 00000000..d46e697f --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atleastoperator.h @@ -0,0 +1,41 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_ATLEASTOPERATOR_H +#define IGLOO_ATLEASTOPERATOR_H + +#include "collectionoperator.h" + +namespace snowhouse { + + struct AtLeastOperator : public CollectionOperator + { + AtLeastOperator(unsigned int expected) : m_expected(expected) {} + + template + void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual) + { + unsigned int passed_elements = CollectionConstraintEvaluator::Evaluate(*this, list, result, operators, actual); + + result.push(passed_elements >= m_expected); + } + + unsigned int m_expected; + }; + + template<> + struct Stringizer + { + static std::string ToString(const AtLeastOperator& op) + { + std::ostringstream stm; + stm << "at least " << op.m_expected; + return stm.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atmostoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atmostoperator.h new file mode 100644 index 00000000..53debbc8 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/atmostoperator.h @@ -0,0 +1,39 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_ATMOSTOPERATOR_H +#define IGLOO_ATMOSTOPERATOR_H + +namespace snowhouse { + + struct AtMostOperator : public CollectionOperator + { + AtMostOperator(unsigned int expected) : m_expected(expected) {} + + template + void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual) + { + unsigned int passed_elements = CollectionConstraintEvaluator::Evaluate(*this, list, result, operators, actual); + + result.push(passed_elements <= m_expected); + } + + unsigned int m_expected; + }; + + template<> + struct Stringizer + { + static std::string ToString(const AtMostOperator& op) + { + std::ostringstream stm; + stm << "at most " << op.m_expected; + return stm.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionconstraintevaluator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionconstraintevaluator.h new file mode 100644 index 00000000..cc9a3aef --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionconstraintevaluator.h @@ -0,0 +1,113 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_COLLECTIONCONSTRAINTEVALUATOR_H +#define IGLOO_COLLECTIONCONSTRAINTEVALUATOR_H + +#include +#include "../../../assertionexception.h" + +namespace snowhouse +{ + +template +struct CollectionConstraintEvaluator +{ + static unsigned int Evaluate(const ConstraintOperator& op, + ConstraintListType& expression, ResultStack& result, + OperatorStack& operators, const ActualType& actual) + { + ConstraintOperator::EvaluateOperatorsWithLessOrEqualPrecedence(op, + operators, result); + + unsigned int passed_elements = 0; + typename ActualType::const_iterator it; + for(it = actual.begin(); it != actual.end(); it++) + { + if(ConstraintOperator::EvaluateElementAgainstRestOfExpression(expression, + *it)) + { + passed_elements++; + } + } + + return passed_elements; + } +}; + +struct StringLineParser +{ + static void Parse(const std::string& str, std::vector& res) + { + size_t start = 0; + size_t newline = FindNewline(str, start); + + while(newline != std::string::npos) + { + StoreLine(str, start, newline, res); + start = MoveToNextLine(str, newline); + newline = FindNewline(str, start); + } + + if(start < str.size()) + { + StoreLine(str, start, std::string::npos, res); + } + } + +private: + static size_t FindNewline(const std::string& str, size_t start) + { + return str.find_first_of("\r\n", start); + } + + static void StoreLine(const std::string& str, size_t start, size_t end, + std::vector& res) + { + std::string line = str.substr(start, end - start); + res.push_back(line); + } + + static size_t MoveToNextLine(const std::string& str, size_t newline) + { + if(str.find("\r\n", newline) == newline) + { + return newline + 2; + } + + if(str.find("\n", newline) == newline) + { + return newline + 1; + } + + if(str.find("\r", newline) == newline) + { + return newline + 1; + } + + std::ostringstream stm; + stm << "This string seems to contain an invalid line ending at position " + << newline << ":\n" << str << std::endl; + throw AssertionException(stm.str()); + } +}; + +template +struct CollectionConstraintEvaluator +{ + static unsigned int Evaluate(const ConstraintOperator& op, + ConstraintListType& expression, ResultStack& result, + OperatorStack& operators, const std::string& actual) + { + std::vector lines; + StringLineParser::Parse(actual, lines); + return CollectionConstraintEvaluator >::Evaluate(op, expression, result, operators, lines); + } +}; + +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionoperator.h new file mode 100644 index 00000000..c1bb8bec --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/collectionoperator.h @@ -0,0 +1,24 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_COLLECTIONOPERATOR_H +#define IGLOO_COLLECTIONOPERATOR_H + +namespace snowhouse { + struct CollectionOperator : public ConstraintOperator + { + void PerformOperation(ResultStack&) + { + } + + int Precedence() const + { + return 1; + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/exactlyoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/exactlyoperator.h new file mode 100644 index 00000000..81c2dcaa --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/exactlyoperator.h @@ -0,0 +1,39 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_EXACTLYOPERATOR_H +#define IGLOO_EXACTLYOPERATOR_H + +namespace snowhouse { + + struct ExactlyOperator : public CollectionOperator + { + ExactlyOperator(unsigned int expected) : m_expected(expected) {} + + template + void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual) + { + unsigned int passed_elements = CollectionConstraintEvaluator::Evaluate(*this, list, result, operators, actual); + + result.push(passed_elements == m_expected); + } + + unsigned int m_expected; + }; + + template<> + struct Stringizer< ExactlyOperator > + { + static std::string ToString(const ExactlyOperator& op) + { + std::ostringstream stm; + stm << "exactly " << op.m_expected; + return stm.str(); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/noneoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/noneoperator.h new file mode 100644 index 00000000..c4570915 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/collections/noneoperator.h @@ -0,0 +1,33 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_NONEOPERATOR_H +#define IGLOO_NONEOPERATOR_H + +namespace snowhouse { + + struct NoneOperator : public CollectionOperator + { + template + void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual) + { + unsigned int passed_elements = CollectionConstraintEvaluator::Evaluate(*this, list, result, operators, actual); + result.push(passed_elements == 0); + } + }; + + template<> + struct Stringizer + { + static std::string ToString(const NoneOperator&) + { + return "none"; + } + }; + +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/constraintoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/constraintoperator.h new file mode 100644 index 00000000..31c19b50 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/constraintoperator.h @@ -0,0 +1,79 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_CONTRAINTOPERATOR_H +#define IGLOO_CONTRAINTOPERATOR_H + +namespace snowhouse { + + struct InvalidExpressionException + { + InvalidExpressionException(const std::string& message) : m_message(message) + { + } + + const std::string& Message() const + { + return m_message; + } + + std::string m_message; + }; + + struct ConstraintOperator + { + virtual ~ConstraintOperator() {} + + virtual void PerformOperation(ResultStack& result) = 0; + virtual int Precedence() const = 0; + + template + static bool EvaluateElementAgainstRestOfExpression(ConstraintListType& list, const ActualType& actual) + { + ResultStack innerResult; + OperatorStack innerOperators; + + EvaluateConstraintList(list.m_tail, innerResult, innerOperators, actual); + EvaluateAllOperatorsOnStack(innerOperators, innerResult); + + if(innerResult.empty()) + { + throw InvalidExpressionException("The expression after \"" + snowhouse::Stringize(list.m_head) + "\" operator does not yield any result"); + } + + return innerResult.top(); + } + + static void EvaluateOperatorsWithLessOrEqualPrecedence(const ConstraintOperator& op, OperatorStack& operators, ResultStack& result) + { + while(!operators.empty()) + { + ConstraintOperator* op_from_stack = operators.top(); + + if(op_from_stack->Precedence() > op.Precedence()) + { + break; + } + + op_from_stack->PerformOperation(result); + operators.pop(); + } + } + + static void EvaluateAllOperatorsOnStack(OperatorStack& operators, ResultStack& result) + { + while(!operators.empty()) + { + ConstraintOperator* op = operators.top(); + op->PerformOperation(result); + operators.pop(); + } + } + }; + +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/notoperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/notoperator.h new file mode 100644 index 00000000..709c8413 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/notoperator.h @@ -0,0 +1,53 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_NOTOPERATOR_H +#define IGLOO_NOTOPERATOR_H + +namespace snowhouse { + + struct NotOperator : public ConstraintOperator + { + template + void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual) + { + EvaluateOperatorsWithLessOrEqualPrecedence(*this, operators, result); + + operators.push(this); + + EvaluateConstraintList(list.m_tail, result, operators, actual); + } + + void PerformOperation(ResultStack& result) + { + if(result.size() < 1) + { + throw InvalidExpressionException("The expression contains a not operator without any operand"); + } + + bool right = result.top(); + result.pop(); + + result.push(!right); + } + + int Precedence() const + { + return 2; + } + }; + + template<> + struct Stringizer + { + static std::string ToString(const NotOperator&) + { + return "not"; + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/oroperator.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/oroperator.h new file mode 100644 index 00000000..5a307ff6 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/fluent/operators/oroperator.h @@ -0,0 +1,55 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_OROPERATOR_H +#define IGLOO_OROPERATOR_H + +namespace snowhouse { + + struct OrOperator : public ConstraintOperator + { + template + void Evaluate(ConstraintListType& list, ResultStack& result, OperatorStack& operators, const ActualType& actual) + { + EvaluateOperatorsWithLessOrEqualPrecedence(*this, operators, result); + + operators.push(this); + + EvaluateConstraintList(list.m_tail, result, operators, actual); + } + + void PerformOperation(ResultStack& result) + { + if(result.size() < 2) + { + throw InvalidExpressionException("The expression contains an or operator with too few operands"); + } + + bool right = result.top(); + result.pop(); + bool left = result.top(); + result.pop(); + + result.push(left || right); + } + + int Precedence() const + { + return 4; + } + }; + + template<> + struct Stringizer + { + static std::string ToString(const OrOperator&) + { + return "or"; + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/snowhouse.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/snowhouse.h new file mode 100644 index 00000000..4c36968e --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/snowhouse.h @@ -0,0 +1,24 @@ +#ifndef _SNOWHOUSE_H_JK_2013_06_28 +#define _SNOWHOUSE_H_JK_2013_06_28 + +#define SNOWHOUSE_VERSION "1.0.2" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stringize.h" +#include "constraints/constraints.h" +#include "fluent/fluent.h" +#include "assertionexception.h" +#include "assert.h" +#include "assertmacro.h" +#include "exceptions.h" + +#endif + diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringize.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringize.h new file mode 100644 index 00000000..ba2a0175 --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringize.h @@ -0,0 +1,90 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_STRINGIZE_H +#define IGLOO_STRINGIZE_H + +namespace snowhouse { + namespace detail { + + // This type soaks up any implicit conversions and makes the following operator<< + // less preferred than any other such operator found via ADL. + struct any + { + // Conversion constructor for any type. + template + any(T const&); + }; + + // A tag type returned by operator<< for the any struct in this namespace + // when T does not support <<. + struct tag {}; + + // Fallback operator<< for types T that don't support <<. + tag operator<<(std::ostream&, any const&); + + // Two overloads to distinguish whether T supports a certain operator expression. + // The first overload returns a reference to a two-element character array and is chosen if + // T does not support the expression, such as <<, whereas the second overload returns a char + // directly and is chosen if T supports the expression. So using sizeof(check()) + // returns 2 for the first overload and 1 for the second overload. + typedef char yes; + typedef char (&no)[2]; + + no check(tag); + + template + yes check(T const&); + + template + struct is_output_streamable + { + static const T& x; + static const bool value = sizeof(check(std::cout << x)) == sizeof(yes); + }; + + template + struct DefaultStringizer + { + static std::string ToString(const T& value) + { + std::ostringstream buf; + buf << value; + return buf.str(); + } + }; + + template + struct DefaultStringizer + { + static std::string ToString(const T&) + { + return "[unsupported type]"; + } + }; + } + + template + struct Stringizer; + + template + std::string Stringize(const T& value) + { + return Stringizer::ToString(value); + } + + // NOTE: Specialize snowhouse::Stringizer to customize assertion messages + template + struct Stringizer + { + static std::string ToString(const T& value) + { + return detail::DefaultStringizer< T, detail::is_output_streamable::value >::ToString(value); + } + }; +} + +#endif diff --git a/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringizers.h b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringizers.h new file mode 100644 index 00000000..79a416de --- /dev/null +++ b/vendor/bandit/bandit/assertion_frameworks/snowhouse/snowhouse/stringizers.h @@ -0,0 +1,60 @@ + +// Copyright Joakim Karlsson & Kim Gräsman 2010-2012. +// 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) + +#ifndef IGLOO_STRINGIZERS_H +#define IGLOO_STRINGIZERS_H + +namespace snowhouse +{ + + namespace detail + { + + template + struct SequentialContainerStringizer + { + static std::string + ToString(const Container& cont) + { + std::ostringstream stm; + typedef typename Container::const_iterator Iterator; + + stm << "[ "; + for (Iterator it = cont.begin(); it != cont.end();) + { + stm << snowhouse::Stringize(*it); + + if (++it != cont.end()) + { + stm << ", "; + } + } + stm << " ]"; + return stm.str(); + } + }; + } + + template + struct Stringizer > : detail::SequentialContainerStringizer< + std::vector > + { + }; + + template + struct Stringizer > : detail::SequentialContainerStringizer< + std::deque > + { + }; + + template + struct Stringizer > : detail::SequentialContainerStringizer< + std::list > + { + }; +} + +#endif diff --git a/vendor/bandit/bandit/bandit.h b/vendor/bandit/bandit/bandit.h new file mode 100644 index 00000000..14566ffa --- /dev/null +++ b/vendor/bandit/bandit/bandit.h @@ -0,0 +1,34 @@ +#ifndef BANDIT_BANDIT_H +#define BANDIT_BANDIT_H + +#include +#include +#include +#include +#include +#include + +#define BANDIT_VERSION "1.1.4" + +namespace bandit { namespace detail { + typedef std::function voidfunc_t; +}} + +#include +using namespace snowhouse; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/vendor/bandit/bandit/context.h b/vendor/bandit/bandit/context.h new file mode 100644 index 00000000..087818ed --- /dev/null +++ b/vendor/bandit/bandit/context.h @@ -0,0 +1,97 @@ +#ifndef BANDIT_CONTEXT_H +#define BANDIT_CONTEXT_H + +namespace bandit { + namespace detail { + + class context + { + public: + virtual ~context() {} + virtual const std::string& name() = 0; + virtual void execution_is_starting() = 0; + virtual void register_before_each(voidfunc_t func) = 0; + virtual void register_after_each(voidfunc_t func) = 0; + virtual void run_before_eaches() = 0; + virtual void run_after_eaches() = 0; + virtual bool hard_skip() = 0; + }; + + class bandit_context : public context + { + public: + bandit_context(const char* desc, bool hard_skip) + : desc_(desc), hard_skip_(hard_skip), is_executing_(false) + {} + + const std::string& name() + { + return desc_; + } + + void execution_is_starting() + { + is_executing_ = true; + } + + void register_before_each(voidfunc_t func) + { + if(is_executing_) + { + throw test_run_error("before_each was called after 'describe' or 'it'"); + } + + before_eaches_.push_back(func); + } + + void register_after_each(voidfunc_t func) + { + if(is_executing_) + { + throw test_run_error("after_each was called after 'describe' or 'it'"); + } + + after_eaches_.push_back(func); + } + + void run_before_eaches() + { + run_all(before_eaches_); + } + + void run_after_eaches() + { + run_all(after_eaches_); + } + + bool hard_skip() + { + return hard_skip_; + } + + private: + void run_all(const std::list& funcs) + { + auto call_func = [](voidfunc_t f){ f(); }; + + for_each(funcs.begin(), funcs.end(), call_func); + } + + private: + std::string desc_; + bool hard_skip_; + bool is_executing_; + std::list before_eaches_; + std::list after_eaches_; + }; + typedef std::deque contextstack_t; + + inline contextstack_t& context_stack() + { + static contextstack_t contexts; + return contexts; + } + } +} + +#endif diff --git a/vendor/bandit/bandit/external/optionparser.h b/vendor/bandit/bandit/external/optionparser.h new file mode 100644 index 00000000..ffeaac66 --- /dev/null +++ b/vendor/bandit/bandit/external/optionparser.h @@ -0,0 +1,2825 @@ +/* + * The Lean Mean C++ Option Parser + * + * Copyright (C) 2012 Matthias S. Benkmann + * + * The "Software" in the following 2 paragraphs refers to this file containing + * the code to The Lean Mean C++ Option Parser. + * The "Software" does NOT refer to any other files which you + * may have received alongside this file (e.g. as part of a larger project that + * incorporates The Lean Mean C++ Option Parser). + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this 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. + */ + +/* + * NOTE: It is recommended that you read the processed HTML doxygen documentation + * rather than this source. If you don't know doxygen, it's like javadoc for C++. + * If you don't want to install doxygen you can find a copy of the processed + * documentation at + * + * http://optionparser.sourceforge.net/ + * + */ + +/** + * @file + * + * @brief This is the only file required to use The Lean Mean C++ Option Parser. + * Just \#include it and you're set. + * + * The Lean Mean C++ Option Parser handles the program's command line arguments + * (argc, argv). + * It supports the short and long option formats of getopt(), getopt_long() + * and getopt_long_only() but has a more convenient interface. + * The following features set it apart from other option parsers: + * + * @par Highlights: + *
    + *
  • It is a header-only library. Just \#include "optionparser.h" and you're set. + *
  • It is freestanding. There are no dependencies whatsoever, not even the + * C or C++ standard library. + *
  • It has a usage message formatter that supports column alignment and + * line wrapping. This aids localization because it adapts to + * translated strings that are shorter or longer (even if they contain + * Asian wide characters). + *
  • Unlike getopt() and derivatives it doesn't force you to loop through + * options sequentially. Instead you can access options directly like this: + *
      + *
    • Test for presence of a switch in the argument vector: + * @code if ( options[QUIET] ) ... @endcode + *
    • Evaluate --enable-foo/--disable-foo pair where the last one used wins: + * @code if ( options[FOO].last()->type() == DISABLE ) ... @endcode + *
    • Cumulative option (-v verbose, -vv more verbose, -vvv even more verbose): + * @code int verbosity = options[VERBOSE].count(); @endcode + *
    • Iterate over all --file=<fname> arguments: + * @code for (Option* opt = options[FILE]; opt; opt = opt->next()) + * fname = opt->arg; ... @endcode + *
    • If you really want to, you can still process all arguments in order: + * @code + * for (int i = 0; i < p.optionsCount(); ++i) { + * Option& opt = buffer[i]; + * switch(opt.index()) { + * case HELP: ... + * case VERBOSE: ... + * case FILE: fname = opt.arg; ... + * case UNKNOWN: ... + * @endcode + *
    + *
@n + * Despite these features the code size remains tiny. + * It is smaller than uClibc's GNU getopt() and just a + * couple 100 bytes larger than uClibc's SUSv3 getopt(). @n + * (This does not include the usage formatter, of course. But you don't have to use that.) + * + * @par Download: + * Tarball with examples and test programs: + * optionparser-1.3.tar.gz @n + * Just the header (this is all you really need): + * optionparser.h + * + * @par Changelog: + * Version 1.3: Compatible with Microsoft Visual C++. @n + * Version 1.2: Added @ref option::Option::namelen "Option::namelen" and removed the extraction + * of short option characters into a special buffer. @n + * Changed @ref option::Arg::Optional "Arg::Optional" to accept arguments if they are attached + * rather than separate. This is what GNU getopt() does and how POSIX recommends + * utilities should interpret their arguments.@n + * Version 1.1: Optional mode with argument reordering as done by GNU getopt(), so that + * options and non-options can be mixed. See + * @ref option::Parser::parse() "Parser::parse()". + * + * @par Feedback: + * Send questions, bug reports, feature requests etc. to: optionparser-feedback (a) lists.sourceforge.net + * @htmlonly @endhtmlonly + * + * + * @par Example program: + * (Note: @c option::* identifiers are links that take you to their documentation.) + * @code + * #include + * #include "optionparser.h" + * + * enum optionIndex { UNKNOWN, HELP, PLUS }; + * const option::Descriptor usage[] = + * { + * {UNKNOWN, 0,"" , "" ,option::Arg::None, "USAGE: example [options]\n\n" + * "Options:" }, + * {HELP, 0,"" , "help",option::Arg::None, " --help \tPrint usage and exit." }, + * {PLUS, 0,"p", "plus",option::Arg::None, " --plus, -p \tIncrement count." }, + * {UNKNOWN, 0,"" , "" ,option::Arg::None, "\nExamples:\n" + * " example --unknown -- --this_is_no_option\n" + * " example -unk --plus -ppp file1 file2\n" }, + * {0,0,0,0,0,0} + * }; + * + * int main(int argc, char* argv[]) + * { + * argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present + * option::Stats stats(usage, argc, argv); + * option::Option options[stats.options_max], buffer[stats.buffer_max]; + * option::Parser parse(usage, argc, argv, options, buffer); + * + * if (parse.error()) + * return 1; + * + * if (options[HELP] || argc == 0) { + * option::printUsage(std::cout, usage); + * return 0; + * } + * + * std::cout << "--plus count: " << + * options[PLUS].count() << "\n"; + * + * for (option::Option* opt = options[UNKNOWN]; opt; opt = opt->next()) + * std::cout << "Unknown option: " << opt->name << "\n"; + * + * for (int i = 0; i < parse.nonOptionsCount(); ++i) + * std::cout << "Non-option #" << i << ": " << parse.nonOption(i) << "\n"; + * } + * @endcode + * + * @par Option syntax: + * @li The Lean Mean C++ Option Parser follows POSIX getopt() conventions and supports + * GNU-style getopt_long() long options as well as Perl-style single-minus + * long options (getopt_long_only()). + * @li short options have the format @c -X where @c X is any character that fits in a char. + * @li short options can be grouped, i.e. -X -Y is equivalent to @c -XY. + * @li a short option may take an argument either separate (-X foo) or + * attached (@c -Xfoo). You can make the parser accept the additional format @c -X=foo by + * registering @c X as a long option (in addition to being a short option) and + * enabling single-minus long options. + * @li an argument-taking short option may be grouped if it is the last in the group, e.g. + * @c -ABCXfoo or -ABCX foo (@c foo is the argument to the @c -X option). + * @li a lone minus character @c '-' is not treated as an option. It is customarily used where + * a file name is expected to refer to stdin or stdout. + * @li long options have the format @c --option-name. + * @li the option-name of a long option can be anything and include any characters. + * Even @c = characters will work, but don't do that. + * @li [optional] long options may be abbreviated as long as the abbreviation is unambiguous. + * You can set a minimum length for abbreviations. + * @li [optional] long options may begin with a single minus. The double minus form is always + * accepted, too. + * @li a long option may take an argument either separate ( --option arg ) or + * attached ( --option=arg ). In the attached form the equals sign is mandatory. + * @li an empty string can be passed as an attached long option argument: --option-name= . + * Note the distinction between an empty string as argument and no argument at all. + * @li an empty string is permitted as separate argument to both long and short options. + * @li Arguments to both short and long options may start with a @c '-' character. E.g. + * -X-X , -X -X or --long-X=-X . If @c -X + * and @c --long-X take an argument, that argument will be @c "-X" in all 3 cases. + * @li If using the built-in @ref option::Arg::Optional "Arg::Optional", optional arguments must + * be attached. + * @li the special option @c -- (i.e. without a name) terminates the list of + * options. Everything that follows is a non-option argument, even if it starts with + * a @c '-' character. The @c -- itself will not appear in the parse results. + * @li the first argument that doesn't start with @c '-' or @c '--' and does not belong to + * a preceding argument-taking option, will terminate the option list and is the + * first non-option argument. All following command line arguments are treated as + * non-option arguments, even if they start with @c '-' . @n + * NOTE: This behaviour is mandated by POSIX, but GNU getopt() only honours this if it is + * explicitly requested (e.g. by setting POSIXLY_CORRECT). @n + * You can enable the GNU behaviour by passing @c true as first argument to + * e.g. @ref option::Parser::parse() "Parser::parse()". + * @li Arguments that look like options (i.e. @c '-' followed by at least 1 character) but + * aren't, are NOT treated as non-option arguments. They are treated as unknown options and + * are collected into a list of unknown options for error reporting. @n + * This means that in order to pass a first non-option + * argument beginning with the minus character it is required to use the + * @c -- special option, e.g. + * @code + * program -x -- --strange-filename + * @endcode + * In this example, @c --strange-filename is a non-option argument. If the @c -- + * were omitted, it would be treated as an unknown option. @n + * See @ref option::Descriptor::longopt for information on how to collect unknown options. + * + */ + +#ifndef OPTIONPARSER_H_ +#define OPTIONPARSER_H_ + +/** @brief The namespace of The Lean Mean C++ Option Parser. */ +namespace option +{ + +#ifdef _MSC_VER +#include +#pragma intrinsic(_BitScanReverse) +struct MSC_Builtin_CLZ +{ + static int builtin_clz(unsigned x) + { + unsigned long index; + _BitScanReverse(&index, x); + return 32-index; // int is always 32bit on Windows, even for target x64 + } +}; +#define __builtin_clz(x) MSC_Builtin_CLZ::builtin_clz(x) + +#pragma warning( push ) +#pragma warning( disable: 4510 ) +#pragma warning( disable: 4512 ) +#pragma warning( disable: 4610 ) +#pragma warning( disable: 4127 ) +#endif + +class Option; + +/** + * @brief Possible results when checking if an argument is valid for a certain option. + * + * In the case that no argument is provided for an option that takes an + * optional argument, return codes @c ARG_OK and @c ARG_IGNORE are equivalent. + */ +enum ArgStatus +{ + //! The option does not take an argument. + ARG_NONE, + //! The argument is acceptable for the option. + ARG_OK, + //! The argument is not acceptable but that's non-fatal because the option's argument is optional. + ARG_IGNORE, + //! The argument is not acceptable and that's fatal. + ARG_ILLEGAL +}; + +/** + * @brief Signature of functions that check if an argument is valid for a certain type of option. + * + * Every Option has such a function assigned in its Descriptor. + * @code + * Descriptor usage[] = { {UNKNOWN, 0, "", "", Arg::None, ""}, ... }; + * @endcode + * + * A CheckArg function has the following signature: + * @code ArgStatus CheckArg(const Option& option, bool msg); @endcode + * + * It is used to check if a potential argument would be acceptable for the option. + * It will even be called if there is no argument. In that case @c option.arg will be @c NULL. + * + * If @c msg is @c true and the function determines that an argument is not acceptable and + * that this is a fatal error, it should output a message to the user before + * returning @ref ARG_ILLEGAL. If @c msg is @c false the function should remain silent (or you + * will get duplicate messages). + * + * See @ref ArgStatus for the meaning of the return values. + * + * While you can provide your own functions, + * often the following pre-defined checks (which never return @ref ARG_ILLEGAL) will suffice: + * + * @li @c Arg::None @copybrief Arg::None + * @li @c Arg::Optional @copybrief Arg::Optional + * + */ +typedef ArgStatus (*CheckArg)(const Option& option, bool msg); + +/** + * @brief Describes an option, its help text (usage) and how it should be parsed. + * + * The main input when constructing an option::Parser is an array of Descriptors. + + * @par Example: + * @code + * enum OptionIndex {CREATE, ...}; + * enum OptionType {DISABLE, ENABLE, OTHER}; + * + * const option::Descriptor usage[] = { + * { CREATE, // index + * OTHER, // type + * "c", // shortopt + * "create", // longopt + * Arg::None, // check_arg + * "--create Tells the program to create something." // help + * } + * , ... + * }; + * @endcode + */ +struct Descriptor +{ + /** + * @brief Index of this option's linked list in the array filled in by the parser. + * + * Command line options whose Descriptors have the same index will end up in the same + * linked list in the order in which they appear on the command line. If you have + * multiple long option aliases that refer to the same option, give their descriptors + * the same @c index. + * + * If you have options that mean exactly opposite things + * (e.g. @c --enable-foo and @c --disable-foo ), you should also give them the same + * @c index, but distinguish them through different values for @ref type. + * That way they end up in the same list and you can just take the last element of the + * list and use its type. This way you get the usual behaviour where switches later + * on the command line override earlier ones without having to code it manually. + * + * @par Tip: + * Use an enum rather than plain ints for better readability, as shown in the example + * at Descriptor. + */ + const unsigned index; + + /** + * @brief Used to distinguish between options with the same @ref index. + * See @ref index for details. + * + * It is recommended that you use an enum rather than a plain int to make your + * code more readable. + */ + const int type; + + /** + * @brief Each char in this string will be accepted as a short option character. + * + * The string must not include the minus character @c '-' or you'll get undefined + * behaviour. + * + * If this Descriptor should not have short option characters, use the empty + * string "". NULL is not permitted here! + * + * See @ref longopt for more information. + */ + const char* const shortopt; + + /** + * @brief The long option name (without the leading @c -- ). + * + * If this Descriptor should not have a long option name, use the empty + * string "". NULL is not permitted here! + * + * While @ref shortopt allows multiple short option characters, each + * Descriptor can have only a single long option name. If you have multiple + * long option names referring to the same option use separate Descriptors + * that have the same @ref index and @ref type. You may repeat + * short option characters in such an alias Descriptor but there's no need to. + * + * @par Dummy Descriptors: + * You can use dummy Descriptors with an + * empty string for both @ref shortopt and @ref longopt to add text to + * the usage that is not related to a specific option. See @ref help. + * The first dummy Descriptor will be used for unknown options (see below). + * + * @par Unknown Option Descriptor: + * The first dummy Descriptor in the list of Descriptors, + * whose @ref shortopt and @ref longopt are both the empty string, will be used + * as the Descriptor for unknown options. An unknown option is a string in + * the argument vector that is not a lone minus @c '-' but starts with a minus + * character and does not match any Descriptor's @ref shortopt or @ref longopt. @n + * Note that the dummy descriptor's @ref check_arg function @e will be called and + * its return value will be evaluated as usual. I.e. if it returns @ref ARG_ILLEGAL + * the parsing will be aborted with Parser::error()==true. @n + * if @c check_arg does not return @ref ARG_ILLEGAL the descriptor's + * @ref index @e will be used to pick the linked list into which + * to put the unknown option. @n + * If there is no dummy descriptor, unknown options will be dropped silently. + * + */ + const char* const longopt; + + /** + * @brief For each option that matches @ref shortopt or @ref longopt this function + * will be called to check a potential argument to the option. + * + * This function will be called even if there is no potential argument. In that case + * it will be passed @c NULL as @c arg parameter. Do not confuse this with the empty + * string. + * + * See @ref CheckArg for more information. + */ + const CheckArg check_arg; + + /** + * @brief The usage text associated with the options in this Descriptor. + * + * You can use option::printUsage() to format your usage message based on + * the @c help texts. You can use dummy Descriptors where + * @ref shortopt and @ref longopt are both the empty string to add text to + * the usage that is not related to a specific option. + * + * See option::printUsage() for special formatting characters you can use in + * @c help to get a column layout. + * + * @attention + * Must be UTF-8-encoded. If your compiler supports C++11 you can use the "u8" + * prefix to make sure string literals are properly encoded. + */ + const char* help; +}; + +/** + * @brief A parsed option from the command line together with its argument if it has one. + * + * The Parser chains all parsed options with the same Descriptor::index together + * to form a linked list. This allows you to easily implement all of the common ways + * of handling repeated options and enable/disable pairs. + * + * @li Test for presence of a switch in the argument vector: + * @code if ( options[QUIET] ) ... @endcode + * @li Evaluate --enable-foo/--disable-foo pair where the last one used wins: + * @code if ( options[FOO].last()->type() == DISABLE ) ... @endcode + * @li Cumulative option (-v verbose, -vv more verbose, -vvv even more verbose): + * @code int verbosity = options[VERBOSE].count(); @endcode + * @li Iterate over all --file=<fname> arguments: + * @code for (Option* opt = options[FILE]; opt; opt = opt->next()) + * fname = opt->arg; ... @endcode + */ +class Option +{ + Option* next_; + Option* prev_; +public: + /** + * @brief Pointer to this Option's Descriptor. + * + * Remember that the first dummy descriptor (see @ref Descriptor::longopt) is used + * for unknown options. + * + * @attention + * @c desc==NULL signals that this Option is unused. This is the default state of + * elements in the result array. You don't need to test @c desc explicitly. You + * can simply write something like this: + * @code + * if (options[CREATE]) + * { + * ... + * } + * @endcode + * This works because of operator const Option*() . + */ + const Descriptor* desc; + + /** + * @brief The name of the option as used on the command line. + * + * The main purpose of this string is to be presented to the user in messages. + * + * In the case of a long option, this is the actual @c argv pointer, i.e. the first + * character is a '-'. In the case of a short option this points to the option + * character within the @c argv string. + * + * Note that in the case of a short option group or an attached option argument, this + * string will contain additional characters following the actual name. Use @ref namelen + * to filter out the actual option name only. + * + */ + const char* name; + + /** + * @brief Pointer to this Option's argument (if any). + * + * NULL if this option has no argument. Do not confuse this with the empty string which + * is a valid argument. + */ + const char* arg; + + /** + * @brief The length of the option @ref name. + * + * Because @ref name points into the actual @c argv string, the option name may be + * followed by more characters (e.g. other short options in the same short option group). + * This value is the number of bytes (not characters!) that are part of the actual name. + * + * For a short option, this length is always 1. For a long option this length is always + * at least 2 if single minus long options are permitted and at least 3 if they are disabled. + * + * @note + * In the pathological case of a minus within a short option group (e.g. @c -xf-z), this + * length is incorrect, because this case will be misinterpreted as a long option and the + * name will therefore extend to the string's 0-terminator or a following '=" character + * if there is one. This is irrelevant for most uses of @ref name and @c namelen. If you + * really need to distinguish the case of a long and a short option, compare @ref name to + * the @c argv pointers. A long option's @c name is always identical to one of them, + * whereas a short option's is never. + */ + int namelen; + + /** + * @brief Returns Descriptor::type of this Option's Descriptor, or 0 if this Option + * is invalid (unused). + * + * Because this method (and last(), too) can be used even on unused Options with desc==0, you can (provided + * you arrange your types properly) switch on type() without testing validity first. + * @code + * enum OptionType { UNUSED=0, DISABLED=0, ENABLED=1 }; + * enum OptionIndex { FOO }; + * const Descriptor usage[] = { + * { FOO, ENABLED, "", "enable-foo", Arg::None, 0 }, + * { FOO, DISABLED, "", "disable-foo", Arg::None, 0 }, + * { 0, 0, 0, 0, 0, 0 } }; + * ... + * switch(options[FOO].last()->type()) // no validity check required! + * { + * case ENABLED: ... + * case DISABLED: ... // UNUSED==DISABLED ! + * } + * @endcode + */ + int type() const + { + return desc == 0 ? 0 : desc->type; + } + + /** + * @brief Returns Descriptor::index of this Option's Descriptor, or -1 if this Option + * is invalid (unused). + */ + int index() const + { + return desc == 0 ? -1 : desc->index; + } + + /** + * @brief Returns the number of times this Option (or others with the same Descriptor::index) + * occurs in the argument vector. + * + * This corresponds to the number of elements in the linked list this Option is part of. + * It doesn't matter on which element you call count(). The return value is always the same. + * + * Use this to implement cumulative options, such as -v, -vv, -vvv for + * different verbosity levels. + * + * Returns 0 when called for an unused/invalid option. + */ + int count() + { + int c = (desc == 0 ? 0 : 1); + Option* p = first(); + while (!p->isLast()) + { + ++c; + p = p->next_; + }; + return c; + } + + /** + * @brief Returns true iff this is the first element of the linked list. + * + * The first element in the linked list is the first option on the command line + * that has the respective Descriptor::index value. + * + * Returns true for an unused/invalid option. + */ + bool isFirst() const + { + return isTagged(prev_); + } + + /** + * @brief Returns true iff this is the last element of the linked list. + * + * The last element in the linked list is the last option on the command line + * that has the respective Descriptor::index value. + * + * Returns true for an unused/invalid option. + */ + bool isLast() const + { + return isTagged(next_); + } + + /** + * @brief Returns a pointer to the first element of the linked list. + * + * Use this when you want the first occurrence of an option on the command line to + * take precedence. Note that this is not the way most programs handle options. + * You should probably be using last() instead. + * + * @note + * This method may be called on an unused/invalid option and will return a pointer to the + * option itself. + */ + Option* first() + { + Option* p = this; + while (!p->isFirst()) + p = p->prev_; + return p; + } + + /** + * @brief Returns a pointer to the last element of the linked list. + * + * Use this when you want the last occurrence of an option on the command line to + * take precedence. This is the most common way of handling conflicting options. + * + * @note + * This method may be called on an unused/invalid option and will return a pointer to the + * option itself. + * + * @par Tip: + * If you have options with opposite meanings (e.g. @c --enable-foo and @c --disable-foo), you + * can assign them the same Descriptor::index to get them into the same list. Distinguish them by + * Descriptor::type and all you have to do is check last()->type() to get + * the state listed last on the command line. + */ + Option* last() + { + return first()->prevwrap(); + } + + /** + * @brief Returns a pointer to the previous element of the linked list or NULL if + * called on first(). + * + * If called on first() this method returns NULL. Otherwise it will return the + * option with the same Descriptor::index that precedes this option on the command + * line. + */ + Option* prev() + { + return isFirst() ? 0 : prev_; + } + + /** + * @brief Returns a pointer to the previous element of the linked list with wrap-around from + * first() to last(). + * + * If called on first() this method returns last(). Otherwise it will return the + * option with the same Descriptor::index that precedes this option on the command + * line. + */ + Option* prevwrap() + { + return untag(prev_); + } + + /** + * @brief Returns a pointer to the next element of the linked list or NULL if called + * on last(). + * + * If called on last() this method returns NULL. Otherwise it will return the + * option with the same Descriptor::index that follows this option on the command + * line. + */ + Option* next() + { + return isLast() ? 0 : next_; + } + + /** + * @brief Returns a pointer to the next element of the linked list with wrap-around from + * last() to first(). + * + * If called on last() this method returns first(). Otherwise it will return the + * option with the same Descriptor::index that follows this option on the command + * line. + */ + Option* nextwrap() + { + return untag(next_); + } + + /** + * @brief Makes @c new_last the new last() by chaining it into the list after last(). + * + * It doesn't matter which element you call append() on. The new element will always + * be appended to last(). + * + * @attention + * @c new_last must not yet be part of a list, or that list will become corrupted, because + * this method does not unchain @c new_last from an existing list. + */ + void append(Option* new_last) + { + Option* p = last(); + Option* f = first(); + p->next_ = new_last; + new_last->prev_ = p; + new_last->next_ = tag(f); + f->prev_ = tag(new_last); + } + + /** + * @brief Casts from Option to const Option* but only if this Option is valid. + * + * If this Option is valid (i.e. @c desc!=NULL), returns this. + * Otherwise returns NULL. This allows testing an Option directly + * in an if-clause to see if it is used: + * @code + * if (options[CREATE]) + * { + * ... + * } + * @endcode + * It also allows you to write loops like this: + * @code for (Option* opt = options[FILE]; opt; opt = opt->next()) + * fname = opt->arg; ... @endcode + */ + operator const Option*() const + { + return desc ? this : 0; + } + + /** + * @brief Casts from Option to Option* but only if this Option is valid. + * + * If this Option is valid (i.e. @c desc!=NULL), returns this. + * Otherwise returns NULL. This allows testing an Option directly + * in an if-clause to see if it is used: + * @code + * if (options[CREATE]) + * { + * ... + * } + * @endcode + * It also allows you to write loops like this: + * @code for (Option* opt = options[FILE]; opt; opt = opt->next()) + * fname = opt->arg; ... @endcode + */ + operator Option*() + { + return desc ? this : 0; + } + + /** + * @brief Creates a new Option that is a one-element linked list and has NULL + * @ref desc, @ref name, @ref arg and @ref namelen. + */ + Option() : + desc(0), name(0), arg(0), namelen(0) + { + prev_ = tag(this); + next_ = tag(this); + } + + /** + * @brief Creates a new Option that is a one-element linked list and has the given + * values for @ref desc, @ref name and @ref arg. + * + * If @c name_ points at a character other than '-' it will be assumed to refer to a + * short option and @ref namelen will be set to 1. Otherwise the length will extend to + * the first '=' character or the string's 0-terminator. + */ + Option(const Descriptor* desc_, const char* name_, const char* arg_) + { + init(desc_, name_, arg_); + } + + /** + * @brief Makes @c *this a copy of @c orig except for the linked list pointers. + * + * After this operation @c *this will be a one-element linked list. + */ + void operator=(const Option& orig) + { + init(orig.desc, orig.name, orig.arg); + } + + /** + * @brief Makes @c *this a copy of @c orig except for the linked list pointers. + * + * After this operation @c *this will be a one-element linked list. + */ + Option(const Option& orig) + { + init(orig.desc, orig.name, orig.arg); + } + +private: + /** + * @internal + * @brief Sets the fields of this Option to the given values (extracting @c name if necessary). + * + * If @c name_ points at a character other than '-' it will be assumed to refer to a + * short option and @ref namelen will be set to 1. Otherwise the length will extend to + * the first '=' character or the string's 0-terminator. + */ + void init(const Descriptor* desc_, const char* name_, const char* arg_) + { + desc = desc_; + name = name_; + arg = arg_; + prev_ = tag(this); + next_ = tag(this); + namelen = 0; + if (name == 0) + return; + namelen = 1; + if (name[0] != '-') + return; + while (name[namelen] != 0 && name[namelen] != '=') + ++namelen; + } + + static Option* tag(Option* ptr) + { + return (Option*) ((unsigned long long) ptr | 1); + } + + static Option* untag(Option* ptr) + { + return (Option*) ((unsigned long long) ptr & ~1ull); + } + + static bool isTagged(Option* ptr) + { + return ((unsigned long long) ptr & 1); + } +}; + +/** + * @brief Functions for checking the validity of option arguments. + * + * @copydetails CheckArg + * + * The following example code + * can serve as starting place for writing your own more complex CheckArg functions: + * @code + * struct Arg: public option::Arg + * { + * static void printError(const char* msg1, const option::Option& opt, const char* msg2) + * { + * fprintf(stderr, "ERROR: %s", msg1); + * fwrite(opt.name, opt.namelen, 1, stderr); + * fprintf(stderr, "%s", msg2); + * } + * + * static option::ArgStatus Unknown(const option::Option& option, bool msg) + * { + * if (msg) printError("Unknown option '", option, "'\n"); + * return option::ARG_ILLEGAL; + * } + * + * static option::ArgStatus Required(const option::Option& option, bool msg) + * { + * if (option.arg != 0) + * return option::ARG_OK; + * + * if (msg) printError("Option '", option, "' requires an argument\n"); + * return option::ARG_ILLEGAL; + * } + * + * static option::ArgStatus NonEmpty(const option::Option& option, bool msg) + * { + * if (option.arg != 0 && option.arg[0] != 0) + * return option::ARG_OK; + * + * if (msg) printError("Option '", option, "' requires a non-empty argument\n"); + * return option::ARG_ILLEGAL; + * } + * + * static option::ArgStatus Numeric(const option::Option& option, bool msg) + * { + * char* endptr = 0; + * if (option.arg != 0 && strtol(option.arg, &endptr, 10)){}; + * if (endptr != option.arg && *endptr == 0) + * return option::ARG_OK; + * + * if (msg) printError("Option '", option, "' requires a numeric argument\n"); + * return option::ARG_ILLEGAL; + * } + * }; + * @endcode + */ +struct Arg +{ + //! @brief For options that don't take an argument: Returns ARG_NONE. + static ArgStatus None(const Option&, bool) + { + return ARG_NONE; + } + + //! @brief Returns ARG_OK if the argument is attached and ARG_IGNORE otherwise. + static ArgStatus Optional(const Option& option, bool) + { + if (option.arg && option.name[option.namelen] != 0) + return ARG_OK; + else + return ARG_IGNORE; + } +}; + +/** + * @brief Determines the minimum lengths of the buffer and options arrays used for Parser. + * + * Because Parser doesn't use dynamic memory its output arrays have to be pre-allocated. + * If you don't want to use fixed size arrays (which may turn out too small, causing + * command line arguments to be dropped), you can use Stats to determine the correct sizes. + * Stats work cumulative. You can first pass in your default options and then the real + * options and afterwards the counts will reflect the union. + */ +struct Stats +{ + /** + * @brief Number of elements needed for a @c buffer[] array to be used for + * @ref Parser::parse() "parsing" the same argument vectors that were fed + * into this Stats object. + * + * @note + * This number is always 1 greater than the actual number needed, to give + * you a sentinel element. + */ + unsigned buffer_max; + + /** + * @brief Number of elements needed for an @c options[] array to be used for + * @ref Parser::parse() "parsing" the same argument vectors that were fed + * into this Stats object. + * + * @note + * @li This number is always 1 greater than the actual number needed, to give + * you a sentinel element. + * @li This number depends only on the @c usage, not the argument vectors, because + * the @c options array needs exactly one slot for each possible Descriptor::index. + */ + unsigned options_max; + + /** + * @brief Creates a Stats object with counts set to 1 (for the sentinel element). + */ + Stats() : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel + { + } + + /** + * @brief Creates a new Stats object and immediately updates it for the + * given @c usage and argument vector. You may pass 0 for @c argc and/or @c argv, + * if you just want to update @ref options_max. + * + * @note + * The calls to Stats methods must match the later calls to Parser methods. + * See Parser::parse() for the meaning of the arguments. + */ + Stats(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel + { + add(gnu, usage, argc, argv, min_abbr_len, single_minus_longopt); + } + + //! @brief Stats(...) with non-const argv. + Stats(bool gnu, const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel + { + add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + } + + //! @brief POSIX Stats(...) (gnu==false). + Stats(const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel + { + add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); + } + + //! @brief POSIX Stats(...) (gnu==false) with non-const argv. + Stats(const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel + { + add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + } + + /** + * @brief Updates this Stats object for the + * given @c usage and argument vector. You may pass 0 for @c argc and/or @c argv, + * if you just want to update @ref options_max. + * + * @note + * The calls to Stats methods must match the later calls to Parser methods. + * See Parser::parse() for the meaning of the arguments. + */ + void add(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false); + + //! @brief add() with non-const argv. + void add(bool gnu, const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) + { + add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + } + + //! @brief POSIX add() (gnu==false). + void add(const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) + { + add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); + } + + //! @brief POSIX add() (gnu==false) with non-const argv. + void add(const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // + bool single_minus_longopt = false) + { + add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + } +private: + class CountOptionsAction; +}; + +/** + * @brief Checks argument vectors for validity and parses them into data + * structures that are easier to work with. + * + * @par Example: + * @code + * int main(int argc, char* argv[]) + * { + * argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present + * option::Stats stats(usage, argc, argv); + * option::Option options[stats.options_max], buffer[stats.buffer_max]; + * option::Parser parse(usage, argc, argv, options, buffer); + * + * if (parse.error()) + * return 1; + * + * if (options[HELP]) + * ... + * @endcode + */ +class Parser +{ + int op_count; //!< @internal @brief see optionsCount() + int nonop_count; //!< @internal @brief see nonOptionsCount() + const char** nonop_args; //!< @internal @brief see nonOptions() + bool err; //!< @internal @brief see error() +public: + + /** + * @brief Creates a new Parser. + */ + Parser() : + op_count(0), nonop_count(0), nonop_args(0), err(false) + { + } + + /** + * @brief Creates a new Parser and immediately parses the given argument vector. + * @copydetails parse() + */ + Parser(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) + { + parse(gnu, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + //! @brief Parser(...) with non-const argv. + Parser(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) + { + parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + //! @brief POSIX Parser(...) (gnu==false). + Parser(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], int min_abbr_len = 0, + bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) + { + parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + //! @brief POSIX Parser(...) (gnu==false) with non-const argv. + Parser(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0, + bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) + { + parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + /** + * @brief Parses the given argument vector. + * + * @param gnu if true, parse() will not stop at the first non-option argument. Instead it will + * reorder arguments so that all non-options are at the end. This is the default behaviour + * of GNU getopt() but is not conforming to POSIX. @n + * Note, that once the argument vector has been reordered, the @c gnu flag will have + * no further effect on this argument vector. So it is enough to pass @c gnu==true when + * creating Stats. + * @param usage Array of Descriptor objects that describe the options to support. The last entry + * of this array must have 0 in all fields. + * @param argc The number of elements from @c argv that are to be parsed. If you pass -1, the number + * will be determined automatically. In that case the @c argv list must end with a NULL + * pointer. + * @param argv The arguments to be parsed. If you pass -1 as @c argc the last pointer in the @c argv + * list must be NULL to mark the end. + * @param options Each entry is the first element of a linked list of Options. Each new option + * that is parsed will be appended to the list specified by that Option's + * Descriptor::index. If an entry is not yet used (i.e. the Option is invalid), + * it will be replaced rather than appended to. @n + * The minimum length of this array is the greatest Descriptor::index value that + * occurs in @c usage @e PLUS ONE. + * @param buffer Each argument that is successfully parsed (including unknown arguments, if they + * have a Descriptor whose CheckArg does not return @ref ARG_ILLEGAL) will be stored in this + * array. parse() scans the array for the first invalid entry and begins writing at that + * index. You can pass @c bufmax to limit the number of options stored. + * @param min_abbr_len Passing a value min_abbr_len > 0 enables abbreviated long + * options. The parser will match a prefix of a long option as if it was + * the full long option (e.g. @c --foob=10 will be interpreted as if it was + * @c --foobar=10 ), as long as the prefix has at least @c min_abbr_len characters + * (not counting the @c -- ) and is unambiguous. + * @n Be careful if combining @c min_abbr_len=1 with @c single_minus_longopt=true + * because the ambiguity check does not consider short options and abbreviated + * single minus long options will take precedence over short options. + * @param single_minus_longopt Passing @c true for this option allows long options to begin with + * a single minus. The double minus form will still be recognized. Note that + * single minus long options take precedence over short options and short option + * groups. E.g. @c -file would be interpreted as @c --file and not as + * -f -i -l -e (assuming a long option named @c "file" exists). + * @param bufmax The greatest index in the @c buffer[] array that parse() will write to is + * @c bufmax-1. If there are more options, they will be processed (in particular + * their CheckArg will be called) but not stored. @n + * If you used Stats::buffer_max to dimension this array, you can pass + * -1 (or not pass @c bufmax at all) which tells parse() that the buffer is + * "large enough". + * @attention + * Remember that @c options and @c buffer store Option @e objects, not pointers. Therefore it + * is not possible for the same object to be in both arrays. For those options that are found in + * both @c buffer[] and @c options[] the respective objects are independent copies. And only the + * objects in @c options[] are properly linked via Option::next() and Option::prev(). + * You can iterate over @c buffer[] to + * process all options in the order they appear in the argument vector, but if you want access to + * the other Options with the same Descriptor::index, then you @e must access the linked list via + * @c options[]. You can get the linked list in options from a buffer object via something like + * @c options[buffer[i].index()]. + */ + void parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1); + + //! @brief parse() with non-const argv. + void parse(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) + { + parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + //! @brief POSIX parse() (gnu==false). + void parse(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) + { + parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + //! @brief POSIX parse() (gnu==false) with non-const argv. + void parse(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0, + bool single_minus_longopt = false, int bufmax = -1) + { + parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + } + + /** + * @brief Returns the number of valid Option objects in @c buffer[]. + * + * @note + * @li The returned value always reflects the number of Options in the buffer[] array used for + * the most recent call to parse(). + * @li The count (and the buffer[]) includes unknown options if they are collected + * (see Descriptor::longopt). + */ + int optionsCount() + { + return op_count; + } + + /** + * @brief Returns the number of non-option arguments that remained at the end of the + * most recent parse() that actually encountered non-option arguments. + * + * @note + * A parse() that does not encounter non-option arguments will leave this value + * as well as nonOptions() undisturbed. This means you can feed the Parser a + * default argument vector that contains non-option arguments (e.g. a default filename). + * Then you feed it the actual arguments from the user. If the user has supplied at + * least one non-option argument, all of the non-option arguments from the default + * disappear and are replaced by the user's non-option arguments. However, if the + * user does not supply any non-option arguments the defaults will still be in + * effect. + */ + int nonOptionsCount() + { + return nonop_count; + } + + /** + * @brief Returns a pointer to an array of non-option arguments (only valid + * if nonOptionsCount() >0 ). + * + * @note + * @li parse() does not copy arguments, so this pointer points into the actual argument + * vector as passed to parse(). + * @li As explained at nonOptionsCount() this pointer is only changed by parse() calls + * that actually encounter non-option arguments. A parse() call that encounters only + * options, will not change nonOptions(). + */ + const char** nonOptions() + { + return nonop_args; + } + + /** + * @brief Returns nonOptions()[i] (@e without checking if i is in range!). + */ + const char* nonOption(int i) + { + return nonOptions()[i]; + } + + /** + * @brief Returns @c true if an unrecoverable error occurred while parsing options. + * + * An illegal argument to an option (i.e. CheckArg returns @ref ARG_ILLEGAL) is an + * unrecoverable error that aborts the parse. Unknown options are only an error if + * their CheckArg function returns @ref ARG_ILLEGAL. Otherwise they are collected. + * In that case if you want to exit the program if either an illegal argument + * or an unknown option has been passed, use code like this + * + * @code + * if (parser.error() || options[UNKNOWN]) + * exit(1); + * @endcode + * + */ + bool error() + { + return err; + } + +private: + friend struct Stats; + class StoreOptionAction; + struct Action; + + /** + * @internal + * @brief This is the core function that does all the parsing. + * @retval false iff an unrecoverable error occurred. + */ + static bool workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action, + bool single_minus_longopt, bool print_errors, int min_abbr_len); + + /** + * @internal + * @brief Returns true iff @c st1 is a prefix of @c st2 and + * in case @c st2 is longer than @c st1, then + * the first additional character is '='. + * + * @par Examples: + * @code + * streq("foo", "foo=bar") == true + * streq("foo", "foobar") == false + * streq("foo", "foo") == true + * streq("foo=bar", "foo") == false + * @endcode + */ + static bool streq(const char* st1, const char* st2) + { + while (*st1 != 0) + if (*st1++ != *st2++) + return false; + return (*st2 == 0 || *st2 == '='); + } + + /** + * @internal + * @brief Like streq() but handles abbreviations. + * + * Returns true iff @c st1 and @c st2 have a common + * prefix with the following properties: + * @li (if min > 0) its length is at least @c min characters or the same length as @c st1 (whichever is smaller). + * @li (if min <= 0) its length is the same as that of @c st1 + * @li within @c st2 the character following the common prefix is either '=' or end-of-string. + * + * Examples: + * @code + * streqabbr("foo", "foo=bar",) == true + * streqabbr("foo", "fo=bar" , 2) == true + * streqabbr("foo", "fo" , 2) == true + * streqabbr("foo", "fo" , 0) == false + * streqabbr("foo", "f=bar" , 2) == false + * streqabbr("foo", "f" , 2) == false + * streqabbr("fo" , "foo=bar",) == false + * streqabbr("foo", "foobar" ,) == false + * streqabbr("foo", "fobar" ,) == false + * streqabbr("foo", "foo" ,) == true + * @endcode + */ + static bool streqabbr(const char* st1, const char* st2, long long min) + { + const char* st1start = st1; + while (*st1 != 0 && (*st1 == *st2)) + { + ++st1; + ++st2; + } + + return (*st1 == 0 || (min > 0 && (st1 - st1start) >= min)) && (*st2 == 0 || *st2 == '='); + } + + /** + * @internal + * @brief Returns true iff character @c ch is contained in the string @c st. + * + * Returns @c true for @c ch==0 . + */ + static bool instr(char ch, const char* st) + { + while (*st != 0 && *st != ch) + ++st; + return *st == ch; + } + + /** + * @internal + * @brief Rotates args[-count],...,args[-1],args[0] to become + * args[0],args[-count],...,args[-1]. + */ + static void shift(const char** args, int count) + { + for (int i = 0; i > -count; --i) + { + const char* temp = args[i]; + args[i] = args[i - 1]; + args[i - 1] = temp; + } + } +}; + +/** + * @internal + * @brief Interface for actions Parser::workhorse() should perform for each Option it + * parses. + */ +struct Parser::Action +{ + /** + * @brief Called by Parser::workhorse() for each Option that has been successfully + * parsed (including unknown + * options if they have a Descriptor whose Descriptor::check_arg does not return + * @ref ARG_ILLEGAL. + * + * Returns @c false iff a fatal error has occured and the parse should be aborted. + */ + virtual bool perform(Option&) + { + return true; + } + + /** + * @brief Called by Parser::workhorse() after finishing the parse. + * @param numargs the number of non-option arguments remaining + * @param args pointer to the first remaining non-option argument (if numargs > 0). + * + * @return + * @c false iff a fatal error has occurred. + */ + virtual bool finished(int numargs, const char** args) + { + (void) numargs; + (void) args; + return true; + } +}; + +/** + * @internal + * @brief An Action to pass to Parser::workhorse() that will increment a counter for + * each parsed Option. + */ +class Stats::CountOptionsAction: public Parser::Action +{ + unsigned* buffer_max; +public: + /** + * Creates a new CountOptionsAction that will increase @c *buffer_max_ for each + * parsed Option. + */ + CountOptionsAction(unsigned* buffer_max_) : + buffer_max(buffer_max_) + { + } + + bool perform(Option&) + { + if (*buffer_max == 0x7fffffff) + return false; // overflow protection: don't accept number of options that doesn't fit signed int + ++*buffer_max; + return true; + } +}; + +/** + * @internal + * @brief An Action to pass to Parser::workhorse() that will store each parsed Option in + * appropriate arrays (see Parser::parse()). + */ +class Parser::StoreOptionAction: public Parser::Action +{ + Parser& parser; + Option* options; + Option* buffer; + int bufmax; //! Number of slots in @c buffer. @c -1 means "large enough". +public: + /** + * @brief Creates a new StoreOption action. + * @param parser_ the parser whose op_count should be updated. + * @param options_ each Option @c o is chained into the linked list @c options_[o.desc->index] + * @param buffer_ each Option is appended to this array as long as there's a free slot. + * @param bufmax_ number of slots in @c buffer_. @c -1 means "large enough". + */ + StoreOptionAction(Parser& parser_, Option options_[], Option buffer_[], int bufmax_) : + parser(parser_), options(options_), buffer(buffer_), bufmax(bufmax_) + { + // find first empty slot in buffer (if any) + int bufidx = 0; + while ((bufmax < 0 || bufidx < bufmax) && buffer[bufidx]) + ++bufidx; + + // set parser's optionCount + parser.op_count = bufidx; + } + + bool perform(Option& option) + { + if (bufmax < 0 || parser.op_count < bufmax) + { + if (parser.op_count == 0x7fffffff) + return false; // overflow protection: don't accept number of options that doesn't fit signed int + + buffer[parser.op_count] = option; + int idx = buffer[parser.op_count].desc->index; + if (options[idx]) + options[idx].append(buffer[parser.op_count]); + else + options[idx] = buffer[parser.op_count]; + ++parser.op_count; + } + return true; // NOTE: an option that is discarded because of a full buffer is not fatal + } + + bool finished(int numargs, const char** args) + { + // only overwrite non-option argument list if there's at least 1 + // new non-option argument. Otherwise we keep the old list. This + // makes it easy to use default non-option arguments. + if (numargs > 0) + { + parser.nonop_count = numargs; + parser.nonop_args = args; + } + + return true; + } +}; + +inline void Parser::parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], + Option buffer[], int min_abbr_len, bool single_minus_longopt, int bufmax) +{ + StoreOptionAction action(*this, options, buffer, bufmax); + err = !workhorse(gnu, usage, argc, argv, action, single_minus_longopt, true, min_abbr_len); +} + +inline void Stats::add(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len, + bool single_minus_longopt) +{ + // determine size of options array. This is the greatest index used in the usage + 1 + int i = 0; + while (usage[i].shortopt != 0) + { + if (usage[i].index + 1 >= options_max) + options_max = (usage[i].index + 1) + 1; // 1 more than necessary as sentinel + + ++i; + } + + CountOptionsAction action(&buffer_max); + Parser::workhorse(gnu, usage, argc, argv, action, single_minus_longopt, false, min_abbr_len); +} + +inline bool Parser::workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action, + bool single_minus_longopt, bool print_errors, int min_abbr_len) +{ + // protect against NULL pointer + if (args == 0) + numargs = 0; + + int nonops = 0; + + while (numargs != 0 && *args != 0) + { + const char* param = *args; // param can be --long-option, -srto or non-option argument + + // in POSIX mode the first non-option argument terminates the option list + // a lone minus character is a non-option argument + if (param[0] != '-' || param[1] == 0) + { + if (gnu) + { + ++nonops; + ++args; + if (numargs > 0) + --numargs; + continue; + } + else + break; + } + + // -- terminates the option list. The -- itself is skipped. + if (param[1] == '-' && param[2] == 0) + { + shift(args, nonops); + ++args; + if (numargs > 0) + --numargs; + break; + } + + bool handle_short_options; + const char* longopt_name; + if (param[1] == '-') // if --long-option + { + handle_short_options = false; + longopt_name = param + 2; + } + else + { + handle_short_options = true; + longopt_name = param + 1; //for testing a potential -long-option + } + + bool try_single_minus_longopt = single_minus_longopt; + bool have_more_args = (numargs > 1 || numargs < 0); // is referencing argv[1] valid? + + do // loop over short options in group, for long options the body is executed only once + { + int idx = 0; + + const char* optarg = 0; + + /******************** long option **********************/ + if (handle_short_options == false || try_single_minus_longopt) + { + idx = 0; + while (usage[idx].longopt != 0 && !streq(usage[idx].longopt, longopt_name)) + ++idx; + + if (usage[idx].longopt == 0 && min_abbr_len > 0) // if we should try to match abbreviated long options + { + int i1 = 0; + while (usage[i1].longopt != 0 && !streqabbr(usage[i1].longopt, longopt_name, min_abbr_len)) + ++i1; + if (usage[i1].longopt != 0) + { // now test if the match is unambiguous by checking for another match + int i2 = i1 + 1; + while (usage[i2].longopt != 0 && !streqabbr(usage[i2].longopt, longopt_name, min_abbr_len)) + ++i2; + + if (usage[i2].longopt == 0) // if there was no second match it's unambiguous, so accept i1 as idx + idx = i1; + } + } + + // if we found something, disable handle_short_options (only relevant if single_minus_longopt) + if (usage[idx].longopt != 0) + handle_short_options = false; + + try_single_minus_longopt = false; // prevent looking for longopt in the middle of shortopt group + + optarg = longopt_name; + while (*optarg != 0 && *optarg != '=') + ++optarg; + if (*optarg == '=') // attached argument + ++optarg; + else + // possibly detached argument + optarg = (have_more_args ? args[1] : 0); + } + + /************************ short option ***********************************/ + if (handle_short_options) + { + if (*++param == 0) // point at the 1st/next option character + break; // end of short option group + + idx = 0; + while (usage[idx].shortopt != 0 && !instr(*param, usage[idx].shortopt)) + ++idx; + + if (param[1] == 0) // if the potential argument is separate + optarg = (have_more_args ? args[1] : 0); + else + // if the potential argument is attached + optarg = param + 1; + } + + const Descriptor* descriptor = &usage[idx]; + + if (descriptor->shortopt == 0) /************** unknown option ********************/ + { + // look for dummy entry (shortopt == "" and longopt == "") to use as Descriptor for unknown options + idx = 0; + while (usage[idx].shortopt != 0 && (usage[idx].shortopt[0] != 0 || usage[idx].longopt[0] != 0)) + ++idx; + descriptor = (usage[idx].shortopt == 0 ? 0 : &usage[idx]); + } + + if (descriptor != 0) + { + Option option(descriptor, param, optarg); + switch (descriptor->check_arg(option, print_errors)) + { + case ARG_ILLEGAL: + return false; // fatal + case ARG_OK: + // skip one element of the argument vector, if it's a separated argument + if (optarg != 0 && have_more_args && optarg == args[1]) + { + shift(args, nonops); + if (numargs > 0) + --numargs; + ++args; + } + + // No further short options are possible after an argument + handle_short_options = false; + + break; + case ARG_IGNORE: + case ARG_NONE: + option.arg = 0; + break; + } + + if (!action.perform(option)) + return false; + } + + } while (handle_short_options); + + shift(args, nonops); + ++args; + if (numargs > 0) + --numargs; + + } // while + + if (numargs > 0 && *args == 0) // It's a bug in the caller if numargs is greater than the actual number + numargs = 0; // of arguments, but as a service to the user we fix this if we spot it. + + if (numargs < 0) // if we don't know the number of remaining non-option arguments + { // we need to count them + numargs = 0; + while (args[numargs] != 0) + ++numargs; + } + + return action.finished(numargs + nonops, args - nonops); +} + +/** + * @internal + * @brief The implementation of option::printUsage(). + */ +struct PrintUsageImplementation +{ + /** + * @internal + * @brief Interface for Functors that write (part of) a string somewhere. + */ + struct IStringWriter + { + /** + * @brief Writes the given number of chars beginning at the given pointer somewhere. + */ + virtual void operator()(const char*, int) + { + } + }; + + /** + * @internal + * @brief Encapsulates a function with signature func(string, size) where + * string can be initialized with a const char* and size with an int. + */ + template + struct FunctionWriter: public IStringWriter + { + Function* write; + + virtual void operator()(const char* str, int size) + { + (*write)(str, size); + } + + FunctionWriter(Function* w) : + write(w) + { + } + }; + + /** + * @internal + * @brief Encapsulates a reference to an object with a write(string, size) + * method like that of @c std::ostream. + */ + template + struct OStreamWriter: public IStringWriter + { + OStream& ostream; + + virtual void operator()(const char* str, int size) + { + ostream.write(str, size); + } + + OStreamWriter(OStream& o) : + ostream(o) + { + } + }; + + /** + * @internal + * @brief Like OStreamWriter but encapsulates a @c const reference, which is + * typically a temporary object of a user class. + */ + template + struct TemporaryWriter: public IStringWriter + { + const Temporary& userstream; + + virtual void operator()(const char* str, int size) + { + userstream.write(str, size); + } + + TemporaryWriter(const Temporary& u) : + userstream(u) + { + } + }; + + /** + * @internal + * @brief Encapsulates a function with the signature func(fd, string, size) (the + * signature of the @c write() system call) + * where fd can be initialized from an int, string from a const char* and size from an int. + */ + template + struct SyscallWriter: public IStringWriter + { + Syscall* write; + int fd; + + virtual void operator()(const char* str, int size) + { + (*write)(fd, str, size); + } + + SyscallWriter(Syscall* w, int f) : + write(w), fd(f) + { + } + }; + + /** + * @internal + * @brief Encapsulates a function with the same signature as @c std::fwrite(). + */ + template + struct StreamWriter: public IStringWriter + { + Function* fwrite; + Stream* stream; + + virtual void operator()(const char* str, int size) + { + (*fwrite)(str, size, 1, stream); + } + + StreamWriter(Function* w, Stream* s) : + fwrite(w), stream(s) + { + } + }; + + /** + * @internal + * @brief Sets i1 = max(i1, i2) + */ + static void upmax(int& i1, int i2) + { + i1 = (i1 >= i2 ? i1 : i2); + } + + /** + * @internal + * @brief Moves the "cursor" to column @c want_x assuming it is currently at column @c x + * and sets @c x=want_x . + * If x > want_x , a line break is output before indenting. + * + * @param write Spaces and possibly a line break are written via this functor to get + * the desired indentation @c want_x . + * @param[in,out] x the current indentation. Set to @c want_x by this method. + * @param want_x the desired indentation. + */ + static void indent(IStringWriter& write, int& x, int want_x) + { + int indent = want_x - x; + if (indent < 0) + { + write("\n", 1); + indent = want_x; + } + + if (indent > 0) + { + char space = ' '; + for (int i = 0; i < indent; ++i) + write(&space, 1); + x = want_x; + } + } + + /** + * @brief Returns true if ch is the unicode code point of a wide character. + * + * @note + * The following character ranges are treated as wide + * @code + * 1100..115F + * 2329..232A (just 2 characters!) + * 2E80..A4C6 except for 303F + * A960..A97C + * AC00..D7FB + * F900..FAFF + * FE10..FE6B + * FF01..FF60 + * FFE0..FFE6 + * 1B000...... + * @endcode + */ + static bool isWideChar(unsigned ch) + { + if (ch == 0x303F) + return false; + + return ((0x1100 <= ch && ch <= 0x115F) || (0x2329 <= ch && ch <= 0x232A) || (0x2E80 <= ch && ch <= 0xA4C6) + || (0xA960 <= ch && ch <= 0xA97C) || (0xAC00 <= ch && ch <= 0xD7FB) || (0xF900 <= ch && ch <= 0xFAFF) + || (0xFE10 <= ch && ch <= 0xFE6B) || (0xFF01 <= ch && ch <= 0xFF60) || (0xFFE0 <= ch && ch <= 0xFFE6) + || (0x1B000 <= ch)); + } + + /** + * @internal + * @brief Splits a @c Descriptor[] array into tables, rows, lines and columns and + * iterates over these components. + * + * The top-level organizational unit is the @e table. + * A table begins at a Descriptor with @c help!=NULL and extends up to + * a Descriptor with @c help==NULL. + * + * A table consists of @e rows. Due to line-wrapping and explicit breaks + * a row may take multiple lines on screen. Rows within the table are separated + * by \\n. They never cross Descriptor boundaries. This means a row ends either + * at \\n or the 0 at the end of the help string. + * + * A row consists of columns/cells. Columns/cells within a row are separated by \\t. + * Line breaks within a cell are marked by \\v. + * + * Rows in the same table need not have the same number of columns/cells. The + * extreme case are interjections, which are rows that contain neither \\t nor \\v. + * These are NOT treated specially by LinePartIterator, but they are treated + * specially by printUsage(). + * + * LinePartIterator iterates through the usage at 3 levels: table, row and part. + * Tables and rows are as described above. A @e part is a line within a cell. + * LinePartIterator iterates through 1st parts of all cells, then through the 2nd + * parts of all cells (if any),... @n + * Example: The row "1 \v 3 \t 2 \v 4" has 2 cells/columns and 4 parts. + * The parts will be returned in the order 1, 2, 3, 4. + * + * It is possible that some cells have fewer parts than others. In this case + * LinePartIterator will "fill up" these cells with 0-length parts. IOW, LinePartIterator + * always returns the same number of parts for each column. Note that this is different + * from the way rows and columns are handled. LinePartIterator does @e not guarantee that + * the same number of columns will be returned for each row. + * + */ + class LinePartIterator + { + const Descriptor* tablestart; //!< The 1st descriptor of the current table. + const Descriptor* rowdesc; //!< The Descriptor that contains the current row. + const char* rowstart; //!< Ptr to 1st character of current row within rowdesc->help. + const char* ptr; //!< Ptr to current part within the current row. + int col; //!< Index of current column. + int len; //!< Length of the current part (that ptr points at) in BYTES + int screenlen; //!< Length of the current part in screen columns (taking narrow/wide chars into account). + int max_line_in_block; //!< Greatest index of a line within the block. This is the number of \\v within the cell with the most \\vs. + int line_in_block; //!< Line index within the current cell of the current part. + int target_line_in_block; //!< Line index of the parts we should return to the user on this iteration. + bool hit_target_line; //!< Flag whether we encountered a part with line index target_line_in_block in the current cell. + + /** + * @brief Determines the byte and character lengths of the part at @ref ptr and + * stores them in @ref len and @ref screenlen respectively. + */ + void update_length() + { + screenlen = 0; + for (len = 0; ptr[len] != 0 && ptr[len] != '\v' && ptr[len] != '\t' && ptr[len] != '\n'; ++len) + { + ++screenlen; + unsigned ch = (unsigned char) ptr[len]; + if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte + { + // int __builtin_clz (unsigned int x) + // Returns the number of leading 0-bits in x, starting at the most significant bit + unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); + ch = ch & mask; // mask out length bits, we don't verify their correctness + while (((unsigned char) ptr[len + 1] ^ 0x80) <= 0x3F) // while next byte is continuation byte + { + ch = (ch << 6) ^ (unsigned char) ptr[len + 1] ^ 0x80; // add continuation to char code + ++len; + } + // ch is the decoded unicode code point + if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case + ++screenlen; + } + } + } + + public: + //! @brief Creates an iterator for @c usage. + LinePartIterator(const Descriptor usage[]) : + tablestart(usage), rowdesc(0), rowstart(0), ptr(0), col(-1), len(0), max_line_in_block(0), line_in_block(0), + target_line_in_block(0), hit_target_line(true) + { + } + + /** + * @brief Moves iteration to the next table (if any). Has to be called once on a new + * LinePartIterator to move to the 1st table. + * @retval false if moving to next table failed because no further table exists. + */ + bool nextTable() + { + // If this is NOT the first time nextTable() is called after the constructor, + // then skip to the next table break (i.e. a Descriptor with help == 0) + if (rowdesc != 0) + { + while (tablestart->help != 0 && tablestart->shortopt != 0) + ++tablestart; + } + + // Find the next table after the break (if any) + while (tablestart->help == 0 && tablestart->shortopt != 0) + ++tablestart; + + restartTable(); + return rowstart != 0; + } + + /** + * @brief Reset iteration to the beginning of the current table. + */ + void restartTable() + { + rowdesc = tablestart; + rowstart = tablestart->help; + ptr = 0; + } + + /** + * @brief Moves iteration to the next row (if any). Has to be called once after each call to + * @ref nextTable() to move to the 1st row of the table. + * @retval false if moving to next row failed because no further row exists. + */ + bool nextRow() + { + if (ptr == 0) + { + restartRow(); + return rowstart != 0; + } + + while (*ptr != 0 && *ptr != '\n') + ++ptr; + + if (*ptr == 0) + { + if ((rowdesc + 1)->help == 0) // table break + return false; + + ++rowdesc; + rowstart = rowdesc->help; + } + else // if (*ptr == '\n') + { + rowstart = ptr + 1; + } + + restartRow(); + return true; + } + + /** + * @brief Reset iteration to the beginning of the current row. + */ + void restartRow() + { + ptr = rowstart; + col = -1; + len = 0; + screenlen = 0; + max_line_in_block = 0; + line_in_block = 0; + target_line_in_block = 0; + hit_target_line = true; + } + + /** + * @brief Moves iteration to the next part (if any). Has to be called once after each call to + * @ref nextRow() to move to the 1st part of the row. + * @retval false if moving to next part failed because no further part exists. + * + * See @ref LinePartIterator for details about the iteration. + */ + bool next() + { + if (ptr == 0) + return false; + + if (col == -1) + { + col = 0; + update_length(); + return true; + } + + ptr += len; + while (true) + { + switch (*ptr) + { + case '\v': + upmax(max_line_in_block, ++line_in_block); + ++ptr; + break; + case '\t': + if (!hit_target_line) // if previous column did not have the targetline + { // then "insert" a 0-length part + update_length(); + hit_target_line = true; + return true; + } + + hit_target_line = false; + line_in_block = 0; + ++col; + ++ptr; + break; + case 0: + case '\n': + if (!hit_target_line) // if previous column did not have the targetline + { // then "insert" a 0-length part + update_length(); + hit_target_line = true; + return true; + } + + if (++target_line_in_block > max_line_in_block) + { + update_length(); + return false; + } + + hit_target_line = false; + line_in_block = 0; + col = 0; + ptr = rowstart; + continue; + default: + ++ptr; + continue; + } // switch + + if (line_in_block == target_line_in_block) + { + update_length(); + hit_target_line = true; + return true; + } + } // while + } + + /** + * @brief Returns the index (counting from 0) of the column in which + * the part pointed to by @ref data() is located. + */ + int column() + { + return col; + } + + /** + * @brief Returns the index (counting from 0) of the line within the current column + * this part belongs to. + */ + int line() + { + return target_line_in_block; // NOT line_in_block !!! It would be wrong if !hit_target_line + } + + /** + * @brief Returns the length of the part pointed to by @ref data() in raw chars (not UTF-8 characters). + */ + int length() + { + return len; + } + + /** + * @brief Returns the width in screen columns of the part pointed to by @ref data(). + * Takes multi-byte UTF-8 sequences and wide characters into account. + */ + int screenLength() + { + return screenlen; + } + + /** + * @brief Returns the current part of the iteration. + */ + const char* data() + { + return ptr; + } + }; + + /** + * @internal + * @brief Takes input and line wraps it, writing out one line at a time so that + * it can be interleaved with output from other columns. + * + * The LineWrapper is used to handle the last column of each table as well as interjections. + * The LineWrapper is called once for each line of output. If the data given to it fits + * into the designated width of the last column it is simply written out. If there + * is too much data, an appropriate split point is located and only the data up to this + * split point is written out. The rest of the data is queued for the next line. + * That way the last column can be line wrapped and interleaved with data from + * other columns. The following example makes this clearer: + * @code + * Column 1,1 Column 2,1 This is a long text + * Column 1,2 Column 2,2 that does not fit into + * a single line. + * @endcode + * + * The difficulty in producing this output is that the whole string + * "This is a long text that does not fit into a single line" is the + * 1st and only part of column 3. In order to produce the above + * output the string must be output piecemeal, interleaved with + * the data from the other columns. + */ + class LineWrapper + { + static const int bufmask = 15; //!< Must be a power of 2 minus 1. + /** + * @brief Ring buffer for length component of pair (data, length). + */ + int lenbuf[bufmask + 1]; + /** + * @brief Ring buffer for data component of pair (data, length). + */ + const char* datbuf[bufmask + 1]; + /** + * @brief The indentation of the column to which the LineBuffer outputs. LineBuffer + * assumes that the indentation has already been written when @ref process() + * is called, so this value is only used when a buffer flush requires writing + * additional lines of output. + */ + int x; + /** + * @brief The width of the column to line wrap. + */ + int width; + int head; //!< @brief index for next write + int tail; //!< @brief index for next read - 1 (i.e. increment tail BEFORE read) + + /** + * @brief Multiple methods of LineWrapper may decide to flush part of the buffer to + * free up space. The contract of process() says that only 1 line is output. So + * this variable is used to track whether something has output a line. It is + * reset at the beginning of process() and checked at the end to decide if + * output has already occurred or is still needed. + */ + bool wrote_something; + + bool buf_empty() + { + return ((tail + 1) & bufmask) == head; + } + + bool buf_full() + { + return tail == head; + } + + void buf_store(const char* data, int len) + { + lenbuf[head] = len; + datbuf[head] = data; + head = (head + 1) & bufmask; + } + + //! @brief Call BEFORE reading ...buf[tail]. + void buf_next() + { + tail = (tail + 1) & bufmask; + } + + /** + * @brief Writes (data,len) into the ring buffer. If the buffer is full, a single line + * is flushed out of the buffer into @c write. + */ + void output(IStringWriter& write, const char* data, int len) + { + if (buf_full()) + write_one_line(write); + + buf_store(data, len); + } + + /** + * @brief Writes a single line of output from the buffer to @c write. + */ + void write_one_line(IStringWriter& write) + { + if (wrote_something) // if we already wrote something, we need to start a new line + { + write("\n", 1); + int _ = 0; + indent(write, _, x); + } + + if (!buf_empty()) + { + buf_next(); + write(datbuf[tail], lenbuf[tail]); + } + + wrote_something = true; + } + public: + + /** + * @brief Writes out all remaining data from the LineWrapper using @c write. + * Unlike @ref process() this method indents all lines including the first and + * will output a \\n at the end (but only if something has been written). + */ + void flush(IStringWriter& write) + { + if (buf_empty()) + return; + int _ = 0; + indent(write, _, x); + wrote_something = false; + while (!buf_empty()) + write_one_line(write); + write("\n", 1); + } + + /** + * @brief Process, wrap and output the next piece of data. + * + * process() will output at least one line of output. This is not necessarily + * the @c data passed in. It may be data queued from a prior call to process(). + * If the internal buffer is full, more than 1 line will be output. + * + * process() assumes that the a proper amount of indentation has already been + * output. It won't write any further indentation before the 1st line. If + * more than 1 line is written due to buffer constraints, the lines following + * the first will be indented by this method, though. + * + * No \\n is written by this method after the last line that is written. + * + * @param write where to write the data. + * @param data the new chunk of data to write. + * @param len the length of the chunk of data to write. + */ + void process(IStringWriter& write, const char* data, int len) + { + wrote_something = false; + + while (len > 0) + { + if (len <= width) // quick test that works because utf8width <= len (all wide chars have at least 2 bytes) + { + output(write, data, len); + len = 0; + } + else // if (len > width) it's possible (but not guaranteed) that utf8len > width + { + int utf8width = 0; + int maxi = 0; + while (maxi < len && utf8width < width) + { + int charbytes = 1; + unsigned ch = (unsigned char) data[maxi]; + if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte + { + // int __builtin_clz (unsigned int x) + // Returns the number of leading 0-bits in x, starting at the most significant bit + unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); + ch = ch & mask; // mask out length bits, we don't verify their correctness + while ((maxi + charbytes < len) && // + (((unsigned char) data[maxi + charbytes] ^ 0x80) <= 0x3F)) // while next byte is continuation byte + { + ch = (ch << 6) ^ (unsigned char) data[maxi + charbytes] ^ 0x80; // add continuation to char code + ++charbytes; + } + // ch is the decoded unicode code point + if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case + { + if (utf8width + 2 > width) + break; + ++utf8width; + } + } + ++utf8width; + maxi += charbytes; + } + + // data[maxi-1] is the last byte of the UTF-8 sequence of the last character that fits + // onto the 1st line. If maxi == len, all characters fit on the line. + + if (maxi == len) + { + output(write, data, len); + len = 0; + } + else // if (maxi < len) at least 1 character (data[maxi] that is) doesn't fit on the line + { + int i; + for (i = maxi; i >= 0; --i) + if (data[i] == ' ') + break; + + if (i >= 0) + { + output(write, data, i); + data += i + 1; + len -= i + 1; + } + else // did not find a space to split at => split before data[maxi] + { // data[maxi] is always the beginning of a character, never a continuation byte + output(write, data, maxi); + data += maxi; + len -= maxi; + } + } + } + } + if (!wrote_something) // if we didn't already write something to make space in the buffer + write_one_line(write); // write at most one line of actual output + } + + /** + * @brief Constructs a LineWrapper that wraps its output to fit into + * screen columns @c x1 (incl.) to @c x2 (excl.). + * + * @c x1 gives the indentation LineWrapper uses if it needs to indent. + */ + LineWrapper(int x1, int x2) : + x(x1), width(x2 - x1), head(0), tail(bufmask) + { + if (width < 2) // because of wide characters we need at least width 2 or the code breaks + width = 2; + } + }; + + /** + * @internal + * @brief This is the implementation that is shared between all printUsage() templates. + * Because all printUsage() templates share this implementation, there is no template bloat. + */ + static void printUsage(IStringWriter& write, const Descriptor usage[], int width = 80, // + int last_column_min_percent = 50, int last_column_own_line_max_percent = 75) + { + if (width < 1) // protect against nonsense values + width = 80; + + if (width > 10000) // protect against overflow in the following computation + width = 10000; + + int last_column_min_width = ((width * last_column_min_percent) + 50) / 100; + int last_column_own_line_max_width = ((width * last_column_own_line_max_percent) + 50) / 100; + if (last_column_own_line_max_width == 0) + last_column_own_line_max_width = 1; + + LinePartIterator part(usage); + while (part.nextTable()) + { + + /***************** Determine column widths *******************************/ + + const int maxcolumns = 8; // 8 columns are enough for everyone + int col_width[maxcolumns]; + int lastcolumn; + int leftwidth; + int overlong_column_threshold = 10000; + do + { + lastcolumn = 0; + for (int i = 0; i < maxcolumns; ++i) + col_width[i] = 0; + + part.restartTable(); + while (part.nextRow()) + { + while (part.next()) + { + if (part.column() < maxcolumns) + { + upmax(lastcolumn, part.column()); + if (part.screenLength() < overlong_column_threshold) + // We don't let rows that don't use table separators (\t or \v) influence + // the width of column 0. This allows the user to interject section headers + // or explanatory paragraphs that do not participate in the table layout. + if (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' + || part.data()[part.length()] == '\v') + upmax(col_width[part.column()], part.screenLength()); + } + } + } + + /* + * If the last column doesn't fit on the same + * line as the other columns, we can fix that by starting it on its own line. + * However we can't do this for any of the columns 0..lastcolumn-1. + * If their sum exceeds the maximum width we try to fix this by iteratively + * ignoring the widest line parts in the width determination until + * we arrive at a series of column widths that fit into one line. + * The result is a layout where everything is nicely formatted + * except for a few overlong fragments. + * */ + + leftwidth = 0; + overlong_column_threshold = 0; + for (int i = 0; i < lastcolumn; ++i) + { + leftwidth += col_width[i]; + upmax(overlong_column_threshold, col_width[i]); + } + + } while (leftwidth > width); + + /**************** Determine tab stops and last column handling **********************/ + + int tabstop[maxcolumns]; + tabstop[0] = 0; + for (int i = 1; i < maxcolumns; ++i) + tabstop[i] = tabstop[i - 1] + col_width[i - 1]; + + int rightwidth = width - tabstop[lastcolumn]; + bool print_last_column_on_own_line = false; + if (rightwidth < last_column_min_width && rightwidth < col_width[lastcolumn]) + { + print_last_column_on_own_line = true; + rightwidth = last_column_own_line_max_width; + } + + // If lastcolumn == 0 we must disable print_last_column_on_own_line because + // otherwise 2 copies of the last (and only) column would be output. + // Actually this is just defensive programming. It is currently not + // possible that lastcolumn==0 and print_last_column_on_own_line==true + // at the same time, because lastcolumn==0 => tabstop[lastcolumn] == 0 => + // rightwidth==width => rightwidth>=last_column_min_width (unless someone passes + // a bullshit value >100 for last_column_min_percent) => the above if condition + // is false => print_last_column_on_own_line==false + if (lastcolumn == 0) + print_last_column_on_own_line = false; + + LineWrapper lastColumnLineWrapper(width - rightwidth, width); + LineWrapper interjectionLineWrapper(0, width); + + part.restartTable(); + + /***************** Print out all rows of the table *************************************/ + + while (part.nextRow()) + { + int x = -1; + while (part.next()) + { + if (part.column() > lastcolumn) + continue; // drop excess columns (can happen if lastcolumn == maxcolumns-1) + + if (part.column() == 0) + { + if (x >= 0) + write("\n", 1); + x = 0; + } + + indent(write, x, tabstop[part.column()]); + + if ((part.column() < lastcolumn) + && (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' + || part.data()[part.length()] == '\v')) + { + write(part.data(), part.length()); + x += part.screenLength(); + } + else // either part.column() == lastcolumn or we are in the special case of + // an interjection that doesn't contain \v or \t + { + // NOTE: This code block is not necessarily executed for + // each line, because some rows may have fewer columns. + + LineWrapper& lineWrapper = (part.column() == 0) ? interjectionLineWrapper : lastColumnLineWrapper; + + if (!print_last_column_on_own_line) + lineWrapper.process(write, part.data(), part.length()); + } + } // while + + if (print_last_column_on_own_line) + { + part.restartRow(); + while (part.next()) + { + if (part.column() == lastcolumn) + { + write("\n", 1); + int _ = 0; + indent(write, _, width - rightwidth); + lastColumnLineWrapper.process(write, part.data(), part.length()); + } + } + } + + write("\n", 1); + lastColumnLineWrapper.flush(write); + interjectionLineWrapper.flush(write); + } + } + } + +} +; + +/** + * @brief Outputs a nicely formatted usage string with support for multi-column formatting + * and line-wrapping. + * + * printUsage() takes the @c help texts of a Descriptor[] array and formats them into + * a usage message, wrapping lines to achieve the desired output width. + * + * Table formatting: + * + * Aside from plain strings which are simply line-wrapped, the usage may contain tables. Tables + * are used to align elements in the output. + * + * @code + * // Without a table. The explanatory texts are not aligned. + * -c, --create |Creates something. + * -k, --kill |Destroys something. + * + * // With table formatting. The explanatory texts are aligned. + * -c, --create |Creates something. + * -k, --kill |Destroys something. + * @endcode + * + * Table formatting removes the need to pad help texts manually with spaces to achieve + * alignment. To create a table, simply insert \\t (tab) characters to separate the cells + * within a row. + * + * @code + * const option::Descriptor usage[] = { + * {..., "-c, --create \tCreates something." }, + * {..., "-k, --kill \tDestroys something." }, ... + * @endcode + * + * Note that you must include the minimum amount of space desired between cells yourself. + * Table formatting will insert further spaces as needed to achieve alignment. + * + * You can insert line breaks within cells by using \\v (vertical tab). + * + * @code + * const option::Descriptor usage[] = { + * {..., "-c,\v--create \tCreates\vsomething." }, + * {..., "-k,\v--kill \tDestroys\vsomething." }, ... + * + * // results in + * + * -c, Creates + * --create something. + * -k, Destroys + * --kill something. + * @endcode + * + * You can mix lines that do not use \\t or \\v with those that do. The plain + * lines will not mess up the table layout. Alignment of the table columns will + * be maintained even across these interjections. + * + * @code + * const option::Descriptor usage[] = { + * {..., "-c, --create \tCreates something." }, + * {..., "----------------------------------" }, + * {..., "-k, --kill \tDestroys something." }, ... + * + * // results in + * + * -c, --create Creates something. + * ---------------------------------- + * -k, --kill Destroys something. + * @endcode + * + * You can have multiple tables within the same usage whose columns are + * aligned independently. Simply insert a dummy Descriptor with @c help==0. + * + * @code + * const option::Descriptor usage[] = { + * {..., "Long options:" }, + * {..., "--very-long-option \tDoes something long." }, + * {..., "--ultra-super-mega-long-option \tTakes forever to complete." }, + * {..., 0 }, // ---------- table break ----------- + * {..., "Short options:" }, + * {..., "-s \tShort." }, + * {..., "-q \tQuick." }, ... + * + * // results in + * + * Long options: + * --very-long-option Does something long. + * --ultra-super-mega-long-option Takes forever to complete. + * Short options: + * -s Short. + * -q Quick. + * + * // Without the table break it would be + * + * Long options: + * --very-long-option Does something long. + * --ultra-super-mega-long-option Takes forever to complete. + * Short options: + * -s Short. + * -q Quick. + * @endcode + * + * Output methods: + * + * Because TheLeanMeanC++Option parser is freestanding, you have to provide the means for + * output in the first argument(s) to printUsage(). Because printUsage() is implemented as + * a set of template functions, you have great flexibility in your choice of output + * method. The following example demonstrates typical uses. Anything that's similar enough + * will work. + * + * @code + * #include // write() + * #include // cout + * #include // ostringstream + * #include // fwrite() + * using namespace std; + * + * void my_write(const char* str, int size) { + * fwrite(str, size, 1, stdout); + * } + * + * struct MyWriter { + * void write(const char* buf, size_t size) const { + * fwrite(str, size, 1, stdout); + * } + * }; + * + * struct MyWriteFunctor { + * void operator()(const char* buf, size_t size) { + * fwrite(str, size, 1, stdout); + * } + * }; + * ... + * printUsage(my_write, usage); // custom write function + * printUsage(MyWriter(), usage); // temporary of a custom class + * MyWriter writer; + * printUsage(writer, usage); // custom class object + * MyWriteFunctor wfunctor; + * printUsage(&wfunctor, usage); // custom functor + * printUsage(write, 1, usage); // write() to file descriptor 1 + * printUsage(cout, usage); // an ostream& + * printUsage(fwrite, stdout, usage); // fwrite() to stdout + * ostringstream sstr; + * printUsage(sstr, usage); // an ostringstream& + * + * @endcode + * + * @par Notes: + * @li the @c write() method of a class that is to be passed as a temporary + * as @c MyWriter() is in the example, must be a @c const method, because + * temporary objects are passed as const reference. This only applies to + * temporary objects that are created and destroyed in the same statement. + * If you create an object like @c writer in the example, this restriction + * does not apply. + * @li a functor like @c MyWriteFunctor in the example must be passed as a pointer. + * This differs from the way functors are passed to e.g. the STL algorithms. + * @li All printUsage() templates are tiny wrappers around a shared non-template implementation. + * So there's no penalty for using different versions in the same program. + * @li printUsage() always interprets Descriptor::help as UTF-8 and always produces UTF-8-encoded + * output. If your system uses a different charset, you must do your own conversion. You + * may also need to change the font of the console to see non-ASCII characters properly. + * This is particularly true for Windows. + * @li @b Security @b warning: Do not insert untrusted strings (such as user-supplied arguments) + * into the usage. printUsage() has no protection against malicious UTF-8 sequences. + * + * @param prn The output method to use. See the examples above. + * @param usage the Descriptor[] array whose @c help texts will be formatted. + * @param width the maximum number of characters per output line. Note that this number is + * in actual characters, not bytes. printUsage() supports UTF-8 in @c help and will + * count multi-byte UTF-8 sequences properly. Asian wide characters are counted + * as 2 characters. + * @param last_column_min_percent (0-100) The minimum percentage of @c width that should be available + * for the last column (which typically contains the textual explanation of an option). + * If less space is available, the last column will be printed on its own line, indented + * according to @c last_column_own_line_max_percent. + * @param last_column_own_line_max_percent (0-100) If the last column is printed on its own line due to + * less than @c last_column_min_percent of the width being available, then only + * @c last_column_own_line_max_percent of the extra line(s) will be used for the + * last column's text. This ensures an indentation. See example below. + * + * @code + * // width=20, last_column_min_percent=50 (i.e. last col. min. width=10) + * --3456789 1234567890 + * 1234567890 + * + * // width=20, last_column_min_percent=75 (i.e. last col. min. width=15) + * // last_column_own_line_max_percent=75 + * --3456789 + * 123456789012345 + * 67890 + * + * // width=20, last_column_min_percent=75 (i.e. last col. min. width=15) + * // last_column_own_line_max_percent=33 (i.e. max. 5) + * --3456789 + * 12345 + * 67890 + * 12345 + * 67890 + * @endcode + */ +template +void printUsage(OStream& prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, + int last_column_own_line_max_percent = 75) +{ + PrintUsageImplementation::OStreamWriter write(prn); + PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); +} + +template +void printUsage(Function* prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, + int last_column_own_line_max_percent = 75) +{ + PrintUsageImplementation::FunctionWriter write(prn); + PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); +} + +template +void printUsage(const Temporary& prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, + int last_column_own_line_max_percent = 75) +{ + PrintUsageImplementation::TemporaryWriter write(prn); + PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); +} + +template +void printUsage(Syscall* prn, int fd, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, + int last_column_own_line_max_percent = 75) +{ + PrintUsageImplementation::SyscallWriter write(prn, fd); + PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); +} + +template +void printUsage(Function* prn, Stream* stream, const Descriptor usage[], int width = 80, int last_column_min_percent = + 50, + int last_column_own_line_max_percent = 75) +{ + PrintUsageImplementation::StreamWriter write(prn, stream); + PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); +} + +} +// namespace option + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif /* OPTIONPARSER_H_ */ diff --git a/vendor/bandit/bandit/failure_formatters/default_failure_formatter.h b/vendor/bandit/bandit/failure_formatters/default_failure_formatter.h new file mode 100644 index 00000000..48cc9021 --- /dev/null +++ b/vendor/bandit/bandit/failure_formatters/default_failure_formatter.h @@ -0,0 +1,30 @@ +#ifndef BANDIT_DEFAULT_FAILURE_FORMATTER_H +#define BANDIT_DEFAULT_FAILURE_FORMATTER_H + +namespace bandit { namespace detail { + + struct default_failure_formatter : public failure_formatter + { + std::string format(const assertion_exception& err) const + { + std::stringstream ss; + if(err.file_name().size()) + { + ss << err.file_name(); + + if(err.line_number()) + { + ss << ":" << err.line_number(); + } + + ss << ": "; + } + + ss << err.what(); + + return ss.str(); + } + }; +}} + +#endif diff --git a/vendor/bandit/bandit/failure_formatters/failure_formatter.h b/vendor/bandit/bandit/failure_formatters/failure_formatter.h new file mode 100644 index 00000000..486624f0 --- /dev/null +++ b/vendor/bandit/bandit/failure_formatters/failure_formatter.h @@ -0,0 +1,13 @@ +#ifndef BANDIT_FAILURE_FORMATTER_H +#define BANDIT_FAILURE_FORMATTER_H + +namespace bandit { namespace detail { + + struct failure_formatter + { + virtual std::string format(const assertion_exception&) const = 0; + }; + typedef std::unique_ptr failure_formatter_ptr; +}} + +#endif diff --git a/vendor/bandit/bandit/failure_formatters/failure_formatters.h b/vendor/bandit/bandit/failure_formatters/failure_formatters.h new file mode 100644 index 00000000..d0914651 --- /dev/null +++ b/vendor/bandit/bandit/failure_formatters/failure_formatters.h @@ -0,0 +1,16 @@ +#ifndef BANDIT_FAILURE_FORMATTERS +#define BANDIT_FAILURE_FORMATTERS + +#include "failure_formatter.h" +#include "default_failure_formatter.h" +#include "visual_studio_failure_formatter.h" + +namespace bandit { namespace detail { + inline failure_formatter& registered_failure_formatter() + { + static default_failure_formatter formatter; + return formatter; + } +}} + +#endif diff --git a/vendor/bandit/bandit/failure_formatters/visual_studio_failure_formatter.h b/vendor/bandit/bandit/failure_formatters/visual_studio_failure_formatter.h new file mode 100644 index 00000000..6ff3fdeb --- /dev/null +++ b/vendor/bandit/bandit/failure_formatters/visual_studio_failure_formatter.h @@ -0,0 +1,36 @@ +#ifndef BANDIT_VISUAL_STUDIO_FAILURE_FORMATTER_H +#define BANDIT_VISUAL_STUDIO_FAILURE_FORMATTER_H + +namespace bandit { namespace detail { + + struct visual_studio_failure_formatter : public failure_formatter + { + std::string format(const assertion_exception& err) const + { + std::stringstream ss; + if(err.file_name().size()) + { + ss << err.file_name(); + + if(err.line_number()) + { + ss << "(" << err.line_number() << ")"; + } + + ss << ": "; + } + else + { + ss << "bandit: "; + } + + ss << err.what(); + + return ss.str(); + + } + }; + +}} + +#endif diff --git a/vendor/bandit/bandit/grammar.h b/vendor/bandit/bandit/grammar.h new file mode 100644 index 00000000..6aaec23d --- /dev/null +++ b/vendor/bandit/bandit/grammar.h @@ -0,0 +1,145 @@ +#ifndef BANDIT_GRAMMAR_H +#define BANDIT_GRAMMAR_H + +namespace bandit { + + inline void describe(const char* desc, detail::voidfunc_t func, + detail::listener& listener, detail::contextstack_t& context_stack, + bool hard_skip = false) + { + listener.context_starting(desc); + + context_stack.back()->execution_is_starting(); + + detail::bandit_context ctxt(desc, hard_skip); + + context_stack.push_back(&ctxt); + try + { + func(); + } + catch(const bandit::detail::test_run_error& error) + { + listener.test_run_error(desc, error); + } + + context_stack.pop_back(); + + listener.context_ended(desc); + } + + inline void describe(const char* desc, detail::voidfunc_t func) + { + describe(desc, func, detail::registered_listener(), detail::context_stack()); + } + + inline void describe_skip(const char* desc, detail::voidfunc_t func, + detail::listener& listener, detail::contextstack_t& context_stack) + { + bool skip = true; + describe(desc, func, listener, context_stack, skip); + } + + inline void describe_skip(const char* desc, detail::voidfunc_t func) + { + describe_skip(desc, func, detail::registered_listener(), + detail::context_stack()); + } + + inline void before_each(detail::voidfunc_t func, + detail::contextstack_t& context_stack) + { + context_stack.back()->register_before_each(func); + } + + inline void before_each(detail::voidfunc_t func) + { + before_each(func, detail::context_stack()); + } + + inline void after_each(detail::voidfunc_t func, + detail::contextstack_t& context_stack) + { + context_stack.back()->register_after_each(func); + } + + inline void after_each(detail::voidfunc_t func) + { + after_each(func, detail::context_stack()); + } + + inline void it_skip(const char* desc, detail::voidfunc_t, detail::listener& listener) + { + listener.it_skip(desc); + } + + inline void it_skip(const char* desc, detail::voidfunc_t func) + { + it_skip(desc, func, detail::registered_listener()); + } + + inline void it(const char* desc, detail::voidfunc_t func, detail::listener& listener, + detail::contextstack_t& context_stack, + bandit::adapters::assertion_adapter& assertion_adapter, + const detail::run_policy& run_policy) + { + if(!run_policy.should_run(desc, context_stack)) + { + it_skip(desc, func, listener); + return; + } + + listener.it_starting(desc); + + context_stack.back()->execution_is_starting(); + + auto run_before_eaches = [&](){ + for_each(context_stack.begin(), context_stack.end(), [](detail::context* ctxt){ + ctxt->run_before_eaches(); + }); + }; + + auto run_after_eaches = [&](){ + for_each(context_stack.begin(), context_stack.end(), [](detail::context* ctxt){ + ctxt->run_after_eaches(); + }); + }; + + try + { + assertion_adapter.adapt_exceptions([&](){ + run_before_eaches(); + + func(); + listener.it_succeeded(desc); + }); + } + catch(const bandit::detail::assertion_exception& ex) + { + listener.it_failed(desc, ex); + } + catch(...) + { + listener.it_unknown_error(desc); + } + + try + { + run_after_eaches(); + } + catch(...) + { + listener.it_unknown_error(desc); + } + } + + inline void it(const char* desc, detail::voidfunc_t func) + { + it(desc, func, detail::registered_listener(), detail::context_stack(), + detail::registered_adapter(), detail::registered_run_policy()); + } + + +} + +#endif diff --git a/vendor/bandit/bandit/listener.h b/vendor/bandit/bandit/listener.h new file mode 100644 index 00000000..07501fcf --- /dev/null +++ b/vendor/bandit/bandit/listener.h @@ -0,0 +1,27 @@ +#ifndef BANDIT_LISTENER_H +#define BANDIT_LISTENER_H + +namespace bandit { namespace detail { + struct listener + { + virtual ~listener() {} + + virtual void test_run_starting() = 0; + virtual void test_run_complete() = 0; + + virtual void context_starting(const char* desc) = 0; + virtual void context_ended(const char* desc) = 0; + virtual void test_run_error(const char* desc, const test_run_error& error) = 0; + + virtual void it_starting(const char* desc) = 0; + virtual void it_succeeded(const char* desc) = 0; + virtual void it_failed(const char* desc, const detail::assertion_exception& ex) = 0; + virtual void it_unknown_error(const char* desc) = 0; + virtual void it_skip(const char* desc) = 0; + + virtual bool did_we_pass() const = 0; + }; + typedef std::unique_ptr listener_ptr; +}} + +#endif diff --git a/vendor/bandit/bandit/options.h b/vendor/bandit/bandit/options.h new file mode 100644 index 00000000..d07c1e5b --- /dev/null +++ b/vendor/bandit/bandit/options.h @@ -0,0 +1,105 @@ +#ifndef BANDIT_OPTIONS_H +#define BANDIT_OPTIONS_H + +namespace bandit { namespace detail { + + // TODO: print any unknown options + // TODO: check for parser errors + struct options + { + + options(int argc, char* argv[]) + { + argc -= (argc>0); argv += (argc>0); // Skip program name (argv[0]) if present + option::Stats stats(usage(), argc, argv); + options_.resize(stats.options_max); + std::vector buffer(stats.buffer_max); + option::Parser parse(usage(), argc, argv, options_.data(), buffer.data()); + parsed_ok_ = !parse.error(); + } + + bool help() const + { + return options_[HELP] != NULL; + } + + void print_usage() const + { + option::printUsage(std::cout, usage()); + } + + bool version() const + { + return options_[VERSION] != NULL; + } + + const char* reporter() const + { + return options_[REPORTER].arg; + } + + bool no_color() const + { + return options_[NO_COLOR] != NULL; + } + + typedef enum + { + FORMATTER_DEFAULT, + FORMATTER_VS, + FORMATTER_UNKNOWN + } formatters; + + formatters formatter() const + { + std::string arg = options_[FORMATTER].arg ? options_[FORMATTER].arg : ""; + if(arg == "vs") + { + return formatters::FORMATTER_VS; + } + + return formatters::FORMATTER_DEFAULT; + } + + const char* skip() const + { + return options_[SKIP].arg ? options_[SKIP].arg : ""; + } + + const char* only() const + { + return options_[ONLY].arg ? options_[ONLY].arg : ""; + } + + private: + enum option_index { UNKNOWN, VERSION, HELP, REPORTER, NO_COLOR, + FORMATTER, SKIP, ONLY }; + + static const option::Descriptor* usage() + { + static const option::Descriptor usage[] = + { + {UNKNOWN, 0, "", "", option::Arg::None, "USAGE: [options]\n\n" + "Options:" }, + {VERSION, 0, "", "version", option::Arg::None, " --version, \tPrint version of bandit"}, + {HELP, 0, "", "help", option::Arg::None, " --help, \tPrint usage and exit."}, + {REPORTER, 0, "", "reporter", option::Arg::Optional, " --reporter=, \tSelect reporter (dots, singleline, xunit, spec)"}, + {NO_COLOR, 0, "", "no-color", option::Arg::None, " --no-color, \tSuppress colors in output"}, + {FORMATTER, 0, "", "formatter", option::Arg::Optional, " --formatter=, \tSelect formatting of errors (default, vs)"}, + {SKIP, 0, "", "skip", option::Arg::Optional, " --skip=, \tskip all 'describe' and 'it' containing substring"}, + {ONLY, 0, "", "only", option::Arg::Optional, " --only=, \tonly run 'describe' and 'it' containing substring"}, + {0, 0, 0, 0, 0, 0} + }; + + return usage; + } + + private: + std::vector options_; + bool parsed_ok_; + + }; + +}} + +#endif diff --git a/vendor/bandit/bandit/registration/registrar.h b/vendor/bandit/bandit/registration/registrar.h new file mode 100644 index 00000000..55d36255 --- /dev/null +++ b/vendor/bandit/bandit/registration/registrar.h @@ -0,0 +1,19 @@ +#ifndef BANDIT_REGISTRAR_H +#define BANDIT_REGISTRAR_H + +namespace bandit { namespace detail { + + struct spec_registrar + { + spec_registrar( bandit::detail::voidfunc_t func) + { + bandit::detail::specs().push_back(func); + } + }; + +}} + +#define go_bandit \ + static bandit::detail::spec_registrar bandit_registrar + +#endif diff --git a/vendor/bandit/bandit/registration/registration.h b/vendor/bandit/bandit/registration/registration.h new file mode 100644 index 00000000..ad3f8b06 --- /dev/null +++ b/vendor/bandit/bandit/registration/registration.h @@ -0,0 +1,7 @@ +#ifndef BANDIT_REGISTRATION_H +#define BANDIT_REGISTRATION_H + +#include +#include + +#endif diff --git a/vendor/bandit/bandit/registration/spec_registry.h b/vendor/bandit/bandit/registration/spec_registry.h new file mode 100644 index 00000000..50c35402 --- /dev/null +++ b/vendor/bandit/bandit/registration/spec_registry.h @@ -0,0 +1,17 @@ +#ifndef BANDIT_SPEC_REGISTRY_H +#define BANDIT_SPEC_REGISTRY_H + +namespace bandit { + namespace detail { + typedef std::list spec_registry; + + inline detail::spec_registry& specs() + { + static detail::spec_registry registry; + return registry; + } + } + +} + +#endif diff --git a/vendor/bandit/bandit/reporters/colorizer.h b/vendor/bandit/bandit/reporters/colorizer.h new file mode 100644 index 00000000..217bdddf --- /dev/null +++ b/vendor/bandit/bandit/reporters/colorizer.h @@ -0,0 +1,99 @@ +#ifndef BANDIT_REPORTERS_COLORIZER_H +#define BANDIT_REPORTERS_COLORIZER_H + +#ifdef _WIN32 + #ifndef MINGW32 + #define NOMINMAX + #endif + + #define WIN32_LEAN_AND_MEAN + #include +#endif + +namespace bandit { namespace detail { + +#ifdef _WIN32 + struct colorizer + { + colorizer(bool colors_enabled = true) + : colors_enabled_(colors_enabled), + stdout_handle_(GetStdHandle(STD_OUTPUT_HANDLE)) + { + original_color_ = get_console_color(); + } + + const char* green() const + { + if(colors_enabled_) + { + set_console_color(FOREGROUND_GREEN); + } + return ""; + } + + const char* red() const + { + if(colors_enabled_) + { + set_console_color(FOREGROUND_RED); + } + return ""; + } + + const char* reset() const + { + if(colors_enabled_) + { + set_console_color(original_color_); + } + return ""; + } + + private: + WORD get_console_color() const + { + CONSOLE_SCREEN_BUFFER_INFO info = {0}; + GetConsoleScreenBufferInfo(stdout_handle_, &info); + return info.wAttributes; + } + + void set_console_color(WORD color) const + { + SetConsoleTextAttribute(stdout_handle_, color); + } + + private: + bool colors_enabled_; + HANDLE stdout_handle_; + WORD original_color_; + }; + +#else + struct colorizer + { + colorizer(bool colors_enabled = true) + : colors_enabled_(colors_enabled) + {} + + const char* green() const + { + return colors_enabled_ ? "\033[1;32m" : ""; + } + + const char* red() const + { + return colors_enabled_ ? "\033[1;31m" : ""; + } + + const char* reset() const + { + return colors_enabled_ ? "\033[0m" : ""; + } + + private: + bool colors_enabled_; + }; +#endif +}} + +#endif diff --git a/vendor/bandit/bandit/reporters/dots_reporter.h b/vendor/bandit/bandit/reporters/dots_reporter.h new file mode 100644 index 00000000..3c5083fe --- /dev/null +++ b/vendor/bandit/bandit/reporters/dots_reporter.h @@ -0,0 +1,69 @@ +#ifndef BANDIT_DOTS_REPORTER_H +#define BANDIT_DOTS_REPORTER_H + +namespace bandit { namespace detail { + + struct dots_reporter : public progress_reporter + { + dots_reporter(std::ostream& stm, const failure_formatter& failure_formatter, + const detail::colorizer& colorizer) + : progress_reporter(failure_formatter), stm_(stm), colorizer_(colorizer) + {} + + dots_reporter(const failure_formatter& failure_formatter, const detail::colorizer& colorizer) + : progress_reporter(failure_formatter), stm_(std::cout), colorizer_(colorizer) + {} + + dots_reporter& operator=(const dots_reporter&) { return *this; } + + void test_run_complete() + { + progress_reporter::test_run_complete(); + + stm_ << std::endl; + + test_run_summary summary(specs_run_, specs_failed_, specs_succeeded_, specs_skipped_, failures_, + test_run_errors_, colorizer_); + summary.write(stm_); + stm_.flush(); + } + + void test_run_error(const char* desc, const struct test_run_error& err) + { + progress_reporter::test_run_error(desc, err); + + std::stringstream ss; + ss << std::endl; + ss << "Failed to run \"" << current_context_name() << "\": error \"" << err.what() << "\"" << std::endl; + + test_run_errors_.push_back(ss.str()); + } + + void it_succeeded(const char* desc) + { + progress_reporter::it_succeeded(desc); + stm_ << colorizer_.green() << "." << colorizer_.reset(); + stm_.flush(); + } + + void it_failed(const char* desc, const assertion_exception& ex) + { + progress_reporter::it_failed(desc, ex); + stm_ << colorizer_.red() << "F" << colorizer_.reset(); + stm_.flush(); + } + + void it_unknown_error(const char* desc) + { + progress_reporter::it_unknown_error(desc); + stm_ << colorizer_.red() << "E" << colorizer_.reset(); + stm_.flush(); + } + + private: + std::ostream& stm_; + const detail::colorizer& colorizer_; + }; +}} + +#endif diff --git a/vendor/bandit/bandit/reporters/progress_reporter.h b/vendor/bandit/bandit/reporters/progress_reporter.h new file mode 100644 index 00000000..d9dc47bd --- /dev/null +++ b/vendor/bandit/bandit/reporters/progress_reporter.h @@ -0,0 +1,116 @@ +#ifndef BANDIT_PROGRESS_REPORTER_H +#define BANDIT_PROGRESS_REPORTER_H + +namespace bandit { namespace detail { + + struct progress_reporter : public listener + { + progress_reporter(const detail::failure_formatter& failure_formatter) + : specs_run_(0), specs_succeeded_(0), specs_failed_(0), specs_skipped_(0), + failure_formatter_(failure_formatter) + {} + + progress_reporter& operator=(const progress_reporter&) { return *this; } + + virtual void test_run_starting() + { + specs_run_ = 0; + specs_succeeded_ = 0; + specs_failed_ = 0; + specs_skipped_ = 0; + failures_.clear(); + contexts_.clear(); + } + + virtual void test_run_complete() + { + } + + virtual void context_starting(const char* desc) + { + contexts_.push_back(std::string(desc)); + } + + virtual void context_ended(const char*) + { + contexts_.pop_back(); + } + + virtual void test_run_error(const char*, const struct test_run_error&) + {} + + void it_starting(const char*) + { + specs_run_++; + } + + void it_succeeded(const char*) + { + specs_succeeded_++; + } + + void it_failed(const char* desc, const assertion_exception& ex) + { + specs_failed_++; + + std::stringstream ss; + ss << std::endl; + ss << current_context_name() << " " << desc << ":" << std::endl; + ss << failure_formatter_.format(ex); + + failures_.push_back(ss.str()); + } + + void it_unknown_error(const char* desc) + { + specs_failed_++; + + std::stringstream ss; + ss << std::endl; + ss << current_context_name() << " " << desc << ":" << std::endl; + ss << "Unknown exception"; + ss << std::endl; + + failures_.push_back(ss.str()); + } + + void it_skip(const char* /* desc */) + { + specs_skipped_++; + } + + bool did_we_pass() const + { + return specs_run_ > 0 && specs_failed_ == 0 && test_run_errors_.size() == 0; + } + + protected: + std::string current_context_name() + { + std::string name; + + std::for_each(contexts_.begin(), contexts_.end(), [&](const std::string context){ + if(name.size() > 0) + { + name += " "; + } + + name += context; + }); + + return name; + } + + protected: + int specs_run_; + int specs_succeeded_; + int specs_failed_; + int specs_skipped_; + const detail::failure_formatter& failure_formatter_; + std::list contexts_; + std::list failures_; + std::list test_run_errors_; + }; +}} + +#endif diff --git a/vendor/bandit/bandit/reporters/reporters.h b/vendor/bandit/bandit/reporters/reporters.h new file mode 100644 index 00000000..1a9a761a --- /dev/null +++ b/vendor/bandit/bandit/reporters/reporters.h @@ -0,0 +1,28 @@ +#ifndef BANDIT_REPORTERS_H +#define BANDIT_REPORTERS_H + +#include +#include +#include +#include +#include +#include +#include + +namespace bandit { namespace detail { + + inline listener& registered_listener(listener* reporter = NULL) + { + static struct listener* reporter_; + + if(reporter) + { + reporter_ = reporter; + } + + return *reporter_; + } + +}} + +#endif diff --git a/vendor/bandit/bandit/reporters/single_line_reporter.h b/vendor/bandit/bandit/reporters/single_line_reporter.h new file mode 100644 index 00000000..08d1c08d --- /dev/null +++ b/vendor/bandit/bandit/reporters/single_line_reporter.h @@ -0,0 +1,86 @@ +#ifndef BANDIT_REPORTERS_SINGLE_LINE_REPORTER_H +#define BANDIT_REPORTERS_SINGLE_LINE_REPORTER_H + +namespace bandit { namespace detail { + + struct single_line_reporter : public progress_reporter + { + single_line_reporter(std::ostream& stm, const failure_formatter& failure_formatter, + const detail::colorizer& colorizer) + : progress_reporter(failure_formatter), stm_(stm), colorizer_(colorizer) + {} + + single_line_reporter(const failure_formatter& failure_formatter, + const detail::colorizer& colorizer) + : progress_reporter(failure_formatter), stm_(std::cout), colorizer_(colorizer) + {} + + single_line_reporter& operator=(const single_line_reporter&) { return *this; } + + void test_run_complete() + { + progress_reporter::test_run_complete(); + + stm_ << std::endl; + + test_run_summary summary(specs_run_, specs_failed_, specs_succeeded_, specs_skipped_, failures_, + test_run_errors_, colorizer_); + summary.write(stm_); + } + + void test_run_error(const char* desc, const struct test_run_error& err) + { + progress_reporter::test_run_error(desc, err); + + std::stringstream ss; + ss << std::endl; + ss << "Failed to run \"" << current_context_name() << "\": error \"" << err.what() << "\"" << std::endl; + + test_run_errors_.push_back(ss.str()); + } + + void it_starting(const char* desc) + { + print_status_line(); + progress_reporter::it_starting(desc); + } + + void it_succeeded(const char* desc) + { + progress_reporter::it_succeeded(desc); + print_status_line(); + } + + void it_failed(const char* desc, const assertion_exception& ex) + { + progress_reporter::it_failed(desc, ex); + print_status_line(); + } + + void it_unknown_error(const char* desc) + { + progress_reporter::it_unknown_error(desc); + print_status_line(); + } + + private: + void print_status_line() + { + stm_ << '\r'; + stm_ << "Executed " << specs_run_ << " tests."; + + if(specs_failed_) + { + stm_ << " " << specs_succeeded_ << " succeeded. " << colorizer_.red() << specs_failed_ << + " failed." << colorizer_.reset(); + } + stm_.flush(); + } + + private: + std::ostream& stm_; + const detail::colorizer& colorizer_; + }; +}} + +#endif diff --git a/vendor/bandit/bandit/reporters/spec_reporter.h b/vendor/bandit/bandit/reporters/spec_reporter.h new file mode 100644 index 00000000..6d63bfb0 --- /dev/null +++ b/vendor/bandit/bandit/reporters/spec_reporter.h @@ -0,0 +1,126 @@ +#ifndef BANDIT_SPEC_REPORTER_H +#define BANDIT_SPEC_REPORTER_H + +namespace bandit { namespace detail { + + struct spec_reporter : public progress_reporter + { + spec_reporter(std::ostream& stm, const failure_formatter& failure_formatter, + const detail::colorizer& colorizer) + : progress_reporter(failure_formatter), stm_(stm), colorizer_(colorizer), indentation_(0) + {} + + spec_reporter(const failure_formatter& failure_formatter, const detail::colorizer& colorizer) + : progress_reporter(failure_formatter), stm_(std::cout), colorizer_(colorizer), indentation_(0) + {} + + spec_reporter& operator=(const spec_reporter&) { return *this; } + + void test_run_complete() + { + progress_reporter::test_run_complete(); + + stm_ << std::endl; + + test_run_summary summary(specs_run_, specs_failed_, specs_succeeded_, specs_skipped_, failures_, + test_run_errors_, colorizer_); + summary.write(stm_); + stm_.flush(); + } + + void test_run_error(const char* desc, const struct test_run_error& err) + { + progress_reporter::test_run_error(desc, err); + + std::stringstream ss; + ss << std::endl; + ss << "Failed to run \"" << current_context_name() << "\": error \"" << err.what() << "\"" << std::endl; + + test_run_errors_.push_back(ss.str()); + } + + virtual void context_starting(const char* desc) + { + progress_reporter::context_starting(desc); + + stm_ << indent(); + stm_ << "describe " << desc << std::endl; + increase_indent(); + stm_.flush(); + + } + + virtual void context_ended(const char* desc) + { + progress_reporter::context_ended(desc); + decrease_indent(); + } + + virtual void it_starting(const char* desc) + { + progress_reporter::it_starting(desc); + stm_ << indent() << "- it " << desc << " ... "; + stm_.flush(); + } + + virtual void it_succeeded(const char* desc) + { + progress_reporter::it_succeeded(desc); + stm_ << colorizer_.green(); + stm_ << "OK"; + stm_ << colorizer_.reset(); + stm_ << std::endl; + stm_.flush(); + } + + virtual void it_failed(const char* desc, const assertion_exception& ex) + { + progress_reporter::it_failed(desc, ex); + stm_ << colorizer_.red(); + stm_ << "FAILED"; + stm_ << colorizer_.reset(); + stm_ << std::endl; + stm_.flush(); + } + + virtual void it_unknown_error(const char* desc) + { + progress_reporter::it_unknown_error(desc); + stm_ << colorizer_.red(); + stm_ << "ERROR"; + stm_ << colorizer_.reset(); + stm_ << std::endl; + stm_.flush(); + } + + virtual void it_skip(const char* desc) + { + progress_reporter::it_skip(desc); + stm_ << indent() << "- it " << desc << " ... SKIPPED" << std::endl; + stm_.flush(); + } + + private: + void increase_indent() + { + indentation_++; + } + + void decrease_indent() + { + indentation_--; + } + + std::string indent() + { + return std::string(indentation_, '\t'); + } + + private: + std::ostream& stm_; + const detail::colorizer& colorizer_; + int indentation_; + }; +}} + +#endif diff --git a/vendor/bandit/bandit/reporters/test_run_summary.h b/vendor/bandit/bandit/reporters/test_run_summary.h new file mode 100644 index 00000000..aa1d4a59 --- /dev/null +++ b/vendor/bandit/bandit/reporters/test_run_summary.h @@ -0,0 +1,90 @@ +#ifndef BANDIT_TEST_RUN_SUMMARY_H +#define BANDIT_TEST_RUN_SUMMARY_H + +namespace bandit { namespace detail { + + struct test_run_summary + { + test_run_summary(int specs_run, int specs_failed, int specs_succeeded, int specs_skipped, + const std::list& failures, const std::list& test_run_errors, + const detail::colorizer& colorizer) + : specs_run_(specs_run), specs_succeeded_(specs_succeeded), specs_failed_(specs_failed), + specs_skipped_(specs_skipped), failures_(failures), test_run_errors_(test_run_errors), + colorizer_(colorizer) + {} + + test_run_summary& operator=(const test_run_summary&) { return *this; } + + void write(std::ostream& stm) + { + if(specs_run_ == 0 && test_run_errors_.size() == 0) + { + stm << colorizer_.red(); + stm << "Could not find any tests."; + stm << colorizer_.reset(); + stm << std::endl; + return; + } + + if(specs_failed_ == 0 && test_run_errors_.size() == 0) + { + stm << colorizer_.green(); + stm << "Success!"; + stm << colorizer_.reset(); + stm << std::endl; + } + + if(test_run_errors_.size() > 0) + { + std::for_each(test_run_errors_.begin(), test_run_errors_.end(), + [&](const std::string& error){ + stm << error << std::endl; + }); + } + + + if(specs_failed_ > 0) + { + stm << colorizer_.red(); + stm << "There were failures!"; + stm << colorizer_.reset() << std::endl; + std::for_each(failures_.begin(), failures_.end(), + [&](const std::string& failure) { + stm << failure << std::endl; + }); + stm << std::endl; + } + + stm << "Test run complete. " << specs_run_ << " tests run. " << specs_succeeded_ << + " succeeded."; + + if(specs_skipped_ > 0) + { + stm << " " << specs_skipped_ << " skipped."; + } + + if(specs_failed_ > 0) + { + stm << " " << specs_failed_ << " failed."; + } + + if(test_run_errors_.size() > 0) + { + stm << " " << test_run_errors_.size() << " test run errors."; + } + + stm << std::endl; + } + + private: + int specs_run_; + int specs_succeeded_; + int specs_failed_; + int specs_skipped_; + std::list failures_; + std::list test_run_errors_; + const detail::colorizer& colorizer_; + }; +}} + +#endif diff --git a/vendor/bandit/bandit/reporters/xunit_reporter.h b/vendor/bandit/bandit/reporters/xunit_reporter.h new file mode 100644 index 00000000..15f6ea29 --- /dev/null +++ b/vendor/bandit/bandit/reporters/xunit_reporter.h @@ -0,0 +1,109 @@ +#ifndef BANDIT_REPORTERS_XUNIT_REPORTER_H +#define BANDIT_REPORTERS_XUNIT_REPORTER_H + +namespace bandit { namespace detail { + + struct xunit_reporter : public progress_reporter + { + xunit_reporter(std::ostream& stm, const failure_formatter& formatter) + : progress_reporter(formatter), stm_(stm) + { + } + + xunit_reporter(const failure_formatter& formatter) + : progress_reporter(formatter), stm_(std::cout) + { + } + + void it_starting(const char* desc) + { + progress_reporter::it_starting(desc); + work_stm_ << "\t\n"; + } + + void it_succeeded(const char* desc) + { + progress_reporter::it_succeeded(desc); + work_stm_ << "\t\n"; + } + + void it_failed(const char* desc, const assertion_exception& ex) + { + progress_reporter::it_failed(desc, ex); + work_stm_ << "\t\t\n"; + work_stm_ << "\t\n"; + } + + void it_unknown_error(const char* desc) + { + progress_reporter::it_unknown_error(desc); + work_stm_ << "\t\t\n"; + work_stm_ << "\t\n"; + } + + void it_skip(const char* desc) + { + progress_reporter::it_skip(desc); + work_stm_ << "\t\n"; + work_stm_ << "\t\t\n"; + work_stm_ << "\t\n"; + } + + void test_run_complete() + { + stm_ << "\n"; + stm_ << " 0) + { + stm_ << " skipped=\"" << specs_skipped_ << "\""; + } + + stm_ << ">\n"; + + stm_ << work_stm_.str(); + + stm_ << "\n"; + } + + private: + std::string escape(const std::string& str) + { + std::stringstream stm; + + std::for_each(str.begin(), str.end(), [&](char c){ + switch(c) + { + case '&': + stm << "&"; + break; + case '<': + stm << "<"; + break; + case '>': + stm << ">"; + break; + case '\\': + stm << "'"; + break; + case '\"': + stm << """; + break; + default: + stm << c; + } + }); + + return stm.str(); + } + + private: + std::ostream& stm_; + std::stringstream work_stm_; + }; +}} + +#endif diff --git a/vendor/bandit/bandit/run_policies/always_run_policy.h b/vendor/bandit/bandit/run_policies/always_run_policy.h new file mode 100644 index 00000000..29bdc627 --- /dev/null +++ b/vendor/bandit/bandit/run_policies/always_run_policy.h @@ -0,0 +1,16 @@ +#ifndef BANDIT_ALWAYS_RUN_POLICY_H +#define BANDIT_ALWAYS_RUN_POLICY_H + +namespace bandit { namespace detail { + + struct always_run_policy : public run_policy + { + bool should_run(const char* /* it_name */, const contextstack_t& /* contexts */) const + { + return true; + } + }; + +}} + +#endif diff --git a/vendor/bandit/bandit/run_policies/bandit_run_policy.h b/vendor/bandit/bandit/run_policies/bandit_run_policy.h new file mode 100644 index 00000000..6e150d8d --- /dev/null +++ b/vendor/bandit/bandit/run_policies/bandit_run_policy.h @@ -0,0 +1,155 @@ +#ifndef BANDIT_BANDIT_RUN_POLICY_H +#define BANDIT_BANDIT_RUN_POLICY_H + +namespace bandit { namespace detail { + + struct bandit_run_policy : public run_policy + { + bandit_run_policy(const char* skip_pattern, const char* only_pattern) + : skip_pattern_(skip_pattern), only_pattern_(only_pattern) + {} + + bool should_run(const char* it_name, const contextstack_t& contexts) const + { + // + // Never run if a context has been marked as skip + // using 'describe_skip' + // + if(has_context_with_hard_skip(contexts)) + { + return false; + } + + // + // Always run if no patterns have been specifed + // + if(!has_skip_pattern() && !has_only_pattern()) + { + return true; + } + + if(has_only_pattern() && !has_skip_pattern()) + { + return context_matches_only_pattern(contexts) + || matches_only_pattern(it_name); + } + + if(has_skip_pattern() && !has_only_pattern()) + { + bool skip = context_matches_skip_pattern(contexts) || + matches_skip_pattern(it_name); + return !skip; + } + + // + // If we've come this far, both 'skip' and 'only' + // have been specified. + // + // If our contexts match 'only' we're still good + // regardless of whether there's a 'skip' somewhere + // in the context stack as well. + if(context_matches_only_pattern(contexts)) + { + // + // We can still mark the current 'it' as 'skip' + // and ignore it. We check that here. + // + return !matches_skip_pattern(it_name); + } + + // + // If we've gotten this far, the context matches 'skip' + // We can still run this spec if it is specifically marked + // as 'only'. + // + return matches_only_pattern(it_name); + } + + private: + bool has_context_with_hard_skip(const contextstack_t& contexts) const + { + contextstack_t::const_iterator it; + for(it = contexts.begin(); it != contexts.end(); it++) + { + if((*it)->hard_skip()) + { + return true; + } + } + + return false; + } + + bool has_only_pattern() const + { + return only_pattern_.size() > 0; + } + + bool has_skip_pattern() const + { + return skip_pattern_.size() > 0; + } + + bool context_matches_only_pattern(const contextstack_t& contexts) const + { + contextstack_t::const_iterator it; + for(it = contexts.begin(); it != contexts.end(); it++) + { + if(matches_only_pattern((*it)->name())) + { + return true; + } + } + + return false; + } + + bool context_matches_skip_pattern(const contextstack_t& contexts) const + { + contextstack_t::const_iterator it; + for(it = contexts.begin(); it != contexts.end(); it++) + { + if(matches_skip_pattern((*it)->name())) + { + return true; + } + } + + return false; + } + + bool matches_only_pattern(const char* name) const + { + std::string n(name); + return matches_only_pattern(n); + } + + bool matches_only_pattern(const std::string& name) const + { + return matches_pattern(name, only_pattern_); + } + + bool matches_skip_pattern(const char* name) const + { + std::string n(name); + return matches_skip_pattern(n); + } + + bool matches_skip_pattern(const std::string& name) const + { + return matches_pattern(name, skip_pattern_); + } + + bool matches_pattern(const std::string& name, const std::string& pattern) const + { + return name.find(pattern) != std::string::npos; + } + + private: + std::string skip_pattern_; + std::string only_pattern_; + }; + +}} + +#endif diff --git a/vendor/bandit/bandit/run_policies/never_run_policy.h b/vendor/bandit/bandit/run_policies/never_run_policy.h new file mode 100644 index 00000000..003fd889 --- /dev/null +++ b/vendor/bandit/bandit/run_policies/never_run_policy.h @@ -0,0 +1,14 @@ +#ifndef BANDIT_NEVER_RUN_POLICY_H +#define BANDIT_NEVER_RUN_POLICY_H + +namespace bandit { namespace detail { + + struct never_run_policy : public run_policy + { + bool should_run(const char* /* it_name */, const contextstack_t& /* contexts */) const + { + return false; + } + }; +}} +#endif diff --git a/vendor/bandit/bandit/run_policies/run_policies.h b/vendor/bandit/bandit/run_policies/run_policies.h new file mode 100644 index 00000000..88d8dbb5 --- /dev/null +++ b/vendor/bandit/bandit/run_policies/run_policies.h @@ -0,0 +1,9 @@ +#ifndef BANDIT_RUN_POLICIES_H +#define BANDIT_RUN_POLICIES_H + +#include "run_policy.h" +#include "always_run_policy.h" +#include "never_run_policy.h" +#include "bandit_run_policy.h" + +#endif diff --git a/vendor/bandit/bandit/run_policies/run_policy.h b/vendor/bandit/bandit/run_policies/run_policy.h new file mode 100644 index 00000000..7440e1cd --- /dev/null +++ b/vendor/bandit/bandit/run_policies/run_policy.h @@ -0,0 +1,27 @@ +#ifndef BANDIT_RUN_POLICY_H +#define BANDIT_RUN_POLICY_H + +namespace bandit { namespace detail { + + struct run_policy + { + virtual ~run_policy() {} + virtual bool should_run(const char* it_name, const contextstack_t& contexts) const = 0; + }; + typedef std::unique_ptr run_policy_ptr; + + inline run_policy& registered_run_policy(run_policy* policy = NULL) + { + static struct run_policy* policy_; + + if(policy) + { + policy_ = policy; + } + + return *policy_; + } + +}} + +#endif diff --git a/vendor/bandit/bandit/runner.h b/vendor/bandit/bandit/runner.h new file mode 100644 index 00000000..5df11005 --- /dev/null +++ b/vendor/bandit/bandit/runner.h @@ -0,0 +1,98 @@ +#ifndef BANDIT_RUNNER_H +#define BANDIT_RUNNER_H + +namespace bandit { + + namespace detail { + + inline run_policy_ptr create_run_policy(const options& opt) + { + return run_policy_ptr(new bandit_run_policy(opt.skip(), opt.only())); + } + + inline listener_ptr create_reporter(const options& opt, + const failure_formatter* formatter, const colorizer& colorizer) + { + std::string name(opt.reporter() ? opt.reporter() : ""); + + if(name == "singleline") + { + return std::unique_ptr(new single_line_reporter(*formatter, colorizer)); + } + + if(name == "xunit") + { + return std::unique_ptr(new xunit_reporter(*formatter)); + } + + if(name == "spec") + { + return std::unique_ptr(new spec_reporter(*formatter, colorizer)); + } + + return std::unique_ptr(new dots_reporter(*formatter, colorizer)); + } + + typedef std::function reporter_factory_fn; + typedef std::function register_reporter_fn; + + inline failure_formatter_ptr create_formatter(const options& opt) + { + if(opt.formatter() == options::formatters::FORMATTER_VS) + { + return failure_formatter_ptr(new visual_studio_failure_formatter()); + } + + return failure_formatter_ptr(new default_failure_formatter()); + } + } + + inline int run(const detail::options& opt, const detail::spec_registry& specs, + detail::contextstack_t& context_stack, detail::listener& listener) + { + if(opt.help()) + { + opt.print_usage(); + return 0; + } + + if(opt.version()) + { + std::cout << "bandit version " << BANDIT_VERSION << std::endl; + return 0; + } + + auto call_func = [](const detail::voidfunc_t& func) { + func(); + }; + + listener.test_run_starting(); + + bool hard_skip = false; + detail::bandit_context global_context("", hard_skip); + context_stack.push_back(&global_context); + + for_each(specs.begin(), specs.end(), call_func); + + listener.test_run_complete(); + + return listener.did_we_pass() ? 0 : 1; + } + + inline int run(int argc, char* argv[]) + { + detail::options opt(argc, argv); + detail::failure_formatter_ptr formatter(create_formatter(opt)); + bandit::detail::colorizer colorizer(!opt.no_color()); + detail::listener_ptr reporter(create_reporter(opt, formatter.get(), colorizer)); + + detail::registered_listener(reporter.get()); + + detail::run_policy_ptr run_policy = create_run_policy(opt); + registered_run_policy(run_policy.get()); + + return run(opt, detail::specs(), detail::context_stack(), *reporter); + } +} + +#endif diff --git a/vendor/bandit/bandit/skip_policies/always_include_policy.h b/vendor/bandit/bandit/skip_policies/always_include_policy.h new file mode 100644 index 00000000..2e978308 --- /dev/null +++ b/vendor/bandit/bandit/skip_policies/always_include_policy.h @@ -0,0 +1,16 @@ +#ifndef BANDIT_ALWAYS_INCLUDE_POLICY_H +#define BANDIT_ALWAYS_INCLUDE_POLICY_H + +namespace bandit { namespace detail { + + struct always_include_policy : public skip_policy + { + bool should_skip(const char*) const + { + return false; + } + }; + +}} + +#endif diff --git a/vendor/bandit/bandit/skip_policies/always_skip_policy.h b/vendor/bandit/bandit/skip_policies/always_skip_policy.h new file mode 100644 index 00000000..9d6a4bfc --- /dev/null +++ b/vendor/bandit/bandit/skip_policies/always_skip_policy.h @@ -0,0 +1,15 @@ +#ifndef BANDIT_ALWAYS_SKIP_POLICY_H +#define BANDIT_ALWAYS_SKIP_POLICY_H + +namespace bandit { namespace detail { + + struct always_skip_policy : public skip_policy + { + bool should_skip(const char*) const + { + return true; + } + }; +}} + +#endif diff --git a/vendor/bandit/bandit/skip_policies/name_contains_skip_policy.h b/vendor/bandit/bandit/skip_policies/name_contains_skip_policy.h new file mode 100644 index 00000000..da727c3d --- /dev/null +++ b/vendor/bandit/bandit/skip_policies/name_contains_skip_policy.h @@ -0,0 +1,28 @@ +#ifndef BANDIT_NAME_CONTAINS_SKIP_POLICY_H +#define BANDIT_NAME_CONTAINS_SKIP_POLICY_H + +namespace bandit { namespace detail { + struct name_contains_skip_policy : public skip_policy + { + name_contains_skip_policy(const char* pattern) + : pattern_(pattern) + {} + + bool should_skip(const char* name) const + { + if(pattern_.size() == 0) + { + return false; + } + + std::string n(name); + bool skip = n.find(pattern_) != std::string::npos; + return skip; + } + + private: + const std::string pattern_; + }; +}} + +#endif diff --git a/vendor/bandit/bandit/skip_policies/skip_policies.h b/vendor/bandit/bandit/skip_policies/skip_policies.h new file mode 100644 index 00000000..f3fbfbfd --- /dev/null +++ b/vendor/bandit/bandit/skip_policies/skip_policies.h @@ -0,0 +1,9 @@ +#ifndef BANDIT_SKIP_POLICIES +#define BANDIT_SKIP_POLICIES + +#include +#include +#include +#include + +#endif diff --git a/vendor/bandit/bandit/skip_policies/skip_policy.h b/vendor/bandit/bandit/skip_policies/skip_policy.h new file mode 100644 index 00000000..ca606dfb --- /dev/null +++ b/vendor/bandit/bandit/skip_policies/skip_policy.h @@ -0,0 +1,29 @@ +#ifndef BANDIT_SKIP_POLICY_H +#define BANDIT_SKIP_POLICY_H + +namespace bandit { + + struct skip_policy + { + virtual bool should_skip(const char* name) const = 0; + }; + typedef std::unique_ptr skip_policy_ptr; + + namespace detail { + + inline skip_policy& registered_skip_policy(skip_policy* policy = NULL) + { + static struct skip_policy* policy_; + + if(policy) + { + policy_ = policy; + } + + return *policy_; + } + } + +} + +#endif diff --git a/vendor/bandit/bandit/test_run_error.h b/vendor/bandit/bandit/test_run_error.h new file mode 100644 index 00000000..307ef3fe --- /dev/null +++ b/vendor/bandit/bandit/test_run_error.h @@ -0,0 +1,12 @@ +#ifndef BANDIT_TEST_RUN_ERROR +#define BANDIT_TEST_RUN_ERROR + +namespace bandit { namespace detail { + + struct test_run_error : public std::runtime_error + { + test_run_error(const char* message) : std::runtime_error(message) {} + }; +}} + +#endif diff --git a/vendor/bandit/cross_compile.sh b/vendor/bandit/cross_compile.sh new file mode 100755 index 00000000..e8de39f7 --- /dev/null +++ b/vendor/bandit/cross_compile.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +function build_for { + CC=$1 + CXX=$2 + BUILD_DIR=build-$CC + + mkdir $BUILD_DIR + pushd $BUILD_DIR + CC=$CC CXX=$CXX cmake ../.. + make + popd +} + +if [[ -d builds ]]; then + rm -rf builds +fi + +mkdir builds +pushd builds + +build_for gcc-4.5 g++-4.5 +GCC45=$? + +build_for gcc-4.6 g++-4.6 +GCC46=$? + +build_for gcc-4.7 g++-4.7 +GCC47=$? + +build_for gcc-4.8 g++-4.8 +GCC48=$? + +build_for clang clang++ +CLANG=$? + +popd + +echo +echo "Result:" +echo -e "gcc-4.5:\t$GCC45" +echo -e "gcc-4.6:\t$GCC46" +echo -e "gcc-4.7:\t$GCC47" +echo -e "gcc-4.8:\t$GCC48" +echo -e "clang:\t$CLANG" +echo "Done" +exit $(( $GCC45 + $GCC46 + $GCC47 + $GCC48 + $CLANG )) diff --git a/vendor/bandit/specs/before_each_after_each.spec.cpp b/vendor/bandit/specs/before_each_after_each.spec.cpp new file mode 100644 index 00000000..29d40574 --- /dev/null +++ b/vendor/bandit/specs/before_each_after_each.spec.cpp @@ -0,0 +1,78 @@ +#include + +namespace bf = bandit::fakes; + +go_bandit([](){ + + describe("before_each/after_each", [&](){ + std::unique_ptr context_stack; + std::unique_ptr context; + + before_each([&](){ + context = std::unique_ptr(new bf::fake_context()); + context_stack = std::unique_ptr(new bandit::detail::contextstack_t()); + context_stack->push_back(context.get()); + }); + + describe("before_each", [&](){ + bandit::detail::voidfunc_t before_each_fn; + + before_each([&](){ + before_each_fn = [](){}; + }); + + it("registers itself for the current context in the stack", [&](){ + before_each(before_each_fn, *context_stack); + Assert::That(context->call_log(), Has().Exactly(1).EqualTo("register_before_each")); + }); + + }); + + describe("after_each", [&](){ + bandit::detail::voidfunc_t after_each_fn; + + before_each([&](){ + after_each_fn = [](){}; + }); + + it("registers itself for the current context in the stack", [&](){ + after_each(after_each_fn, *context_stack); + Assert::That(context->call_log(), Has().Exactly(1).EqualTo("register_after_each")); + }); + + }); + }); + + describe("before_each/after_each integration", [&](){ + bandit::specs::logging_fake logger; + + before_each([&](){ + logger.log() << "first before_each called" << std::endl; + }); + + before_each([&](){ + logger.log() << "second before_each called" << std::endl; + }); + + after_each([&](){ + logger.log() << "first after_each called" << std::endl; + }); + + after_each([&](){ + logger.log() << "second after_each called" << std::endl; + }); + + it("should only have called the before_each functions for the first test", [&](){ + Assert::That(logger.call_log(), Has().Exactly(1).EqualTo("first before_each called")); + Assert::That(logger.call_log(), Has().Exactly(1).EqualTo("second before_each called")); + Assert::That(logger.call_log(), Has().None().Containing("after_each")); + }); + + it("should have called 'before_each' function twice, and 'after_each' functions once for the second test", [&](){ + Assert::That(logger.call_log(), Has().Exactly(2).EqualTo("first before_each called")); + Assert::That(logger.call_log(), Has().Exactly(2).EqualTo("second before_each called")); + Assert::That(logger.call_log(), Has().Exactly(1).EqualTo("first after_each called")); + Assert::That(logger.call_log(), Has().Exactly(1).EqualTo("second after_each called")); + }); + }); +}); diff --git a/vendor/bandit/specs/context.spec.cpp b/vendor/bandit/specs/context.spec.cpp new file mode 100644 index 00000000..d517ef80 --- /dev/null +++ b/vendor/bandit/specs/context.spec.cpp @@ -0,0 +1,44 @@ +#include + +go_bandit([](){ + + describe("bandit_context:", [&](){ + + std::unique_ptr context; + + before_each([&](){ + bool hard_skip = false; + context = std::unique_ptr( + new bandit::detail::bandit_context("context name", hard_skip)); + }); + + it("is ok to register before_each as it is not executing", [&](){ + context->register_before_each([](){}); + }); + + it("is ok to register after_each as it is not executing", [&](){ + context->register_after_each([](){}); + }); + + describe("is executing", [&](){ + + before_each([&](){ + context->execution_is_starting(); + }); + + it("is not ok to register before_each", [&](){ + AssertThrows(bandit::detail::test_run_error, context->register_before_each([](){})); + Assert::That(LastException().what(), + Equals("before_each was called after 'describe' or 'it'")); + }); + + it("is not ok to register after_each", [&](){ + AssertThrows(bandit::detail::test_run_error, context->register_after_each([](){})); + Assert::That(LastException().what(), + Equals("after_each was called after 'describe' or 'it'")); + }); + }); + + }); + +}); diff --git a/vendor/bandit/specs/describe.spec.cpp b/vendor/bandit/specs/describe.spec.cpp new file mode 100644 index 00000000..7578fcac --- /dev/null +++ b/vendor/bandit/specs/describe.spec.cpp @@ -0,0 +1,108 @@ +#include + +using namespace bandit::fakes; +namespace bd = bandit::detail; + +go_bandit([](){ + + describe("describe:", [](){ + bandit::detail::voidfunc_t describe_fn; + fake_reporter_ptr reporter; + std::unique_ptr context_stack; + std::unique_ptr global_context; + + before_each([&](){ + reporter = fake_reporter_ptr(new fake_reporter()); + + context_stack = std::unique_ptr(new bd::contextstack_t()); + + global_context = std::unique_ptr(new fake_context()); + context_stack->push_back(global_context.get()); + }); + + + auto call_describe = [&](){ + describe("context name", describe_fn, *reporter, *context_stack); + }; + + describe("with a succeeding 'it'", [&](){ + int context_stack_size_while_running; + + before_each([&](){ + context_stack_size_while_running = 0; + describe_fn = [&](){context_stack_size_while_running = context_stack->size();}; + }); + + it("tells its parent context that execution has started", [&](){ + // This is important as once execution has started, + // before_each and after_each calls cannot be guaranteed to + // be run before any 'it' method. + + call_describe(); + AssertThat(global_context->call_log(), Has().AtLeast(1).EqualTo("execution_is_starting")); + }); + + it("tells reporter it's starting a run", [&](){ + call_describe(); + AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("context_starting: context name")); + }); + + it("tells reporter it's finished a run", [&](){ + call_describe(); + AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("context_ended: context name")); + }); + + it("pushes a new context during execution", [&](){ + call_describe(); + AssertThat(context_stack_size_while_running, Equals(2)); + }); + + it("pops the context from the stack after execution so that only the global context is left", [&](){ + call_describe(); + AssertThat(*context_stack, Is().OfLength(1)); + }); + + }); + + describe("with test run error", [&](){ + // + // This can occur if after_each or before_each are called + // after execution has started for a context. + // + + before_each([&](){ + describe_fn = [&](){ throw bandit::detail::test_run_error("we dun goofed!"); }; + }); + + it("doesn't propagate the error", [&](){ + call_describe(); + }); + + it("tells reporter to report the error", [&](){ + call_describe(); + AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("test_run_error: context name (we dun goofed!)")); + }); + + }); + + describe("skip", [&](){ + bool context_is_hard_skip; + auto describe_fn = + [&](){ context_is_hard_skip = context_stack->back()->hard_skip(); }; + + before_each([&](){ + context_is_hard_skip = false; + }); + + describe("describe_skip", [&](){ + + it("pushes a context marked as skipped on the stack", [&](){ + describe_skip("context name", describe_fn, *reporter, *context_stack); + AssertThat(context_is_hard_skip, IsTrue()); + }); + + }); + }); + }); + +}); diff --git a/vendor/bandit/specs/failure_formatters/default_formatter.spec.cpp b/vendor/bandit/specs/failure_formatters/default_formatter.spec.cpp new file mode 100644 index 00000000..6d29b694 --- /dev/null +++ b/vendor/bandit/specs/failure_formatters/default_formatter.spec.cpp @@ -0,0 +1,21 @@ +#include +namespace bd = bandit::detail; + +go_bandit([](){ + + describe("default failure formatter", [&](){ + bd::default_failure_formatter formatter; + + it("formats assertions with file and line number", [&](){ + bd::assertion_exception exception("message", "file", 321); + AssertThat(formatter.format(exception), Equals("file:321: message")); + }); + + it("formats assertions without file and line number", [&](){ + bd::assertion_exception exception("message"); + AssertThat(formatter.format(exception), Equals("message")); + }); + + }); + +}); diff --git a/vendor/bandit/specs/failure_formatters/visual_studio_failure_formatter.spec.cpp b/vendor/bandit/specs/failure_formatters/visual_studio_failure_formatter.spec.cpp new file mode 100644 index 00000000..0d283c8e --- /dev/null +++ b/vendor/bandit/specs/failure_formatters/visual_studio_failure_formatter.spec.cpp @@ -0,0 +1,22 @@ +#include +namespace bd = bandit::detail; + +go_bandit([](){ + + describe("Visual Studio failure formatter:", [&](){ + + bd::visual_studio_failure_formatter formatter; + + it("formats assertions with file and line number", [&](){ + bd::assertion_exception exception("message", "file", 321); + AssertThat(formatter.format(exception), Equals("file(321): message")); + }); + + it("formats assertions without file and line number", [&](){ + bd::assertion_exception exception("message"); + AssertThat(formatter.format(exception), Equals("bandit: message")); + }); + + }); + +}); diff --git a/vendor/bandit/specs/fakes/fake_context.h b/vendor/bandit/specs/fakes/fake_context.h new file mode 100644 index 00000000..1161cfc7 --- /dev/null +++ b/vendor/bandit/specs/fakes/fake_context.h @@ -0,0 +1,54 @@ +#ifndef BANDIT_FAKE_CONTEXT_H +#define BANDIT_FAKE_CONTEXT_H + +namespace bandit { namespace fakes { + + struct fake_context : public bandit::detail::context, public bandit::specs::logging_fake + { + fake_context() : hard_skip_(false), name_("fake_context") + {} + + const std::string& name() + { + log() << "name" << std::endl; + return name_; + } + + void execution_is_starting() + { + log() << "execution_is_starting" << std::endl; + } + + void register_before_each(detail::voidfunc_t) + { + log() << "register_before_each" << std::endl; + } + + void register_after_each(detail::voidfunc_t) + { + log() << "register_after_each" << std::endl; + } + + void run_before_eaches() + { + log() << "run_before_eaches" << std::endl; + } + + void run_after_eaches() + { + log() << "run_after_eaches" << std::endl; + } + + bool hard_skip() + { + log() << "hard_skip: returning " << hard_skip_ << std::endl; + return hard_skip_; + } + + private: + bool hard_skip_; + std::string name_; + }; +}} + +#endif diff --git a/vendor/bandit/specs/fakes/fake_reporter.h b/vendor/bandit/specs/fakes/fake_reporter.h new file mode 100644 index 00000000..032ed44d --- /dev/null +++ b/vendor/bandit/specs/fakes/fake_reporter.h @@ -0,0 +1,78 @@ +#ifndef BANDIT_SPECS_FAKE_REPORTER_H +#define BANDIT_SPECS_FAKE_REPORTER_H + +namespace bandit { namespace fakes { + struct fake_reporter : + public bandit::detail::listener, + public bandit::specs::logging_fake + { + fake_reporter() : test_run_status_(true) + {} + + void test_run_starting() + { + log() << "test_run_starting" << std::endl; + } + + void test_run_complete() + { + log() << "test_run_complete" << std::endl; + } + + void context_starting(const char* desc) + { + log() << "context_starting: " << desc << std::endl; + } + + void context_ended(const char* desc) + { + log() << "context_ended: " << desc << std::endl; + } + + void test_run_error(const char* desc, const struct bandit::detail::test_run_error& err) + { + log() << "test_run_error: " << desc << " (" << strip_newline(err.what()) << ")" << std::endl; + } + + void it_starting(const char* desc) + { + log() << "it_starting: " << desc << std::endl; + } + + void it_succeeded(const char* desc) + { + log() << "it_succeeded: " << desc << std::endl; + } + + void it_failed(const char* desc, const bandit::detail::assertion_exception& ex) + { + log() << "it_failed: " << desc << " (" << strip_newline(ex.what()) << ")" << std::endl; + } + + void it_unknown_error(const char* desc) + { + log() << "it_unknown_error: " << desc << std::endl; + } + + void it_skip(const char* desc) + { + log() << "it_skip: " << desc << std::endl; + } + + bool did_we_pass() const + { + return test_run_status_; + } + + void set_test_run_status(bool status) + { + test_run_status_ = status; + } + + private: + bool test_run_status_; + }; + typedef std::unique_ptr fake_reporter_ptr; +}} + +#endif diff --git a/vendor/bandit/specs/fakes/fakes.h b/vendor/bandit/specs/fakes/fakes.h new file mode 100644 index 00000000..a48517f6 --- /dev/null +++ b/vendor/bandit/specs/fakes/fakes.h @@ -0,0 +1,8 @@ +#ifndef BANDIT_SPECS_FAKES_H +#define BANDIT_SPECS_FAKES_H + +#include +#include +#include + +#endif diff --git a/vendor/bandit/specs/fakes/logging_fake.h b/vendor/bandit/specs/fakes/logging_fake.h new file mode 100644 index 00000000..ac1d3dd0 --- /dev/null +++ b/vendor/bandit/specs/fakes/logging_fake.h @@ -0,0 +1,32 @@ +#ifndef BANDIT_SPECS_LOGGING_FAKE_H +#define BANDIT_SPECS_LOGGING_FAKE_H +#include + +namespace bandit { namespace specs { + + struct logging_fake + { + std::ostream& log() + { + return logstm_; + } + + std::string strip_newline(const char* val) + { + std::string no_newline = val; + std::transform(no_newline.begin(), no_newline.end(), no_newline.begin(), [](const char& c) { + return (c == '\n' || c == '\r') ? ' ' : c; + }); + return no_newline; + } + + std::string call_log() + { + return logstm_.str(); + } + + private: + std::stringstream logstm_; + }; +}} +#endif diff --git a/vendor/bandit/specs/fuzzbox.spec.cpp b/vendor/bandit/specs/fuzzbox.spec.cpp new file mode 100644 index 00000000..6515a554 --- /dev/null +++ b/vendor/bandit/specs/fuzzbox.spec.cpp @@ -0,0 +1,77 @@ +#include + +namespace fuzzbox { + + typedef enum { + clean, + distorted + } sounds; + + struct fuzzbox + { + fuzzbox() : sound_(sounds::clean) + {} + + void flip() + { + sound_ = sounds::distorted; + } + + sounds sound() + { + return sound_; + } + + private: + sounds sound_; + }; + typedef std::unique_ptr fuzzbox_ptr; + + struct guitar + { + void add_effect(fuzzbox* effect) + { + effect_ = effect; + } + + sounds sound() + { + return effect_->sound(); + } + + private: + fuzzbox* effect_; + }; + typedef std::unique_ptr guitar_ptr; + +go_bandit([](){ + + describe("fuzzbox:", [](){ + guitar_ptr guitar; + fuzzbox_ptr fuzzbox; + + before_each([&](){ + guitar = guitar_ptr(new struct guitar()); + fuzzbox = fuzzbox_ptr(new struct fuzzbox()); + guitar->add_effect(fuzzbox.get()); + }); + + it("starts in clean mode", [&](){ + AssertThat(guitar->sound(), Equals(sounds::clean)); + }); + + describe("in distorted mode", [&](){ + + before_each([&](){ + fuzzbox->flip(); + }); + + it("sounds distorted", [&](){ + AssertThat(guitar->sound(), Equals(sounds::distorted)); + }); + }); + }); + +}); + +} diff --git a/vendor/bandit/specs/it.spec.cpp b/vendor/bandit/specs/it.spec.cpp new file mode 100644 index 00000000..b7f5a998 --- /dev/null +++ b/vendor/bandit/specs/it.spec.cpp @@ -0,0 +1,168 @@ +#include +using namespace bandit::fakes; +namespace bd = bandit::detail; + +go_bandit([](){ + describe("it:", [&](){ + bd::voidfunc_t it_func; + fake_reporter_ptr reporter; + std::unique_ptr contexts; + std::unique_ptr context; + bandit::adapters::snowhouse_adapter assertion_adapter; + bd::run_policy_ptr run_policy; + + before_each([&](){ + reporter = fake_reporter_ptr(new fake_reporter()); + contexts = std::unique_ptr(new bd::contextstack_t()); + context = std::unique_ptr(new fake_context()); + contexts->push_back(context.get()); + + run_policy = bd::run_policy_ptr(new bd::always_run_policy()); + }); + + auto call_it = [&]() { + it("my it", it_func, *reporter, *contexts, assertion_adapter, *run_policy); + }; + + it("tells the current context that execution has started", [&](){ + // This is important as once execution has started, + // before_each and after_each calls cannot be guaranteed to + // be run before any 'it' method. + + call_it(); + AssertThat(context->call_log(), Has().AtLeast(1).EqualTo("execution_is_starting")); + }); + + describe("with succeeding test", [&](){ + before_each([&](){ + it_func = [](){}; + }); + + it("tells reporter it's starting", [&](){ + call_it(); + AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_starting: my it")); + }); + + it("tells reporter it's succeeded", [&](){ + call_it(); + AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_succeeded: my it")); + }); + + it("calls before_each in context", [&](){ + call_it(); + AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_before_eaches")); + }); + + it("calls after_each in context", [&](){ + call_it(); + AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_after_eaches")); + }); + }); + + describe("with failing test", [&](){ + before_each([&](){ + it_func = [](){ AssertThat(3, Equals(2)); }; + }); + + it("tells reporter it's failed", [&](){ + call_it(); + AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_failed: my it (Expected: equal to 2 Actual: 3 )")); + }); + + it("calls before_each in context", [&](){ + call_it(); + AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_before_eaches")); + }); + + it("calls after_each in context", [&](){ + call_it(); + AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_after_eaches")); + }); + + }); + + describe("with crashing test", [&](){ + before_each([&](){ + it_func = [](){ throw std::logic_error("serious crash"); }; + }); + + it("tells reporter it's failed", [&](){ + call_it(); + AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_unknown_error: my it")); + }); + + it("calls before_each in context", [&](){ + call_it(); + AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_before_eaches")); + }); + + it("calls after_each in context", [&](){ + call_it(); + AssertThat(context->call_log(), Has().Exactly(1).EqualTo("run_after_eaches")); + }); + }); + + describe("it_skip", [&](){ + + it("tells reporter it's skipped", [&](){ + it_skip("my it", [](){}, *reporter); + AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_skip: my it")); + }); + + it("doesn't call function", [&](){ + bool called = false; + it_skip("my it", [&](){ called = true; }, *reporter); + AssertThat(called, IsFalse()); + }); + + }); + + describe("with a run policy that says to skip this 'it'", [&](){ + bool it_was_called; + + before_each([&](){ + run_policy = bd::run_policy_ptr(new bd::never_run_policy()); + it_func = [&](){ it_was_called = true; }; + it_was_called = false; + }); + + it("tells reporter it's skipped", [&](){ + call_it(); + AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_skip: my it")); + }); + + it("doesn't call function", [&](){ + call_it(); + AssertThat(it_was_called, IsFalse()); + }); + + }); + + describe("skipping", [&](){ + bool it_was_called; + + before_each([&](){ + it_func = [&](){ it_was_called = true; }; + it_was_called = false; + }); + + describe("with a policy that says to skip this it", [&](){ + + before_each([&](){ + run_policy = bd::run_policy_ptr(new bd::never_run_policy()); + }); + + it("tells reporter it's skipped", [&](){ + call_it(); + AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("it_skip: my it")); + }); + + it("doesn't call function", [&](){ + call_it(); + AssertThat(it_was_called, IsFalse()); + }); + + }); + }); + }); +}); diff --git a/vendor/bandit/specs/main.cpp b/vendor/bandit/specs/main.cpp new file mode 100644 index 00000000..dde5de2d --- /dev/null +++ b/vendor/bandit/specs/main.cpp @@ -0,0 +1,6 @@ +#include + +int main(int argc, char* argv[]) +{ + return bandit::run(argc, argv); +} diff --git a/vendor/bandit/specs/options.spec.cpp b/vendor/bandit/specs/options.spec.cpp new file mode 100644 index 00000000..02e2818c --- /dev/null +++ b/vendor/bandit/specs/options.spec.cpp @@ -0,0 +1,109 @@ +#include + +using namespace bandit::specs::util; +namespace bd = bandit::detail; + +go_bandit([](){ + + describe("options:", [&](){ + + it("parses the '--help' option", [&](){ + const char* args[] = {"executable", "--help"}; + argv_helper argv(2, args); + + bd::options opt(argv.argc(), argv.argv()); + + AssertThat(opt.help(), IsTrue()); + }); + + it("parses the '--version' option", [&](){ + const char* args[] = {"executable", "--version"}; + argv_helper argv(2, args); + + bd::options opt(argv.argc(), argv.argv()); + + AssertThat(opt.version(), IsTrue()); + }); + + it("parses the '--no-color' option", [&](){ + const char* args[] = {"executable", "--no-color"}; + argv_helper argv(2, args); + + bd::options opt(argv.argc(), argv.argv()); + + AssertThat(opt.no_color(), IsTrue()); + }); + + it("parser the '--formatter=vs' option", [&](){ + const char* args[] = {"executable", "--formatter=vs"}; + argv_helper argv(2, args); + + bd::options opt(argv.argc(), argv.argv()); + AssertThat(opt.formatter(), Equals(bd::options::formatters::FORMATTER_VS)); + }); + + it("parser the '--formatter=default' option", [&](){ + const char* args[] = {"executable", "--formatter=default"}; + argv_helper argv(2, args); + + bd::options opt(argv.argc(), argv.argv()); + AssertThat(opt.formatter(), Equals(bd::options::formatters::FORMATTER_DEFAULT)); + }); + + it("parses the '--skip=\"substring\"' option", [&](){ + const char* args[] = {"executable", "--skip=substring"}; + argv_helper argv(2, args); + + bd::options opt(argv.argc(), argv.argv()); + AssertThat(opt.skip(), Equals("substring")); + }); + + it("parses skip as empty string if not present", [&](){ + const char* args[] = {"executable"}; + argv_helper argv(1, args); + + bd::options opt(argv.argc(), argv.argv()); + AssertThat(opt.skip(), Equals("")); + }); + + it("parses the '--only=\"substring\"' option", [&](){ + const char* args[] = {"executable", "--only=substring"}; + argv_helper argv(2, args); + + bd::options opt(argv.argc(), argv.argv()); + AssertThat(opt.only(), Equals("substring")); + }); + + it("parses only as empty string if not present", [&](){ + const char* args[] = {"executable"}; + argv_helper argv(1, args); + + bd::options opt(argv.argc(), argv.argv()); + AssertThat(opt.only(), Equals("")); + }); + + describe("with no arguments", [&](){ + const char* args[] = {"executable"}; + argv_helper argv(1, args); + bd::options opt(argv.argc(), argv.argv()); + + + it("cannot find '--help'", [&](){ + AssertThat(opt.help(), IsFalse()); + }); + + it("cannot find '--version'", [&](){ + AssertThat(opt.version(), IsFalse()); + }); + + it("cannot find '--no-color'", [&](){ + AssertThat(opt.no_color(), IsFalse()); + }); + + it("uses default formatter for '--formatter'", [&](){ + AssertThat(opt.formatter(), Equals(bd::options::formatters::FORMATTER_DEFAULT)); + }); + }); + }); + +}); diff --git a/vendor/bandit/specs/reporters/colorizer.spec.cpp b/vendor/bandit/specs/reporters/colorizer.spec.cpp new file mode 100644 index 00000000..7708ec81 --- /dev/null +++ b/vendor/bandit/specs/reporters/colorizer.spec.cpp @@ -0,0 +1,45 @@ +#ifndef _WIN32 +#include + +go_bandit([](){ + + describe("colorizer: ", [&](){ + + describe("colors enabled", [&](){ + bandit::detail::colorizer colorizer; + + it("can set color to green", [&](){ + AssertThat(colorizer.green(), Equals("\033[1;32m")); + }); + + it("set color to red", [&](){ + AssertThat(colorizer.red(), Equals("\033[1;31m")); + }); + it("resets color", [&](){ + AssertThat(colorizer.reset(), Equals("\033[0m")); + }); + + }); + + describe("colors disabled", [&](){ + + bandit::detail::colorizer colorizer(false); + + it("ignores setting color to green", [&](){ + AssertThat(colorizer.green(), Equals("")); + }); + + it("ignores setting color to red", [&](){ + AssertThat(colorizer.red(), Equals("")); + }); + + it("ignores resetting colors", [&](){ + AssertThat(colorizer.reset(), Equals("")); + }); + + }); + + }); + +}); +#endif \ No newline at end of file diff --git a/vendor/bandit/specs/reporters/dots_reporter.spec.cpp b/vendor/bandit/specs/reporters/dots_reporter.spec.cpp new file mode 100644 index 00000000..f06c8d77 --- /dev/null +++ b/vendor/bandit/specs/reporters/dots_reporter.spec.cpp @@ -0,0 +1,202 @@ +#include +namespace bd = bandit::detail; + +go_bandit([](){ + + describe("dots_reporter:", [&](){ + std::unique_ptr stm; + std::unique_ptr reporter; + bd::default_failure_formatter formatter; + bd::colorizer colorizer(false); + + before_each([&](){ + stm = std::unique_ptr(new std::stringstream()); + reporter = std::unique_ptr( + new bd::dots_reporter(*stm, formatter, colorizer)); + }); + + auto output = [&](){ return stm->str(); }; + + describe("an empty test run", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->test_run_complete(); + }); + + it("reports no tests where run", [&](){ + AssertThat(output(), Equals("\nCould not find any tests.\n")); + }); + + it("is not considered successful", [&](){ + AssertThat(reporter->did_we_pass(), Equals(false)); + }); + + }); + + describe("a successful test run", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + reporter->it_starting("my test"); + reporter->it_succeeded("my test"); + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("reports a successful test run", [&](){ + AssertThat(output(), Contains("Success!")); + AssertThat(output(), EndsWith("Test run complete. 1 tests run. 1 succeeded.\n")); + }); + + it("displays a dot for the successful test", [&](){ + AssertThat(output(), StartsWith(".")); + }); + + it("reports a successful test run", [&](){ + AssertThat(reporter->did_we_pass(), Equals(true)); + }); + }); + + describe("a failing test run", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + reporter->it_starting("my test"); + + bd::assertion_exception exception("assertion failed!", "some_file", 123); + reporter->it_failed("my test", exception); + + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("reports a failing test run in summary", [&](){ + AssertThat(output(), EndsWith("Test run complete. 1 tests run. 0 succeeded. 1 failed.\n")); + }); + + it("reports the failed assertion", [&](){ + AssertThat(output(), Contains("my context my test:\nsome_file:123: assertion failed!")); + }); + + it("only reports assertion failure once", [&](){ + AssertThat(output(), Has().Exactly(1).EndingWith("assertion failed!")); + }); + + it("reports an 'F' for the failed assertion", [&](){ + AssertThat(output(), StartsWith("F")); + }); + + it("reports a failed test run", [&](){ + AssertThat(reporter->did_we_pass(), Equals(false)); + }); + }); + + describe("a test run with a non assertion_exception thrown", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + reporter->it_starting("my test"); + + reporter->it_unknown_error("my test"); + + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("reports an 'E' for the failed test", [&](){ + AssertThat(output(), StartsWith("E")); + }); + + it("reports the failed test", [&](){ + AssertThat(output(), Contains("my context my test:\nUnknown exception")) + }); + + }); + + describe("a failing test run with nested contexts", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + reporter->context_starting("a nested context"); + reporter->it_starting("my test"); + + bd::assertion_exception exception("assertion failed!", "some_file", 123); + reporter->it_failed("my test", exception); + + reporter->context_ended("a nested context"); + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("reports a failing test run in summary", [&](){ + AssertThat(output(), EndsWith("Test run complete. 1 tests run. 0 succeeded. 1 failed.\n")); + }); + + it("reports the failed assertion", [&](){ + AssertThat(output(), Contains("my context a nested context my test:\nsome_file:123: assertion failed!")); + }); + + it("reports an 'F' for the failed assertion", [&](){ + AssertThat(output(), StartsWith("F")); + }); + + it("reports a failed test run", [&](){ + AssertThat(reporter->did_we_pass(), Equals(false)); + }); + + }); + + describe("a context with test run errors", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + + bd::test_run_error error("we dun goofed!"); + reporter->test_run_error("my context", error); + + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("reports that the context has failed", [&](){ + AssertThat(output(), Contains("Failed to run \"my context\": error \"we dun goofed!\"")); + }); + + it("reports test run errors in summary", [&](){ + AssertThat(output(), EndsWith("Test run complete. 0 tests run. 0 succeeded. 1 test run errors.\n")) + }); + + it("reports a failed test run", [&](){ + AssertThat(reporter->did_we_pass(), Equals(false)); + }); + }); + + describe("a context with a skipped test", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + + reporter->it_starting("my test"); + reporter->it_succeeded("my test"); + reporter->it_skip("my skipped test"); + + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("reports that there is one skipped test in the summary", [&](){ + AssertThat(output(), EndsWith("Test run complete. 1 tests run. 1 succeeded. 1 skipped.\n")); + }); + + }); + }); + + +}); diff --git a/vendor/bandit/specs/reporters/single_line_reporter.spec.cpp b/vendor/bandit/specs/reporters/single_line_reporter.spec.cpp new file mode 100644 index 00000000..ef7b5206 --- /dev/null +++ b/vendor/bandit/specs/reporters/single_line_reporter.spec.cpp @@ -0,0 +1,201 @@ +#include +namespace bd = bandit::detail; + +go_bandit([](){ + + describe("single line reporter", [&](){ + std::unique_ptr stm; + std::unique_ptr reporter; + bd::default_failure_formatter formatter; + bd::colorizer colorizer(false); + + before_each([&](){ + stm = std::unique_ptr(new std::stringstream()); + reporter = std::unique_ptr( + new bd::single_line_reporter(*stm, formatter, colorizer)); + }); + + auto output = [&](){ return stm->str(); }; + + describe("an empty test run", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->test_run_complete(); + }); + + it("reports that no tests were run", [&](){ + AssertThat(output(), Equals("\nCould not find any tests.\n")); + }); + + it("is not considered successful", [&](){ + AssertThat(reporter->did_we_pass(), Equals(false)); + }); + }); + + describe("a successful test run", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + reporter->it_starting("my test"); + reporter->it_succeeded("my test"); + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("reports a successful test run", [&](){ + AssertThat(output(), EndsWith("Test run complete. 1 tests run. 1 succeeded.\n")); + }); + + it("displays progress for the test", [&](){ + AssertThat(output(), StartsWith("\rExecuted 0 tests." + "\rExecuted 1 tests.")); + }); + + it("reports a successful test run", [&](){ + AssertThat(reporter->did_we_pass(), Equals(true)); + }); + }); + + describe("a failing test run", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + reporter->it_starting("my test"); + + bd::assertion_exception exception("assertion failed!", "some_file", 123); + reporter->it_failed("my test", exception); + + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("reports a failing test run in summary", [&](){ + AssertThat(output(), EndsWith("Test run complete. 1 tests run. 0 succeeded. 1 failed.\n")); + }); + + it("reports the failed assertion", [&](){ + AssertThat(output(), Contains("my context my test:\nsome_file:123: assertion failed!")); + }); + + it("reports failing test in progress", [&](){ + AssertThat(output(), StartsWith("\rExecuted 0 tests." + "\rExecuted 1 tests. 0 succeeded. 1 failed.")); + }); + + it("reports a failed test run", [&](){ + AssertThat(reporter->did_we_pass(), Equals(false)); + }); + }); + + describe("a test run with a non assertion_exception thrown", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + reporter->it_starting("my test"); + + reporter->it_unknown_error("my test"); + + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("reports failing test in progress", [&](){ + AssertThat(output(), StartsWith("\rExecuted 0 tests." + "\rExecuted 1 tests. 0 succeeded. 1 failed.")); + }); + + it("reports the failed test", [&](){ + AssertThat(output(), Contains("my context my test:\nUnknown exception")) + }); + + }); + + describe("a failing test run with nested contexts", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + reporter->context_starting("a nested context"); + reporter->it_starting("my test"); + + bd::assertion_exception exception("assertion failed!", "some_file", 123); + reporter->it_failed("my test", exception); + + reporter->context_ended("a nested context"); + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("reports a failing test run in summary", [&](){ + AssertThat(output(), EndsWith("Test run complete. 1 tests run. 0 succeeded. 1 failed.\n")); + }); + + it("reports the failed assertion", [&](){ + AssertThat(output(), Contains("my context a nested context my test:\nsome_file:123: assertion failed!")); + }); + + it("displays a failed test in progress report", [&](){ + AssertThat(output(), StartsWith("\rExecuted 0 tests." + "\rExecuted 1 tests. 0 succeeded. 1 failed.")); + }); + + it("reports a failed test run", [&](){ + AssertThat(reporter->did_we_pass(), Equals(false)); + }); + + }); + + describe("a context with test run errors", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + + bd::test_run_error error("we dun goofed!"); + reporter->test_run_error("my context", error); + + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("reports that the context has failed", [&](){ + AssertThat(output(), Contains("Failed to run \"my context\": error \"we dun goofed!\"")); + }); + + it("reports test run errors in summary", [&](){ + AssertThat(output(), EndsWith("Test run complete. 0 tests run. 0 succeeded. 1 test run errors.\n")) + }); + + it("reports a failed test run", [&](){ + AssertThat(reporter->did_we_pass(), Equals(false)); + }); + }); + + describe("a context with a skipped test", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + + reporter->it_starting("my test"); + reporter->it_succeeded("my test"); + reporter->it_skip("my skipped test"); + + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("reports that there is one skipped test in the summary", [&](){ + AssertThat(output(), EndsWith("Test run complete. 1 tests run. 1 succeeded. 1 skipped.\n")); + }); + + }); + + + }); + +}); diff --git a/vendor/bandit/specs/reporters/xunit_reporter.spec.cpp b/vendor/bandit/specs/reporters/xunit_reporter.spec.cpp new file mode 100644 index 00000000..07f0c3b7 --- /dev/null +++ b/vendor/bandit/specs/reporters/xunit_reporter.spec.cpp @@ -0,0 +1,161 @@ +#include +namespace bd = bandit::detail; + +go_bandit([](){ + + describe("xunit_reporter:", [&](){ + std::unique_ptr stm; + bd::default_failure_formatter formatter; + std::unique_ptr reporter; + + auto output = [&](){ return stm->str(); }; + + before_each([&](){ + stm = std::unique_ptr(new std::stringstream()); + reporter = std::unique_ptr(new bd::xunit_reporter(*stm, formatter)); + }); + + describe("an empty test run", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->test_run_complete(); + }); + + it("adds a header to the output", [&](){ + AssertThat(output(), StartsWith("\n")); + }); + + it("outputs an empty test report", [&](){ + AssertThat(output(), Contains( + "\n" + "\n")); + }); + + }); + + describe("a test run with one, successful, test", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + reporter->it_starting("my test"); + reporter->it_succeeded("my test"); + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("outputs info about the successful test", [&](){ + AssertThat(output(), Contains( + "\n" + "\t\n" + "\t\n" + "\n")); + }); + }); + + describe("a test run with one, failing test", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + reporter->it_starting("my test"); + + bd::assertion_exception exception("assertion failed!", "some_file", 123); + reporter->it_failed("my test", exception); + + reporter->context_ended("my context"); + reporter->test_run_complete(); + + }); + + it("outputs the failing test", [&](){ + AssertThat(output(), Contains( + "\n" + "\t\n" + "\t\t\n" + "\t\n" + "\n")); + }); + + }); + + describe("a test run with one test with an unknown error", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + reporter->it_starting("my test"); + + reporter->it_unknown_error("my test"); + + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("outputs the erroneous test", [&](){ + AssertThat(output(), Contains( + "\n" + "\t\n" + "\t\t\n" + "\t\n" + "\n")); + }); + + }); + + describe("a test run with one test failing with characters that need escaping", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context & < > \\ \""); + reporter->it_starting("my test & < > \\ \""); + + bd::assertion_exception exception("assertion failed & < > \\ \"", "some_file", 123); + reporter->it_failed("my test & < > \\ \"", exception); + + reporter->context_ended("my context & < > \\ \""); + reporter->test_run_complete(); + }); + + it("outputs the escaped characters", [&](){ + AssertThat(output(), Contains( + "\n" + "\t\n" + "\t\t\n" + "\t\n" + "\n")); + }); + + }); + + describe("a context with a skipped test", [&](){ + + before_each([&](){ + reporter->test_run_starting(); + reporter->context_starting("my context"); + + reporter->it_starting("my test"); + reporter->it_succeeded("my test"); + reporter->it_skip("my skipped test"); + + reporter->context_ended("my context"); + reporter->test_run_complete(); + }); + + it("outputs info about the skipped test", [&](){ + AssertThat(output(), Contains( + "\n" + "\t\n" + "\t\n" + "\t\n" + "\t\t\n" + "\t\n" + "\n")); + }); + + }); + + }); + +}); diff --git a/vendor/bandit/specs/run.spec.cpp b/vendor/bandit/specs/run.spec.cpp new file mode 100644 index 00000000..4ef5ffc6 --- /dev/null +++ b/vendor/bandit/specs/run.spec.cpp @@ -0,0 +1,77 @@ +#include +using namespace bandit::fakes; +using namespace bandit::specs::util; +namespace bd = bandit::detail; + +go_bandit([](){ + + describe("run:", [&](){ + std::unique_ptr specs; + std::unique_ptr argv; + fake_reporter_ptr reporter; + std::unique_ptr context_stack; + + auto call_run = [&](){ + bd::options opt(argv->argc(), argv->argv()); + return bandit::run(opt, *specs, *context_stack, *reporter); + }; + + before_each([&](){ + specs = std::unique_ptr(new bd::spec_registry()); + + reporter = fake_reporter_ptr(new fake_reporter()); + + context_stack = std::unique_ptr(new bd::contextstack_t()); + + const char* args[] = {"executable"}; + argv = std::unique_ptr(new argv_helper(1, args)); + }); + + it("pushes the global context on the context stack", [&](){ + call_run(); + AssertThat(*context_stack, Is().OfLength(1)); + }); + + describe("a successful test run", [&](){ + int number_of_specs_called; + + before_each([&](){ + number_of_specs_called = 0; + specs->push_back([&](){ number_of_specs_called++; }); + }); + + it("calls the context", [&](){ + call_run(); + AssertThat(number_of_specs_called, Equals(1)); + }); + + it("tells reporter a test run is about to start", [&](){ + call_run(); + AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("test_run_starting")); + }); + + it("tells reporter a test run has completed", [&](){ + call_run(); + AssertThat(reporter->call_log(), Has().Exactly(1).EqualTo("test_run_complete")); + }); + + it("returns 0 as no specs failed", [&](){ + AssertThat(call_run(), Equals(0)); + }); + }); + + + describe("a failing test run", [&](){ + + before_each([&](){ + reporter->set_test_run_status(false); + }); + + it("returns a non-zero error code", [&](){ + AssertThat(call_run(), IsGreaterThan(0)); + }); + + }); + }); + +}); diff --git a/vendor/bandit/specs/run_policies/bandit_run_policy.spec.cpp b/vendor/bandit/specs/run_policies/bandit_run_policy.spec.cpp new file mode 100644 index 00000000..3f383402 --- /dev/null +++ b/vendor/bandit/specs/run_policies/bandit_run_policy.spec.cpp @@ -0,0 +1,226 @@ +#include + +go_bandit([](){ + namespace bd = bandit::detail; + + describe("bandit run policy", [&](){ + std::unique_ptr contextstack; + std::unique_ptr global_context; + std::string only_pattern; + std::string skip_pattern; + + before_each([&](){ + contextstack = std::unique_ptr(new bd::contextstack_t()); + bool hard_skip = false; + global_context = std::unique_ptr(new bd::bandit_context("", hard_skip)); + contextstack->push_back(global_context.get()); + }); + + describe("neither skip nor only specified", [&](){ + + before_each([&](){ + only_pattern = ""; + skip_pattern = ""; + }); + + it("always says run", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name", *contextstack), IsTrue()); + }); + + describe("has context marked with 'hard_skip' in stack", [&](){ + std::unique_ptr hard_skip_context; + + before_each([&](){ + bool hard_skip = true; + hard_skip_context = std::unique_ptr(new bd::bandit_context("always ignore", hard_skip)); + contextstack->push_back(hard_skip_context.get()); + }); + + it("never runs", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name", *contextstack), IsFalse()); + AssertThat(policy.should_run("it name matches 'skip'", *contextstack), IsFalse()); + AssertThat(policy.should_run("it name matches 'only'", *contextstack), IsFalse()); + }); + + }); + + }); + + describe("'skip' specified, 'only' unspecified", [&](){ + + before_each([&](){ + only_pattern = ""; + skip_pattern = "skip"; + }); + + describe("current context matches 'skip'", [&](){ + std::unique_ptr current_context; + + before_each([&](){ + bool hard_skip = false; + current_context = std::unique_ptr(new bd::bandit_context("context matches 'skip'", hard_skip)); + contextstack->push_back(current_context.get()); + }); + + it("never runs", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name", *contextstack), IsFalse()); + }); + + }); + + describe("current context doesn't match 'skip'", [&](){ + std::unique_ptr current_context; + + before_each([&](){ + bool hard_skip = false; + current_context = std::unique_ptr(new bd::bandit_context("context doesn't match", hard_skip)); + contextstack->push_back(current_context.get()); + }); + + it("runs if spec's name doesn't match", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name", *contextstack), IsTrue()); + }); + + it("doesn't run if spec's name matches", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name matching 'skip'", *contextstack), IsFalse()); + }); + + }); + + }); + + describe("'only' specified, 'skip' unspecified", [&](){ + + before_each([&](){ + only_pattern = "only"; + skip_pattern = ""; + }); + + describe("current context matches 'only'", [&](){ + std::unique_ptr current_context; + + before_each([&](){ + bool hard_skip = false; + current_context = std::unique_ptr(new bd::bandit_context("context matches 'only'", hard_skip)); + contextstack->push_back(current_context.get()); + }); + + it("always runs", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name", *contextstack), IsTrue()); + }); + + }); + + describe("current context doesn't match 'only'", [&](){ + std::unique_ptr current_context; + + before_each([&](){ + bool hard_skip = false; + current_context = std::unique_ptr(new bd::bandit_context("context doesn't match", hard_skip)); + contextstack->push_back(current_context.get()); + }); + + it("doesn't run if spec's name doesn't match", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name", *contextstack), IsFalse()); + }); + + it("runs if spec's name matches", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name matching 'only'", *contextstack), IsTrue()); + }); + + }); + + }); + + describe("'skip' specified, 'only' specified", [&](){ + + before_each([&](){ + only_pattern = "only"; + skip_pattern = "skip"; + }); + + describe("current context matches 'skip'", [&](){ + std::unique_ptr current_context; + + before_each([&](){ + bool hard_skip = false; + current_context = std::unique_ptr(new bd::bandit_context("context matches 'skip'", hard_skip)); + contextstack->push_back(current_context.get()); + }); + + it("doesn't run if 'it' doesn't match 'only'", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name", *contextstack), IsFalse()); + }); + + it("runs if 'it' matches 'only'", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it matches 'only'", *contextstack), IsTrue()); + }); + + }); + + describe("current context 'only'", [&](){ + std::unique_ptr current_context; + + before_each([&](){ + bool hard_skip = false; + current_context = std::unique_ptr(new bd::bandit_context("context matches 'only'", hard_skip)); + contextstack->push_back(current_context.get()); + }); + + it("runs if spec's name doesn't match anything", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name", *contextstack), IsTrue()); + }); + + it("doesn't run if spec's name matches 'skip'", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name matching 'skip'", *contextstack), IsFalse()); + }); + + }); + + describe("has both 'only' and 'skip' in context stack", [&](){ + std::unique_ptr current_context; + std::unique_ptr parent_context; + + before_each([&](){ + bool hard_skip = false; + current_context = std::unique_ptr(new bd::bandit_context("context matches 'only'", hard_skip)); + parent_context = std::unique_ptr(new bd::bandit_context("context matches 'skip'", hard_skip)); + contextstack->push_back(parent_context.get()); + contextstack->push_back(current_context.get()); + }); + + it("runs if spec's name doesn't match anything", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name", *contextstack), IsTrue()); + }); + + it("doesn't run if spec's name matches 'skip'", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name matching 'skip'", *contextstack), IsFalse()); + }); + it("runs if spec's name matches 'only'", [&](){ + bd::bandit_run_policy policy(skip_pattern.c_str(), only_pattern.c_str()); + AssertThat(policy.should_run("it name matching 'only'", *contextstack), IsTrue()); + }); + + }); + + }); + + + }); + +}); + diff --git a/vendor/bandit/specs/specs.h b/vendor/bandit/specs/specs.h new file mode 100644 index 00000000..219e89ee --- /dev/null +++ b/vendor/bandit/specs/specs.h @@ -0,0 +1,10 @@ +#ifndef BANDIT_SPECS +#define BANDIT_SPECS + +#include +using namespace bandit; + +#include +#include + +#endif diff --git a/vendor/bandit/specs/synopsis.spec.cpp b/vendor/bandit/specs/synopsis.spec.cpp new file mode 100644 index 00000000..3b717f75 --- /dev/null +++ b/vendor/bandit/specs/synopsis.spec.cpp @@ -0,0 +1,54 @@ +#include + +go_bandit([](){ + describe("my first spec", [&]() { + int a; + + before_each([&](){ + a = 99; + }); + + it("should be initialized", [&](){ + AssertThat(a, Equals(99)); + a = 102; + }); + + describe("nested spec", [&](){ + + before_each([&](){ + a += 3; + }); + + it("should build on outer spec", [&](){ + AssertThat(a, Equals(102)); + a = 666; + }); + + it("should build on outer spec yet again", [&](){ + AssertThat(a, Equals(102)); + a = 667; + }); + + }); + + it("should be initialized before each it", [&](){ + AssertThat(a, Equals(99)); + }); + }); + + describe("my second spec", [&](){ + int b; + + before_each([&](){ + b = 22; + }); + + before_each([&](){ + b += 3; + }); + + it("should be 25", [&](){ + AssertThat(b, Equals(25)); + }); + }); +}); diff --git a/vendor/bandit/specs/util/argv_helper.h b/vendor/bandit/specs/util/argv_helper.h new file mode 100644 index 00000000..4e92e725 --- /dev/null +++ b/vendor/bandit/specs/util/argv_helper.h @@ -0,0 +1,62 @@ +#ifndef BANDIT_SPECS_ARGV_HELPER_H +#define BANDIT_SPECS_ARGV_HELPER_H + +#include + +namespace bandit { namespace specs { namespace util { + + // + // main() is supposed to receive its arguments as a non const 'char* argv[]'. + // This is a pain to create for each test. It's a whole lot easier to create + // a 'const char* argv[]' construct. + // + // This class helps copy from 'const char**' to 'char**' and handle cleanup + // automatically. + // + struct argv_helper + { + argv_helper(int argc, const char* argv[]) + : argc_(argc) + { + non_const_argv_ = new char*[argc]; + for(int i=0; i < argc; i++) + { + std::string s(argv[i]); + non_const_argv_[i] = new char[s.size() + 1]; + for(size_t c=0;c